|
POST
|
What if you wrote a console ArcObjects application that you execute from python? I believe you can get a list of current ArcMap application references from the IAppROT interface. That might work. The IAppROT interface seems promising for connecting to any running ESRI application. Hopefully I could have Python send a text string naming the Map and test that string against the IAppROT list of running applications (realizing that only ArcMap applications would be valid). From the help on the AppRef returned by the Item method of IAppROT, it says "Note you can only use the AppRef object if your code is running inside one of the ArcGIS application processes". I assume a console ArcObjects application would meet that requirement, but I have not designed one before, so I am not sure. In any case, so far this looks like a viable option.
... View more
08-24-2012
10:26 AM
|
0
|
0
|
2944
|
|
POST
|
Can I interest you in writing a Java add-in? 😄 Sarcasm aside, the python script tool dialog is doing the essential things that a form in ArcObjects does, in that it listens to actions done on the form and allows me to respond. The debug time of validating user input on an ArcObjects form is nearly unending when text boxes have to take care of virtually everything and I have to write all of the validation code to restrict it to providing valid dates, numbers, and combobox lists, or knowing when to cross check each interface object at precisely the right moment during an onchange event for each interface object, etc. Interface design in ArcObjects is full of undocumented pitfalls that have wasted weeks of my time that I simply don't have to deal with on the script tool form. I am utterly tired of ArcObjects expecting me to take control of everything rather than being able to rely on well designed built-in code that takes care of at least 50% of my validation needs.
... View more
08-24-2012
10:11 AM
|
0
|
0
|
2944
|
|
POST
|
At first thought, your pattern sounds like the observer pattern, notify registered observers when an event occurs, except you're using two different languages, so I'm not sure how you would connect the two. At second thought, there is: com.esri.arcgis.carto.IActiveViewEventsFocusMapChangedEvent Which might work. Lastly, Add-ins respond to user initiated events, so I'm not sure how you would initiate the tool, button, menu, utility object or extension that should be listening for the events from python. Thanks for the suggestions. I was afraid of the last thing you said. The notification process I outlined would ensure that any user triggered refresh would cause the listener to respond, detect the update by Python from the text object, and perform its action. A refresh is harder for a user to avoid as a trigger than pressing another button. That approach may be my best option, but it would introduce a delay to the full completion of the update that I would rather avoid and would not be anywhere close to fully reliable. It would be a nice enhancement of the Python arcpy interface to have it be able to call buttons or activate tools on a toolbar, but I also see the potential dangers in allowing that. I am at 10.0, so I know it cannot do that. With 10.1 Python add-ins I did not hear about anything like that. Without being able to pass an application object hook or map to a DLL from Python, I also do not see a way to do this from the Python side. The hook would be required to get ArcObjects to trigger any button or toolbar I might build designed to work within an open ArcMap session. I would wrap the script tool inside of an ArcObjects call if ArcObjects could actually trigger the tool to actually open it's dialog and continue when the dialog closes, but I have not seen anything like that either. I also see dangers in that approach as well, since the program notification processes of the two languages would remain incompatible. I do not think the FocusMapChanged Event helps, since I believe that only occurs when a user opens another map, not while continuing to work within a single map. But the help description is not very clear about its intended behavior.
... View more
08-24-2012
09:40 AM
|
0
|
0
|
2944
|
|
POST
|
so i need to join the data of every "influence"polygon with the "field"polygon where they are lying on, but in a way that the lying upon another "influence"polygons dont get the data from each other, just from the field. (sorry for my cruel english in this case..) or is this possible with the dissolve tool/spatial join and i did something wrong? best regards andreas I do not follow what you want to do. The Spatial Join/Dissolve technique should be able to handle keeping influence polygons matched to valid field polygons with the proper use of the One-To-Many option and the One-To-One option for the Spatial Join, selecting valid combinations and selecting the appropriate list of fields to use in the Dissolve. A picture is worth a thousand words (whether in perfect english or broken english), so screenshot and upload a picture that gives an example of what you want and what you don't want.
... View more
08-24-2012
09:08 AM
|
0
|
0
|
3779
|
|
POST
|
What about that: embed your Python script into a Python script tool inside a custom toolbox. Extent your add-in with a button. If the user clicks the button, the add-in runs the script tool (synchron) and after it finished, it does the other stuff with ArcObjects. For deployment (to the assembly cache) you can add the custom toolbox as a resource to the add-in. The code for using an embedded toolbox is something like that: // Initialize the geoprocessor. IGeoProcessor2 gp = new GeoProcessorClass(); // Add the toolbox. string strPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string strToolbox = strPath + "\\PythonTools.tbx"; gp.AddToolbox(strToolbox); // Generate the array of parameters. IVariantArray parameters = new VarArrayClass(); parameters.Add(inString); // Execute the model tool by name. IGeoProcessorResult result = gp.Execute("Reply", parameters, null); // Ergebnis zurückliefern string reply = result.GetOutput(0).GetAsText(); I do not think this will work for me. My python script tool is unconventional and is not merely designed to act as a geoprocessing tool. It acts like a form object with user interface capabilities. I do not want the user to simply feed parameters to the tool. I intend for them to interact with the tool. I have adapted the script tool's ToolValidator to responds to each action by the user so that as they make choices, that action changes the options that work for other parameters according to the databases the user is querying. The interface ensures that the user ends up with inputs that will return valid results when the actual geoprocessing takes place. For example, if a user inputs a date range, the ToolValidator reads the database to make sure that there is data that extends to both limits of the range, and corrects the range if it does not. It also responds to the date range change by altering the list of Primary Roads that the user can choose to only be those roads that actually had events within the date range. When the user chooses a Primary Road, the validator makes sure it is in the list, and then updates the lists of intersecting roads that connect to the chosen Primary road. As the user chooses intersecting roads and sets offsets from that intersection that will ultimately create point events or a road segment event when the actual georocessing takes place, the interface makes sure that any offsets will remain on the road and corrects the user's entries if they do not. This interactive experience is essential to the tool's use and converting to it a mere geoprocessing tool called by ArcObjects would defeat my purpose. I am aware that with .Net I could design a form using ArcObjects, but I have no interest in doing that. Therefore the python script tool dialog must actually be opened by the user and only the user can determine when they are happy with the parameters values they have chosen within the dialog to trigger the actual geoprocessing actions of the tool. Given my objectives and restrictions, do you still think your suggestion could somehow be adapted to work?
... View more
08-24-2012
08:22 AM
|
0
|
0
|
2944
|
|
POST
|
I have created several symbols with transparent backgrounds and saved them as a .png. I would like to display them on my .mxd with the transparent background. I do not mind if the symbols have to be imported as pictures or if they can be used as the actual symbols. If the symbols can be designed using vectors, I highly recommend using EMF format to store and display the symbols. EMFs do tranparent backgrounds very nicely. They have little impact on memory use, scale incredibly well to different sizes and print or export to PDF reliably. Bitmap or pixel based picture symbols can easily create memory problems if there are a lot of different symbols in your map. In my maps they begin to turn completely black and cause other distortion problems for export to PDF and printing. I have upwards of 600 possilble symbols on my maps to show all of my street sign faces and since I moved to EMF pictures I have had no problems.
... View more
08-23-2012
08:47 PM
|
0
|
0
|
763
|
|
POST
|
I want to know if it is possible to create an Add-in extension that listens for map refresh events to specifically respond to the actions of a python script? I envision adding a hidden text object into a specific kind of map that will at first indicate to the Add-in Extension that nothing needs to be done. When the Python script runs it will write to that text object to set a flag telling the Extension that it is processing a change to the map, so ignore any map refresh actions during the script processing. Just prior to completion of the Python script, the Python script will update the Text Object to say that it just finished and needs the Extension to do its thing. Then the Python script will refresh the map. I assume the Add-in Extension can detect the map refresh event triggered by the Python script, read the text object to detect that python wants the extension to run and then the extension can fire its action on the map. When the extension finishes it will reset the text object back to the state indicating that nothing needs to be done by the extension. Does this sound like it will work? I want the Add-in to detect when arcpy.mapping finishes updating everything that it can in the map, and then have the extension change the data frame clipping geometry to match a feature's shape according to the current definition query on a layer that the python script set. I do not want the user to have to push another button after the Python script finishes to do things that Python seemingly cannot do, and I do not want the Add-in to do the Python script actions. I do not see any other way to have Python trigger an ArcObjects action within the currently open map, since the ctypes functionality says it is limited to basic C++ types and I do not see a way to pass a hook for the current map to an ArcObjects DLL method from Python. Please correct me if I am wrong about any of this or if there is a better approach to do what I want.
... View more
08-23-2012
04:27 PM
|
0
|
19
|
7621
|
|
POST
|
I wanted to volunteer some code that may help some of those doing python coding. I have created a python script using arcpy and the portions of the Minimum Bounding Rectangle code by Dan Patterson that can take a selected feature (or a portion of a road as a feature) and Rotate and Zoom the Data Frame so that the feature is at its optimum angle and centered completely within its minimum rectangle extent. I found that the order of operations involved: 1. Get the minimum bounding rectangle and it centeriod coordinate. 2. Get and set the rotation angle of Data Frame, using the rectangle angle. The angle needs to be adjusted to ensure that North never drops below the horizontal plain of the map when the Data Frame is rotated. 3. Determine the optimum scale in the longer plain. For a Data Frame that is wider by 2.5 over its height, I found a scale that is equal to 88% of the length of the long axis of the rectangle resulted in a nice margin on the sides of the map. When a minimum bounding rectangle has a width that exceeds that adjusted length I use that distance as the scale. 4. Set the Extent coordinates centered on the rectangle centroid and position the coordinates so that X and Y spread equal the length and width of the minimum bounding rectangle. 5 Repeat the steps 3 and 4 six more times. The Extent refuses to set correctly the first time except for rectanlges perfectly compass oriented in their native postions so that they match the Data Frame's longest plane. The repetition results in a successive approximation that gradually moves the Data Frame center to the centroid of the feature. (At about the 6th time through it seems fine, but I went to 7 to be perfect). The end result is like the screen shot attached. At the moment the code is overly long, since I have not optimized it (banged my head for quite some time to figure out that the repetition was essential to the process.) A main portion of the code is below: import sys, os, math
import arcpy
import arcpy_helper
reload(arcpy_helper) #uncomment if editing arcpy_helper Got it from Dan Patterson's code.
try:
import convexHull_helper, geom_helper, smallestCircle_helper, groupPnts_helper
except:
arcpy.AddMessage("\n The helper scripts:" +
"\n convexHull_helper, geom_helper, arcpy_helper, smallestCircle_helper " +
"\n groupPnts_helper are not located in the same folder as the main" +
"\n script and the toolbox OR one of the scripts is being edited." +
"\n" + arcpy.GetMessages())
sys.exit()
# get a feature with a cursor. Code not shown.
desc = arcpy.Describe
SR = desc(studySegmentLyr).spatialReference
# Check the shape type and get the geometry field
theType = desc(studySegmentLyr).shapeType
shape_field = desc(studySegmentLyr).shapeFieldName
hullList=[]
pntList=[]
rectList=[]
minN = 3
dx = None
dy = None
angle = None
rotation = 90
rows = arcpy.SearchCursor(studySegmentLyr)
for row in rows:
aShape = row.getValue(shape_field) #get the shape from the shape field
pnts = arcpy_helper.shapeToPoints(aShape, theType, arcpy)
if len(pnts) < minN:
arcpy.AddMessage("Ignoring this set. You need at least " + str(minN) + " valid points")
#sys.exit()
else:
returnedList = convexHull_helper.convexHull(pnts)
if len(returnedList)!=3:
arcpy.AddMessage("No hull returned")
else:
hull = returnedList[0]
theReturned = geom_helper.minRect(list(hull))
arcpy.AddMessage(str(theReturned))
aRect = theReturned[0] #rectangle
arcpy.AddMessage("Here is the rect = " + str(aRect))
rectAngle = theReturned[1] #angle
arcpy.AddMessage("Here is the rectAngle = " + str(rectAngle))
dx = min(theReturned[2], theReturned[3]) #width and axis length
arcpy.AddMessage("Here is dx = " + str(dx))
dy = max(theReturned[2], theReturned[3])
arcpy.AddMessage("Here is dy = " + str(dy))
p12dist = geom_helper.dxdyHypot(aRect[0], aRect[1])
p23dist = geom_helper.dxdyHypot(aRect[1], aRect[2])
if p12dist >= p23dist:
pnts = ([aRect[0], aRect[1]])
else:
pnts = ([aRect[1], aRect[2]])
pnts.sort()
Xs = ([aRect[0][0], aRect[1][0], aRect[2][0], aRect[3][0]])
Xs.sort()
rectXCenter = Xs[3] - ((Xs[3] - Xs[0])/2)
arcpy.AddMessage("Here is Xmin,Xmax,XCenter = " + str(Xs[0]) + "; " + str(Xs[3]) + "; " + str(rectXCenter))
Ys = ([aRect[0][1], aRect[1][1], aRect[2][1], aRect[3][1]])
Ys.sort()
rectYCenter = Ys[3] - ((Ys[3] - Ys[0])/2)
arcpy.AddMessage("Here is Ymin,Ymax,YCenter = " + str(Ys[0]) + "; " + str(Ys[3]) + "; " + str(rectYCenter))
arcpy.AddMessage("Try to set extent")
newExtent = colldf.extent
shapeextent = aShape.extent
angle = geom_helper.p1p2Azimuth(pnts[0], pnts[1])
if angle < 0:
rotation = angle + 360
elif angle < 90:
rotation = angle + 270
elif angle < 180:
rotation = angle - 90
elif angle < 270:
rotation = angle + 90
elif angle < 360:
rotation = angle - 270
elif angle >= 360:
rotation = angle - 360
for df in arcpy.mapping.ListDataFrames(mxd):
df.rotation = rotation
arcpy.AddMessage("Rotation = " + str(rotation)) The code below is then repeated 7 times: if dy * 0.88 >= dx * 1.78:
colldf.scale = dy * .88 # 0.88 factor worked. Adjust to taste
else:
colldf.scale = dx * 1.78 # 1.78 factor worked. Adjust to taste
arcpy.RefreshActiveView()
newExtent = colldf.extent
dfwidth = newExtent.XMax - newExtent.XMin
dfheight = newExtent.YMax - newExtent.YMin
newExtent.XMin, newExtent.YMin, newExtent.XMax, newExtent.YMax = (rectXCenter - dfwidth/2), (rectYCenter - dfheight/2), (rectXCenter + dfwidth/2), (rectYCenter + dfheight/2)
colldf.extent = newExtent
... View more
08-23-2012
12:40 PM
|
0
|
0
|
2065
|
|
POST
|
Thanks rfairhur24 for the nugget of information, but when i run the script it does not return any record(s)? Any other suggestion(s) Is your SQLStatement valid for selecting records? Paste it into a Select By Attributes query on the feature class you have used in your model in ArcMap and run it to see what it selects. If it does not select anything build a query that does select something and paste that working query into the tool within your model to see if it gets the same selection results. The selection results will not appear on your map, they will just appear as an output for use within the Model for another tool to use, so connect another tool that uses a feature selection and see if you get any output. let me know what tool you used and publish your SQLStatement. If you are trying to get the selection to show up in the map you have open I believe that takes other Python code.
... View more
08-16-2012
04:22 PM
|
0
|
0
|
2882
|
|
POST
|
You did not use the SQLStatement parameter to filter the selection. I would revise the code as follows: import arcpy
arcpy.env.overwriteOutput = True
try:
# Get the input parameters for the selection tool
FeatureClass = arcpy.GetParameterAsText(0)
SQLStatement = arcpy.GetParameterAsText(1)
# Select by Layer Attribute
arcpy.SelectLayerByAttribute_management(FeatureClass, "NEW_SELECTION", SQLStatement)
# Report a success message
arcpy.AddMessage("All done!")
except:
# Report an error messages
arcpy.AddError("Could not complete the selection")
# Report any error messages that the selection tool might have generated
arcpy.AddMessage(arcpy.GetMessages())
... View more
08-15-2012
09:20 PM
|
0
|
0
|
2882
|
|
POST
|
This worked well, thank you! I was unaware of the field Properties in the Join dialog. Much appreciated information! Here is another suggestion that may help. I find it is very useful to have a point layer containing every street name pair in all possible orders for each intersection. So 2 street names have 2 pairs (A ST & B ST; B ST & A ST), 3 names at an intersection have 6 pairs, and 4 names at an intersection have 12 pairs. These combinations may seem repetitive, but it really helps to not have to worry about name arrangement or alphabetical order when trying to match user provided cross-street names to your intersection names, especially when users do not spell them accurately. For example, if a user gave me "B AVE" at "A AVE", and I get no match, I can easily select STREET LIKE 'B %' AND CROSS_STREET LIKE 'A %' to find that only B ST crossing A ST exists in my network. If names were only in alphabetical order I would have had to remember to first switch the name order in my query to get the match (which I never remember to do until I notice I am getting no hits on names that I know I have in my network). To Create such a point layer, do the Spatial Join of the Dissolved layers with each Dissolve_ID and Street Name to itself (you can put it in twice). Specify that you want the ONE_TO_MANY option and Keep All Target values (the join merge rule and delimiter do not apply). You can use the Field Properties to rename the second set of fields to label them as CROSS_STREET, CROSS_TYPE, etc. The output will include real street name pairs, together with pairs that have the same street name for both names. Just select and export the pairs where STREET_NAME and CROSS_STREET are not the same and your export will contain every possible street name pair in every possible order from your network. This pairing arrangement is especially useful if it is extended to include a Route ID and Measure of each street at an intersection derived from a Linear Referencing route network. You could use Locate Features On Route with your original points or the above pairing output if you only have ArcView or ArcEditor, but it is a slow process. If you have ArcInfo, you can use Features To Lines on your Linear Reference Route Network to segment your routes at each intersection. Then use Feature Vertices to Points with the BOTH option to get the segment end points that have M values. Create a field called MEAS and calculate the M value of each point into it with the formula: !Shape.Centroid.M! If you only have ArcView or ArcEditor you can alternatively create a copy of your Route Network and in Editor select every Route and then Planarize it from the Topology Tool Bar to segment it at each intersection. Then you can calculate an X, Y and M set of fields for each end point (calculate !Shape.FirstPoint.M! and !Shape.LastPoint.M! to get M values from the start and end of the line) and export to table those values with the Route IDs and street names. Then Create two X/Y Event layers for each end and export it to get actual points with Route ID and M values associated with the street name. Merge or Append the two exports together to get the complete set of network end points. Dissolve these points and use the X_Y Dissolve ID, Street_Name, Route ID and Meas as your case fields. These Dissolved points would then be the inputs for the Spatial Join described above. Export the intersection points that have different primary and cross street names where the primary street is correctly matched with the Route ID and Measure values. With this output you can find Primary Streets for every intersection by its Street Name and Cross Street pair (possible duplicates in multiple locations), Street Name and X/Y coordinate pair (unique location), or its Route ID and Measure pair (unique location assuming routes have constantly increasing measures).
... View more
08-15-2012
08:24 AM
|
0
|
0
|
3542
|
|
POST
|
Try moving the parameter[0] value testing part of your code from the initializeParameters section to the updateParameters section. The behavior you are experiencing is consistent with the descriptions of the behavior of the initializeParameters section. It only looks at the input parameter when the tool first opens and then ignores anything that happens afterward. The updateParameters section is intended to be triggered any time a parameter value changes. To keep track of changes in a particular parameter (so that changes in other parameters are ignored), create a self. variable in the __init__ section. For example, assume that there are two date fields in the tool that define a date range. You want the tool to open with an initial pair of dates that are set 3 years apart relative to the first day of the month of today's date whenever the tool is run. You also want the tool to detect whenever the user changes either of the dates, but only if he has kept both dates assigned. The tool will run the change code when the initial values are assigned and then anytime the user changes one of the dates without blanking it out: from datetime import date, timedelta
class ToolValidator(object):
"""Class for validating a tool's parameter values and controlling
the behavior of the tool's dialog."""
def __init__(self):
"""Setup arcpy and the list of tool parameters."""
self.params = arcpy.GetParameterInfo()
self.collisiondates = (None, None) # a date pair defining a range.
def initializeParameters(self):
"""Refine the properties of a tool's parameters. This method is
called when the tool is opened."""
# Set an initial date range relative to the day the user starts the tool
today = date.today()
from_date = str(date(today.year - 3, today.month, 1)) # 3 years back from the 1st of today's month
self.params[0].value = from_date
to_date = str(date(today.year, today.month, 1)-timedelta(days=1)) # the ending of the month before today's month
self.params[1].value = to_date
def updateParameters(self):
"""Modify the values and properties of parameters before internal
validation is performed. This method is called whenever a parameter
has been changed."""
frdate = str(self.params[0].value)
tdate = str(self.params[1].value)
if self.collisiondates != (frdate, tdate) and frdate and tdate:
self.collisiondates = (frdate, tdate)
# code at this indent level only activates when both dates are assigned and the user changed one of the dates
else:
# code at this indent level only activates when one or both the dates of the range are unassigned or they are both unchanged
def updateMessages(self):
"""Modify the messages created by internal validation for each tool
parameter. This method is called after internal validation."""
return
Applying this to your code I would say you want 4 self. variables in the __init__ section to store your lists and make them accessible to the other methods. self.address = [] self.device = [] self.address1 = [] self.device1 = [] You should assign to these variables (add the self. prefix) and perform the SearchCursors in the initializeParameters section as you have done (assuming they are static once read and never need to be updated while the tool is being used). Then you should move the actual assignment of the lists to the updateParameters section (you don't need to test for both "City" and "MHA", just one). self.params[0].filter.list = ["City", "MHA"]
if self.params[0].value == "City":
self.params[1].filter.list = self.device1
self.params[2].filter.list = self.address1
else:
self.params[1].filter.list = self.device
self.params[2].filter.list = self.address
... View more
08-14-2012
12:35 PM
|
0
|
0
|
1013
|
|
POST
|
Based on your screen shots your script tool is still messed up. The second parameter is supposed to have the Obtained From set to "Feature_Class_Table" (the name of the first parameter). The fourth parameter is supposed to be Derived not Required or Optional for the Type property. The 4th parameter will not appear in the tool interface if it is set up correctly. The Direction property is supposed to be Output. Also, its Obtained From property is supposed to be "Value" (the name of the third parameter). If it is set up correctly, after you choose your input feature class the Field parameter should create a field list that will include the field named "Problems". Check that field. Then the third parameter should generate a list of values from the Problem field and you should be able to choose the value you want. The output will be the fourth parameter, which will be set to the value of the third parameter. If this does not work start a new model, drag the tool in (once it works as a script) and then try setting it up. If that works, delete it from your real model and add it again to refresh its settings. This all works for me.
... View more
08-14-2012
06:47 AM
|
0
|
0
|
2025
|
|
POST
|
I do not believe that this questions is about clipping a feature or matching a data frame's extent rectangle to bound a feature. I believe he is trying to do the clipping of a data frame to a shape as shown in this help description where the data frame only displays mapped information inside or outside of a non-rectangular shape that is specified by the user. I would also like in code to be able to reset the data frame to clip what is shown to the same shape as a feature. I am sure it can be done with ArcObjects with the IMap.ClipGeometry property, but I do not see any data frame property that would let me do this in Python. It seems the data frame extent property only takes rectangular coordinates. I have not figured out how to call ArcObjects from Python, but if someone knows how, it would be nice if they could share a way to call the ArcObjects IMap.ClipGeometry property from Python.
... View more
08-13-2012
12:24 PM
|
0
|
0
|
4505
|
|
POST
|
I wanted to amend the instructions above, since Step 6 is unnecessary if you use the Delimiter you want to end up with in Step 4. Therefore, you can use " & " as the Delimiter in Step 4. Also, I recommend against using "_" as a Delimiter, since this is a wildcard character for a file geodatabase. You cannot use a LIKE SQL query with that character or a wildcard character of any particular database. So avoid _, %, *, ?, etc. I also have found that joins do not work properly for some databases if I use "(" or ")" as separators. However, "{" and "}" work fine.
... View more
08-12-2012
08:02 AM
|
0
|
0
|
3542
|
| Title | Kudos | Posted |
|---|---|---|
| 1 | 03-24-2026 11:37 PM | |
| 1 | 03-24-2026 08:01 PM | |
| 6 | 02-23-2026 08:34 AM | |
| 1 | 03-31-2025 03:25 PM | |
| 1 | 03-28-2025 06:54 PM |
| Online Status |
Online
|
| Date Last Visited |
a week ago
|