Help with inserting 2d element

I am working on a tool that will insert a new family instance that is already loaded in the file. These files are 2d elements.

Currently the below works but has limitations. It will insert the family and every type over itself. Is there a way to specify the type I want to insert?

Second, is the method of inserting. Currently it just puts it a origin point which I understand since it is being told to go to 0,0,0. What I want is to be able to have the user pick the insertion point.

Any help would be great. Thank you.

I am new to coding and python but not new to revit.

import clr
clr.AddReference('RevitAPI') 
clr.AddReference('RevitAPIUI') 
from Autodesk.Revit.DB import *

app = __revit__.Application
doc = __revit__.ActiveUIDocument.Document
 
t = Transaction(doc, 'Create family instance.')
 
t.Start()
 
#Family symbol name to place.
symbName = 'CN_AIRFLOW ARROW'
 
#create a filtered element collector set to Category OST_Mass and Class FamilySymbol 
collector = FilteredElementCollector(doc)
collector.OfCategory(BuiltInCategory.OST_GenericAnnotation)
collector.OfClass(FamilySymbol)
 
famtypeitr = collector.GetElementIdIterator()
famtypeitr.Reset()
 
#Search Family Symbols in document.
for item in famtypeitr:
    famtypeID = item
    famsymb = doc.GetElement(famtypeID)
 
    #If the FamilySymbol is the name we are looking for, create a new instance.
    if famsymb.Family.Name == symbName:
 
        #location to place family
        loc = XYZ(0,0,0)
 
        #NewFamilyInstance()
        familyInst = doc.Create.NewFamilyInstance(loc, famsymb, doc.ActiveView)
 
t.Commit()

Are you working in dynamo or in python itself?
I’m not sure if it’s totally relevant, but it could help finding an answer;

  1. Changing your familyinstance type.
    So this is actually not that hard, it’s just a bit inconvenient.
    NewFamilyInstance will always use the ‘last used’ type used by the interface.(1)
    But we CAN change the type after the instance is created.
    The element will have a ‘ChangeTypeId()’ function that you can use. You’ll also need to get all the type id’s for that family I presume. Here’s how:
# 1. Get all family types/symbols ( yeah ..)
# note that this won't work for walls/floors or other system families.
all_family_types = list(famsymb.Family.GetFamilySymbolIds())

#2. Change the type fo the element to what you've chosen as a type.
# provided you've created a variable named chosen_type_id which contains a single elementID from all_family_types
familyInst.ChangeTypeId( chosen_type_id)

Now for choosing a position. Also a bit more convoluted at first glance. But not too bad. The biggest thing here is the way the selection tool works.
Mainly, what happens if a user presses the ‘Escape’ button.
Revit will throw out an error which you have to catch in order for your script to keep running. This might sound scary, but it’s actually a pretty clean way to detect specific circumstances and have code be interrupted. It will make your code more robust. But I suggest diving into a tutorial video about exceptions for that.

# You'll need to add an additional import at the top of your code
# try either (remove 1)
from Autodesk.Revit import UI as UI
# or 
from pyrevit import UI

uidoc = __revit__.ActiveUIDocument
snap_mode = UI.Selection.ObjectSnapTypes.Intersections
desired_location = XYZ() # set up a 'default' location at 0,0,0
try:
    desired_location = uidoc.Selection.PickPoint(snap_mode)
except Autodesk.Revit.Exceptions.OperationCanceledException as cancelled_ex:
    # this part of the code gets executed when the user presses escape. 
    # desired_location is already set to (0,0,0) so we don't really need to do anything here.
    # I'm just putting in a print statement for the info, but you can also just write pass. Should work fine.
    print('User cancelled operation')
    # pass is sort of like a 'do nothing' line; But it counts as code.
    # but it keeps your code readable. You can use pass in other places as well. It's not limited to exceptions. It's just an empty line.
    pass

    

That should do it.
Now for a bit of an extra, IF you run your scripts from python (don’t know about dynamo tbh)
you can import a logger that provides some more clean output, and which can help you debug your code.

from pyrevit import script
logger = script.get_logger()
output = script.get_output()
output.close_others() # this just makes sure your previous logger windows get closed on the next script run

logger.info('This is a standard info log')
logger.warn('This is a warning log, should be orange')
logger.error('This is an error log, red')

These don’t change much, but it can help you

(1) Now looking at revitapidocs it seems that there’s no way around it. (I could be wrong, I glossed over it)