|
POST
|
I fixed the code in my previous post. SHAPE@X is the Centroid, so I changed [email protected] and Y to the correct syntax. I see nothing that indicates that you need another cursor. My code will back up each selected point and should include any attributes of the FC pioint when the point is being inserted through the InsertRow metnod. Note that my code gets the point and ACCOUNT and SiteStreet inserted in one command. If there are other shared fields, just read them from the FC and follow the pattern to insert them while the point feature is being created. You can insert the point and all attributes of the point at the same time with one insert cursor. The only reason attributes are not being copied is that you are not including the fields in one cursor or the other, either to be read from the updateCursor or to be written to the point being inserted while it is being inserted. Since I have no idea what your data actually looks like and I cannot divine what you really want, I just made the code do what the code you posted did. If you think your other code did something else point it out. I assumed you never wanted a back up of points if no selection existed on your FC. If that is not the case, let me know. That is the only thing my code won't do.
... View more
06-25-2015
04:01 PM
|
2
|
13
|
2857
|
|
POST
|
After looking more carefully at your code you are opening way too many cursors on the same data. Based on how I read what your code is actually doing, you only need one UpdateCursor and one InsertCursor as shown below: import arcpy
from datetime import datetime as d
startTime = d.now()
#set to folder where features are located
arcpy.env.workspace = r"C:\CCAP_Copy_NEW.gdb" #on windows use \ instead of /
arcpy.env.overwriteOutput = True
#---------------------------
#define variables for cursor
#---------------------------
FC = "test"
incidentsFC = "CCAP"
dsc = arcpy.Describe(FC)
rowInserter = arcpy.da.InsertCursor(incidentsFC, ["SHAPE@", 'Account', 'SiteStreet'])
selection_set = dsc.FIDSet
if len(selection_set) == 0:
print "There are no features selected"
elif parcelsCount >= 1:
with arcpy.da.UpdateCursor(FC, ["FacltyType", "StructType", 'Verified", "Status", "StructCat", "APA_CODE", "ACCOUNT", 'SiteStreet', 'SHAPE@X', 'SHAPE@Y']) as rows
for row in rows:
row[0] = ("Single Family Home")
row[1] = ("Primary, Private")
row[2] = ("Yes, GRM, TA")
row[3] = ("Active")
row[4] = ("Residential")
row[5] = ("1110")
ACCOUNT = row[6]
SiteStreet = row[7]
X = row[8]
Y = row[9]
rows.updateRow(row)
inPoint = arcpy.PointGeometry(arcpy.Point(X,Y))
rowInserter.insertRow((inPoint, ACCOUNT, SiteStreet))
del row
del rows
del rowInserter
arcpy.RefreshActiveView()
try:
print '(Elapsed time: ' + str(d.now() - startTime)[:-3] + ')'
except Exception, e:
# If an error occurred, print line number and error message
import traceback, sys
tb = sys.exc_info()[2]
print "Line %i" % tb.tb_lineno
print e.message
... View more
06-25-2015
03:13 PM
|
2
|
15
|
1777
|
|
POST
|
You also need to use the da insert cursor in your code in addition to the Search or Update cursors. Create a list variable and append the objectIDs returned by the InsertRow method. Then use that list to build an expression ot only update points with those objectIDs, either using python logic or SQL. OIDList = [] ... Set up insert cursor OIDList.append(cursor.InsertRow(row)) # use this for an update cursor's SQL where clause whereclause = 'OBJECTID IN (' + ', '.join(OIDList) + ')' If each point is individually attributed then create a dictionary that uses the ObjectIDs returned by the InsertRow method as the key and store a list of attributes as the dictionary value for the key. Then use the dictionaries keys as the join list. OIDDict = {} OID = cursor.InsertRow(row) OIDDict[OID] = [fieldAttribute1, fieldAttribute2, fieldAttribute3] # use this for an update cursor's SQL where clause whereclause = 'OBJECTID IN (' + ', '.join(OIDDict.keys()) + ')' # Assume first field is OBJECTID OID = row[0] row[1] = OIDDict[OID][0] row[2] = OIDDict[OID][1] row[3] = OIDDict[OID][2]
... View more
06-25-2015
10:33 AM
|
0
|
17
|
1777
|
|
POST
|
You can set up Attribute Assistant to preform a rounding calculation on the field during an editing session as soon as the user posts their edit, if you set up the DynamicValue table to do that and include that table in you map project. The DynamicValue table can be set up to trigger field updates in response to feature creation, attribute changes, geometry updates or manual processing. The EXPRESSION value method uses VB Script like the following to update the specified field: Round([LEN], 2) You can get Attribute Assistant as part of any of the Local Government Information Model component downloads here.
... View more
06-25-2015
10:22 AM
|
0
|
1
|
2637
|
|
POST
|
The code I use to create intersection related items is in the Street Matching thread. For the actual Route creation from the Centerlines I have created a field called ROUTE_NAME for my Route ID field. That field is made up of the Street Names plus an abbreviation of the primary area plan boundary it falls in (to distinguish streets like Main St that occur have several separate unrelated alignments across my County). I build the routes based on those IDs using the Create Routes Tool in the Linear Referencing Toolbox. I use the length of the roads for the measures and generally use Lower Left coordinate priority as the primary orientation of the routes so that they generally build from west to east of from south to north. I also check the option to add measures within Gaps. I had to look at the routes to ensure they built correctly. Some don't when you build them from the Lower Left, particularly when the route is on a diagonal alignment or very curvy. I added a field to my Centerline to be hold values like LL, LR, UL and UR to be able to assign the orientation I wanted to apply to sets of Centerlines. That way I can select the lines that should be built for each orientation and run the Create Routes tool. I then append all of those routes together into my final Route feature class in a script.
... View more
06-25-2015
09:01 AM
|
1
|
0
|
2954
|
|
POST
|
The cursor is not to blame. You either have a corrupt fgdb, fc, or a bad install of ArcMap. If you have tech support available to you, then after creating a new fgdb and testing that, then if the speed problem is fixed call them.
... View more
06-24-2015
08:54 PM
|
1
|
19
|
1777
|
|
POST
|
I just tried the insert cursor with a standaline table and it appears to have the same problem as the append tool. It is inserting the records, but if the table view is open, the user will not see the records they just inserted until they manually close and then reopen the table view. That makes interactive automation impossible, since most users would just assume the insert failed if they don't see any new records. Training them to close and reopen the table view to show that the tool worked is not a good tool design. There is no way to use arcpy to automatically close and reopen the table view as far as I can see or to get the table view cache to refresh. Your code is slow because you are not using arcpy.da cursors. The old cursors are useless. Lookup the arcpy.da.update cursor syntax in the help and rewrite your code to use that type of cursor. The only reason this only works for one point is that you added code to restrict it to only copy 1 point (if 0 < parcelsCount2 < 2: means only 1 point. At least 1 point would be if 0 < parcelsCount2:). The append tool is not limited to copying a single point. If you want one point per each feature class, then that is another matter, but why would you need that? It is backed up whether there is one point or 50 points in your backup layer. Anyway, I don't think you have clearly explained what you want to do with the 1 to 50 points with your code.
... View more
06-24-2015
11:49 AM
|
1
|
21
|
1777
|
|
POST
|
Pavement management is dealing with the same basic problem as this post about Street Matching. Linear Referencing (LR) is the solution and was probably developed specifically because of pavement management requirements. LR can create any point or line segment using any portion of a linear feature without having to break that feature up. My solution is to maintian a segmented Centerline layer in a standard polyline feature class with a topology that ensures I create segment breaks at every intersection that fully snap together. Then I have a script that derives 3 things from the Centerlines: 1) an LR Route network that merges all segments for each road into a single route with measures, 2) an intersection point feature class, and 3) an event table containing the route ID and measure of every pair of names in all orders that meet at each intersection. The event table contains fields I can join to other tables (like a PM table) based on a concatenated pair of street names so that I can convert other tables to LR event tables. Based on this derivative data I also have been able to build an application that walks a user through creating human readable limits that the application will convert into the route and measures required to define that segment. You also should develop some kind of ID system for you PM segments. My jurisdiction uses our maintained road number billing numbers and a numerical segment ID for our PM IDs. It looks like M12345A 01000, where the first part is the billing number and the last part is the segment number. The use of a 5 digit segment number starting at 01000 lets us change the segmentation of the roads and maintain a unique sequential (south to north or west to east) numbering of each set of route limits, so that each set of limits over time is defined by a unique number.
... View more
06-24-2015
08:39 AM
|
1
|
3
|
2954
|
|
POST
|
Rebecca: For my tool I am using several fairly low level geometry operations supported by ArcObjects that do not have any obvious equivalent in Python. Additionally, my original data uses true curves and I want a tool that supports the use of true curves, which Python does not do. Those are my primary reasons for doing this in ArcObjects. As far as the code I created for the buffers it was developed in response to this thread and started really taking shape beginning at about the 9th post. Your flight lines would need to be in a projected coordinate systems for my tool to work, since as I mentioned it is too complicated for me to work with angular units and bearings. If your flights cover large areas of the globe, projected coordinate systems may not be suitable for your analysis, since they may produce too much distortion in one or more of the geometry measurements you are wanting to evaluate. Additionally, the buffer width my application expects should be relatively narrow, since the geometry operations start creating too many self overlaps and other strange artifacts or just plain fail if the buffer width is too far from the original line.
... View more
06-23-2015
10:53 AM
|
1
|
1
|
3796
|
|
POST
|
First check that no more than 2 names got concatenated, since if you have cases where more than that occur you probably should figure out why manually. For ordering the cities alphabetically; first select all records that contain a semicolon and space in the output (i.e., CITY_NAME Like '%; %') to make sure no records with just a single name are included, then you can do the following Python calculation to reorder the two names alphabetically (make the field name match your field name of course): sorted(!CITY_NAME!.split('; '))[0] + '; ' + sorted(!CITY_NAME!.split('; '))[1]
... View more
06-23-2015
10:16 AM
|
1
|
0
|
823
|
|
POST
|
I think I kind of understand what you are working with. You split a larger bus route into multiple lines based on city intermediate stops, but did not insert the intermediate stops into the table. Presumably different several bus lines connect the same intermediate destination pairs for at least part of their route so they should have a way to be summarized to get counts from your data. Assuming the points shown on your exhibit are the intermediate stops you mentioned, you can use the Spatial Join tool to get their attributes on to the lines. Use the Spatial Join Tool with the One To One option and make the Lines the Target features and the Points the Join Features. Any points that do not actually represent stops that you split the lines with should be excluded when the tool is run so that only the points at the start and end of each line are used. In the Field Map of the Spatial Join tool right click the field to adjust the properties for the City Name field (or whatever field works to identify each intermediate stop) to make it a Text Field with 254 characters and use Join as the Merge Rule Option with a delimiter character, such as a semicolon or a dash. This will concatenate the two stop names together into a new field on the lines. You may want to alter the Name order in the field, since the concatenation won't really be based on any kind of driving directions or alphabetical order, just the order it encountered the points. You then should be able to use those to summarize the intermediate stop segments based on that field.
... View more
06-23-2015
08:04 AM
|
1
|
2
|
823
|
|
POST
|
I moved this to a new place to hopefully generate at least one comment. I would at least hope that Melita Kennedy or Dan Patterson could give this post a look and offer some suggestions.
... View more
06-23-2015
05:40 AM
|
0
|
3
|
3796
|
|
POST
|
Well, just to give you the idea of what I do I am pasting my actual script code that occurs after the Intersections are created based on a script similar to the one I posted here. The script assumes a template feature class (CL_INTERSECTIONS_PAIRS_Full) exists that holds all of the fields inserted for the CL_INTERSECTIONS_Cross1. Those fields are in the first fields list (["RID", "MEAS", "Distance", "STNAMES", "X_COORD", "Y_COORD", "X_Y_LINK", "WAYS_COUNT", "STNAME_WAYS", "POINT_ID", "STNAME", "X_Y_STNAME", "X_Y_ROUTE", "MAINT_TYPE", "IS_MAINTAINED", "CROSS_STREET", "X_Y_CROSS", "CROSS_MAINT_TYPE", "CROSS_IS_MAINTAINED", "PAIR_STNAMES", "X_Y_PAIRS", "RDNUMBER", "CROSS_RDNUMBER", "INT_CLARIFY", "INT_SPREAD", "GOOGLE_URL", "SEQUENCE"]). As I said, this code is not generic, so it is will not be easily adapted to other data configurations. Not all of the fields I create for intersection points in my personal script are included in the script I posted here. My script is actually creating a Linear Referencing event table so it derives the M coordinates of the points against a set of routes derived from my centerlines in a previous part of the script. Also, although your output has FULLNAME with bracketed names, my output just has a normal single street name in that field. STNAMES is the field in my data with the bracketed set of street names that meet at the intersection. This is only intended to convey the idea of what can be done to manipulate the intersection data with python cursors and dictionaries. import time
from time import strftime
start_time = time.time()
last_time = start_time
import logging
logging.basicConfig(filename=r"\\agency\agencydfs\Tran\Files\GISData\rfairhur\Python Scripts\Centerline_Derivatives_Update_New.log",level=logging.DEBUG)
print "Start script: " + strftime("%Y-%m-%d %H:%M:%S")
logging.info(" ****Start script: " + strftime("%Y-%m-%d %H:%M:%S") + "****")
# Set the necessary product code
import arcinfo
import sys, string, os, arcpy, numpy
from arcpy import env
import datetime
import math
def hms_string(sec_elapsed):
h = int(sec_elapsed / (60 * 60))
m = int((sec_elapsed % (60 * 60)) / 60)
s = sec_elapsed % 60.
return "{}:{:>02}:{:>05.2f}".format(h, m, s)
# End hms_string
def print_times(event, last, start):
current_time = time.time()
print ""
print "{} took {} to execute".format(event, hms_string(current_time - last))
print "{} - Total script run time is {}".format(strftime("%Y-%m-%d %H:%M:%S"), hms_string(current_time - start))
logging.info("")
logging.info(" {} took {} to execute".format(event, hms_string(current_time - last)))
logging.info(" {} - Total script run time is {}".format(strftime("%Y-%m-%d %H:%M:%S"), hms_string(current_time - start)))
return current_time
last_time = print_times("Imports Loaded", last_time, start_time)
env.workspace = r"\\agency\agencydfs\Tran\Files\GISData\rfairhur\Layers\Centerline_Synch.gdb"
Trans_Connection = r"Database Connections\Trans Connection to SQL Server.sde"
TRANS_MAINT = Trans_Connection + r"\GDB_TRANS.TRANS.TRANSPORTATION_MAINT"
Centerline_Synch_gdb = r"\\agency\agencydfs\Tran\Files\GISData\rfairhur\Layers\Centerline_Synch.gdb"
CL_Maint = Centerline_Synch_gdb + r"\CL_Maint"
# Customize the point_ID to fit your network
CL_INTERSECTION_POINTS_IDS = r"CL_INTERSECTION_POINTS_IDS"
CL_INTERSECTION_POINTS_IDS_Full = env.workspace + "\\" + CL_INTERSECTION_POINTS_IDS
CENTERLINE_ROUTES_APPEND = "CENTERLINE_ROUTES_APPEND"
CENTERLINE_ROUTES_APPEND_Full = CL_Maint + "\\" + CENTERLINE_ROUTES_APPEND
CENTERLINE_ROUTES_MERGED_OLD = "CENTERLINE_ROUTES_MERGED_OLD"
CENTERLINE_ROUTES_MERGED_OLD_Full = CL_Maint + "\\" + CENTERLINE_ROUTES_MERGED_OLD
CENTERLINE_ROUTES_MERGED = "CENTERLINE_ROUTES_MERGED"
CENTERLINE_ROUTES_MERGED_Full = CL_Maint + "\\" + CENTERLINE_ROUTES_MERGED
CENTERLINE_ROUTES_MERGED_Layer = "CENTERLINE_ROUTES_MERGED_Layer"
# Customize the old feature class as a backup to fit your network
CL_INTERSECTION_POINTS_OLD = "CL_INTERSECTION_POINTS_OLD"
CL_INTERSECTION_POINTS_OLD_Full = CL_Maint + "\\" + CL_INTERSECTION_POINTS_OLD
# Customize the output feature class or layer to fit your network
CL_INTERSECTION_POINTS = r"CL_INTERSECTION_POINTS"
CL_INTERSECTION_POINTS_Full = CL_Maint + "\\" + CL_INTERSECTION_POINTS
CL_INTERSECTION_POINTS_Layer = "CL_INTERSECTION_POINTS_Layer"
# Customize the point_ID to fit your network
CL_INTERSECTION_POINTS_IDS = r"CL_INTERSECTION_POINTS_IDS"
CL_INTERSECTION_POINTS_IDS_Full = env.workspace + "\\" + CL_INTERSECTION_POINTS_IDS
# Customize the Intersections to fit your network
CENTERLINE_INTERSECTIONS_OLD = r"CENTERLINE_INTERSECTIONS_OLD"
CENTERLINE_INTERSECTIONS_OLD_Full = CL_Maint + "\\" + CENTERLINE_INTERSECTIONS_OLD
# Customize the Intersections to fit your network
CENTERLINE_INTERSECTIONS = r"CENTERLINE_INTERSECTIONS"
CENTERLINE_INTERSECTIONS_Full = CL_Maint + "\\" + CENTERLINE_INTERSECTIONS
CENTERLINE_CULS_STUBS_OLD = r"CENTERLINE_CULS_STUBS_OLD"
CENTERLINE_CULS_STUBS_OLD_Full = CL_Maint + "\\" + CENTERLINE_CULS_STUBS_OLD
CENTERLINE_CULS_STUBS = r"CENTERLINE_CULS_STUBS"
CENTERLINE_CULS_STUBS_Full = CL_Maint + "\\" + CENTERLINE_CULS_STUBS
CENTERLINE_CULS_STUBS_Layer = "CENTERLINE_CULS_STUBS_Layer"
CL_MAINT_TYPE = "CL_MAINT_TYPE"
CL_MAINT_TYPE_Full = Centerline_Synch_gdb + "\\" + CL_MAINT_TYPE
CL_INTERSECTIONS_Cross1 = "CL_INTERSECTIONS_Cross1"
CL_INTERSECTIONS_Cross1_Full = Centerline_Synch_gdb + "\\" + CL_INTERSECTIONS_Cross1
CL_INTERSECTIONS_Cross1_View = "CL_INTERSECTIONS_Cross1_View"
Route_Event_Table_Properties = "RID POINT MEAS"
CL_INTERSECTIONS_PAIRS_OLD = "CL_INTERSECTIONS_PAIRS_OLD"
CL_INTERSECTIONS_PAIRS_OLD_Full = Centerline_Synch_gdb + "\\" + CL_INTERSECTIONS_PAIRS_OLD
CL_INTERSECTIONS_PAIRS = "CL_INTERSECTIONS_PAIRS"
CL_INTERSECTIONS_PAIRS_Full = Centerline_Synch_gdb + "\\" + CL_INTERSECTIONS_PAIRS
CL_INTERSECTIONS_PAIRS_View = "CL_INTERSECTIONS_PAIRS_View"
Try:
arcpy.CreateTable_management(Centerline_Synch_gdb, CL_INTERSECTIONS_Cross1, CL_INTERSECTIONS_PAIRS_Full)
last_time = print_times("Created CL_INTERSECTIONS_Cross1", last_time, start_time)
fields = ["RID", "MEAS", "Distance", "STNAMES", "X_COORD", "Y_COORD", "X_Y_LINK", "WAYS_COUNT", "STNAME_WAYS", "POINT_ID", "STNAME", "X_Y_STNAME", "X_Y_ROUTE", "MAINT_TYPE", "IS_MAINTAINED", "CROSS_STREET", "X_Y_CROSS", "CROSS_MAINT_TYPE", "CROSS_IS_MAINTAINED", "PAIR_STNAMES", "X_Y_PAIRS", "RDNUMBER", "CROSS_RDNUMBER", "INT_CLARIFY", "INT_SPREAD", "GOOGLE_URL", "SEQUENCE"]
insertcursor = arcpy.da.InsertCursor(CL_INTERSECTIONS_Cross1_Full, fields)
insertList = []
print ""
print str(len(pointRouteMDict.keys()))
count = 0
for street in pointIDDict.keys():
count += 1
if not street in pointRouteMDict:
print street + " has no Point and no Route"
else:
for pointXY in pointIDDict[street].keys():
if not pointXY in pointRouteMDict[street]:
print street + " " + pointXY + " has no Route"
else:
for item in pointRouteMDict[street][pointXY]:
insertList.append(item)
insertList = sorted(insertList)
last_time = print_times("Created InsertList", last_time, start_time)
for item in insertList:
row = item
insertcursor.insertRow(row)
del insertcursor
del insertList
pointIDDict = {}
sourceFieldsList = ["X_Y_LINK", "OID@", "FREQUENCY", "FIRST_STNAMES", "CREATED", "MODIFIED", "DELETED", "POINT_ID", "X_COORD", "Y_COORD", "G_LAT", "G_LONG", "GOOGLE_URL"]
# Use list comprehension to build a dictionary from a da SearchCursor
pointIDDict = {r[0]:(r[1:]) for r in arcpy.da.SearchCursor(CL_INTERSECTION_POINTS_IDS_Full, sourceFieldsList)}
last_time = print_times("Created POINT_ID Dict", last_time, start_time)
searchFC = CENTERLINE_ROUTES_MERGED_Full
searchFieldsList = ["FULL_NAME", "ROUTE_NAME", "SHAPE@", "LENGTH", "LINE_LINK", "MMONOTONIC", "PARTS", "FROM_M", "TO_M"]
# This code extracts M values from Route vertices and is much faster than the Locate Features Along Route tool.
pointRouteMDict = {}
with arcpy.da.SearchCursor(searchFC, searchFieldsList) as searchRows:
for searchRow in searchRows:
keyValue = searchRow[0]
if keyValue > " " and keyValue in pointIDDict:
Route = searchRow[1]
feat = searchRow[2]
partnum = 0
partcount = feat.partCount
pntcount = 0
while partnum < partcount:
part = feat.getPart(partnum)
pnt = part.next()
while pnt:
pntcount += 1
x = pnt.X
y = pnt.Y
for xy in pointIDDict[keyValue]:
if abs(x - pointIDDict[keyValue][xy][1]) <= .0001 and abs(y - pointIDDict[keyValue][xy][2]) <= .0001:
cross_streets = pointIDDict[keyValue][xy][0].replace('{' + keyValue + '}', '').replace('}{', ';').replace('{', '').replace('}', '').split(';')
if cross_streets == [""]:
if pointIDDict[keyValue][xy][4] == 1:
cross = "END"
elif pointIDDict[keyValue][xy][4] == 2:
cross = "PSEUDO_NODE"
else:
cross = "BRANCH"
if not keyValue in pointRouteMDict:
pointRouteMDict[keyValue] = {}
pointRouteMDict[keyValue][xy] = [[Route, pnt.M, math.hypot(x-pointIDDict[keyValue][xy][1], y - pointIDDict[keyValue][xy][2]), pointIDDict[keyValue][xy][0], pointIDDict[keyValue][xy][1], pointIDDict[keyValue][xy][2], pointIDDict[keyValue][xy][3], pointIDDict[keyValue][xy][4], pointIDDict[keyValue][xy][5], pointIDDict[keyValue][xy][6], keyValue, xy + "{" + keyValue + "}", xy + "{" + Route + "}", None, None, cross, xy + "{" + cross + "}", None, None, "{" + keyValue + "}{" + cross + "}", xy + "{" + keyValue + "}{" + cross + "}", None, None, None, None, None, None]]
elif not xy in pointRouteMDict[keyValue]:
pointRouteMDict[keyValue][xy] = [[Route, pnt.M, math.hypot(x-pointIDDict[keyValue][xy][1], y - pointIDDict[keyValue][xy][2]), pointIDDict[keyValue][xy][0], pointIDDict[keyValue][xy][1], pointIDDict[keyValue][xy][2], pointIDDict[keyValue][xy][3], pointIDDict[keyValue][xy][4], pointIDDict[keyValue][xy][5], pointIDDict[keyValue][xy][6], keyValue, xy + "{" + keyValue + "}", xy + "{" + Route + "}", None, None, cross, xy + "{" + cross + "}", None, None, "{" + keyValue + "}{" + cross + "}", xy + "{" + keyValue + "}{" + cross + "}", None, None, None, None, None, None]]
else:
pointRouteMDict[keyValue][xy].append([Route, pnt.M, math.hypot(x-pointIDDict[keyValue][xy][1], y - pointIDDict[keyValue][xy][2]), pointIDDict[keyValue][xy][0], pointIDDict[keyValue][xy][1], pointIDDict[keyValue][xy][2], pointIDDict[keyValue][xy][3], pointIDDict[keyValue][xy][4], pointIDDict[keyValue][xy][5], pointIDDict[keyValue][xy][6], keyValue, xy + "{" + keyValue + "}", xy + "{" + Route + "}", None, None, cross, xy + "{" + cross + "}", None, None, "{" + keyValue + "}{" + cross + "}", xy + "{" + keyValue + "}{" + cross + "}", None, None, None, None, None, None])
else:
for cross in cross_streets:
if not keyValue in pointRouteMDict:
pointRouteMDict[keyValue] = {}
pointRouteMDict[keyValue][xy] = [[Route, pnt.M, math.hypot(x-pointIDDict[keyValue][xy][1], y - pointIDDict[keyValue][xy][2]), pointIDDict[keyValue][xy][0], pointIDDict[keyValue][xy][1], pointIDDict[keyValue][xy][2], pointIDDict[keyValue][xy][3], pointIDDict[keyValue][xy][4], pointIDDict[keyValue][xy][5], pointIDDict[keyValue][xy][6], keyValue, xy + "{" + keyValue + "}", xy + "{" + Route + "}", None, None, cross, xy + "{" + cross + "}", None, None, "{" + keyValue + "}{" + cross + "}", xy + "{" + keyValue + "}{" + cross + "}", None, None, None, None, None, None]]
elif not xy in pointRouteMDict[keyValue]:
pointRouteMDict[keyValue][xy] = [[Route, pnt.M, math.hypot(x-pointIDDict[keyValue][xy][1], y - pointIDDict[keyValue][xy][2]), pointIDDict[keyValue][xy][0], pointIDDict[keyValue][xy][1], pointIDDict[keyValue][xy][2], pointIDDict[keyValue][xy][3], pointIDDict[keyValue][xy][4], pointIDDict[keyValue][xy][5], pointIDDict[keyValue][xy][6], keyValue, xy + "{" + keyValue + "}", xy + "{" + Route + "}", None, None, cross, xy + "{" + cross + "}", None, None, "{" + keyValue + "}{" + cross + "}", xy + "{" + keyValue + "}{" + cross + "}", None, None, None, None, None, None]]
else:
pointRouteMDict[keyValue][xy].append([Route, pnt.M, math.hypot(x-pointIDDict[keyValue][xy][1], y - pointIDDict[keyValue][xy][2]), pointIDDict[keyValue][xy][0], pointIDDict[keyValue][xy][1], pointIDDict[keyValue][xy][2], pointIDDict[keyValue][xy][3], pointIDDict[keyValue][xy][4], pointIDDict[keyValue][xy][5], pointIDDict[keyValue][xy][6], keyValue, xy + "{" + keyValue + "}", xy + "{" + Route + "}", None, None, cross, xy + "{" + cross + "}", None, None, "{" + keyValue + "}{" + cross + "}", xy + "{" + keyValue + "}{" + cross + "}", None, None, None, None, None, None])
pnt = part.next()
if not pnt:
pnt = part.next()
partnum += 1
del searchRows
last_time = print_times("Created pointRouteMDict", last_time, start_time)
if arcpy.Exists(CL_INTERSECTIONS_Cross1_Full):
# Process: Delete (43)...
arcpy.Delete_management(CL_INTERSECTIONS_Cross1_Full, "Table")
last_time = print_times("Deleted Cross1", last_time, start_time)
arcpy.CreateTable_management(Centerline_Synch_gdb, CL_INTERSECTIONS_Cross1, CL_INTERSECTIONS_PAIRS_Full)
last_time = print_times("Created CL_INTERSECTIONS_Cross1", last_time, start_time)
fields = ["RID", "MEAS", "Distance", "STNAMES", "X_COORD", "Y_COORD", "X_Y_LINK", "WAYS_COUNT", "STNAME_WAYS", "POINT_ID", "STNAME", "X_Y_STNAME", "X_Y_ROUTE", "MAINT_TYPE", "IS_MAINTAINED", "CROSS_STREET", "X_Y_CROSS", "CROSS_MAINT_TYPE", "CROSS_IS_MAINTAINED", "PAIR_STNAMES", "X_Y_PAIRS", "RDNUMBER", "CROSS_RDNUMBER", "INT_CLARIFY", "INT_SPREAD", "GOOGLE_URL", "SEQUENCE"]
insertcursor = arcpy.da.InsertCursor(CL_INTERSECTIONS_Cross1_Full, fields)
insertList = []
print ""
print str(len(pointRouteMDict.keys()))
count = 0
for street in pointIDDict.keys():
count += 1
if not street in pointRouteMDict:
print street + " has no Point and no Route"
else:
for pointXY in pointIDDict[street].keys():
if not pointXY in pointRouteMDict[street]:
print street + " " + pointXY + " has no Route"
else:
for item in pointRouteMDict[street][pointXY]:
insertList.append(item)
insertList = sorted(insertList)
last_time = print_times("Created InsertList", last_time, start_time)
for item in insertList:
row = item
insertcursor.insertRow(row)
del insertcursor
del insertList
last_time = print_times("Inserted rows into CL_INTERSECTIONS_Cross1", last_time, start_time)
# Process: Make Table View (8)...
arcpy.MakeTableView_management(CL_INTERSECTIONS_Cross1_Full, CL_INTERSECTIONS_Cross1_View, "", "", "RID RID VISIBLE NONE;MEAS MEAS VISIBLE NONE;Distance Distance VISIBLE NONE;STNAMES STNAMES VISIBLE NONE;X_COORD X_COORD VISIBLE NONE;Y_COORD Y_COORD VISIBLE NONE;X_Y_LINK X_Y_LINK VISIBLE NONE;WAYS_COUNT WAYS_COUNT VISIBLE NONE;STNAME_WAYS STNAME_WAYS VISIBLE NONE;POINT_ID POINT_ID VISIBLE NONE;STNAME STNAME VISIBLE NONE;X_Y_STNAME X_Y_STNAME VISIBLE NONE;X_Y_ROUTE X_Y_ROUTE VISIBLE NONE;MAINT_TYPE MAINT_TYPE VISIBLE NONE;IS_MAINTAINED IS_MAINTAINED VISIBLE NONE;CROSS_STREET CROSS_STREET VISIBLE NONE;X_Y_CROSS X_Y_CROSS VISIBLE NONE;CROSS_MAINT_TYPE CROSS_MAINT_TYPE VISIBLE NONE;CROSS_IS_MAINTAINED CROSS_IS_MAINTAINED VISIBLE NONE;PAIR_STNAMES PAIR_STNAMES VISIBLE NONE;X_Y_PAIRS X_Y_PAIRS VISIBLE NONE;RDNUMBER RDNUMBER VISIBLE NONE;CROSS_RDNUMBER CROSS_RDNUMBER VISIBLE NONE; GOOGLE_URL GOOGLE_URL VISIBLE NONE; SEQUENCE SEQUENCE VISIBLE NONE")
sourceFC = CENTERLINE_ROUTES_MERGED_Layer
sourceFieldsList = ["ROUTE_NAME", "FULL_NAME"]
valueDict = {r[0]:(r[1:]) for r in arcpy.da.SearchCursor(sourceFC, sourceFieldsList)}
updateFC = CL_INTERSECTIONS_Cross1_View
updateFieldsList = ["RID", "MEAS", "Distance", "STNAMES", "X_COORD", "Y_COORD", "X_Y_LINK", "WAYS_COUNT", "STNAME_WAYS", "POINT_ID", "STNAME", "X_Y_STNAME", "X_Y_ROUTE", "MAINT_TYPE", "IS_MAINTAINED", "CROSS_STREET", "X_Y_CROSS", "CROSS_MAINT_TYPE", "CROSS_IS_MAINTAINED", "PAIR_STNAMES", "X_Y_PAIRS", "RDNUMBER", "CROSS_RDNUMBER", "GOOGLE_URL", "SEQUENCE"]
with arcpy.da.UpdateCursor(updateFC, updateFieldsList) as updateRows:
for updateRow in updateRows:
keyValue = updateRow[0]
if keyValue in valueDict:
X_Y_STNAME = updateRow[11]
X_Y_CROSS = updateRow[16]
updateRow[13] = XYNameDict[X_Y_STNAME][0]
updateRow[14] = isMaintDict[updateRow[13]]
updateRow[21] = XYNameDict[X_Y_STNAME][1]
if X_Y_CROSS in XYNameDict:
updateRow[17] = XYNameDict[X_Y_CROSS][0]
updateRow[18] = isMaintDict[updateRow[17]]
updateRow[22] = XYNameDict[X_Y_CROSS][1]
else:
updateRow[17] = ""
updateRow[18] = 0
updateRow[22] = ""
updateRows.updateRow(updateRow)
del updateRows
del valueDict
last_time = print_times("Updated Cross1", last_time, start_time)
sourceFields = ["PAIR_STNAMES", "X_Y_LINK"]
pairsXYDict = {}
with arcpy.da.SearchCursor(CL_INTERSECTIONS_Cross1_View, sourceFields) as searchRows:
for searchRow in searchRows:
pair_name = searchRow[0]
XYLink = searchRow[1]
if not pair_name in pairsXYDict:
pairsXYDict[pair_name] = [XYLink]
elif not XYLink in pairsXYDict[pair_name]:
pairsXYDict[pair_name].append(XYLink)
del searchRows
last_time = print_times("Created pairsXYDict", last_time, start_time)
pairsMinMaxDict = {}
pairsYXDict = {}
for key in pairsXYDict:
pairsXYDict[key] = sorted(pairsXYDict[key])
minX = 99999999999
maxX = 0
minY = 99999999999
maxY = 0
YXSorted = []
for item in pairsXYDict[key]:
if minX > float(item[1:13]):
minX = float(item[1:13])
if maxX < float(item[1:13]):
maxX = float(item[1:13])
if minY > float(item[15:27]):
minY = float(item[15:27])
if maxY < float(item[15:27]):
maxY = float(item[15:27])
YX = item[14:27] + item[0:13]
YXSorted.append([YX, item])
pairsMinMaxDict[key] = [minX, maxX, minY, maxY]
pairsYXDict[key] = sorted(YXSorted)
last_time = print_times("Created pairsYXDict", last_time, start_time)
pointLatLongDict = {}
sourceFieldsList = ["X_Y_LINK", "G_LAT", "G_LONG", "POINT_ID"]
# Use list comprehension to build a dictionary from a da SearchCursor
pointLatLongDict = {r[0]:(r[1:]) for r in arcpy.da.SearchCursor(CL_INTERSECTION_POINTS_IDS_Full, sourceFieldsList)}
last_time = print_times("Created XYLatLongDict", last_time, start_time)
RIDVal = " "
Sequence = 0
LastXY = " "
updateFields = ["PAIR_STNAMES", "X_Y_LINK", "INT_CLARIFY", "INT_SPREAD", "RID", "SEQUENCE", "GOOGLE_URL"]
with arcpy.da.UpdateCursor(CL_INTERSECTIONS_Cross1_View, updateFields) as updateRows:
for updateRow in updateRows:
pair_name = updateRow[0]
XYLink = updateRow[1]
if RIDVal != updateRow[4]:
RIDVal = updateRow[4]
LastXY = XYLink
Sequence = 1
if LastXY != XYLink:
LastXY = XYLink
Sequence += 1
updateRow[5] = Sequence
XYSorted = pairsXYDict[pair_name]
YXSorted = pairsYXDict[pair_name]
minX = pairsMinMaxDict[pair_name][0]
maxX = pairsMinMaxDict[pair_name][1]
minY = pairsMinMaxDict[pair_name][2]
maxY = pairsMinMaxDict[pair_name][3]
updateRow[3] = math.hypot(maxX - minX, maxY - minY)
if len(XYSorted) == 1:
updateRow[2] = " "
updateRow[3] = 0
elif len(XYSorted) == 2:
if maxX - minX >= maxY - minY:
if XYLink == XYSorted[0]:
updateRow[2] = "W"
else:
updateRow[2] = "E"
else:
if XYLink == XYSorted[0]:
if float(XYSorted[0][15:27]) > float(XYSorted[1][15:27]):
updateRow[2] = "N"
else:
updateRow[2] = "S"
else:
if float(XYSorted[0][15:27]) < float(XYSorted[1][15:27]):
updateRow[2] = "N"
else:
updateRow[2] = "S"
else:
if maxX - minX >= maxY - minY:
for i in range(len(XYSorted)):
if XYLink == XYSorted:
if len(XYSorted) < 9:
updateRow[2] = str(i+1)
else:
updateRow[2] = "{:02.0f}".format(i+1)
break
else:
for i in range(len(XYSorted)):
if XYLink == YXSorted[1]:
if len(YXSorted) < 9:
updateRow[2] = str(i+1)
else:
updateRow[2] = "{:02.0f}".format(i+1)
break
updateRow[6] = "http://maps.google.com/maps?q=" + str(round(pointLatLongDict[XYLink][0], 8)) + "," + str(round(pointLatLongDict[XYLink][1], 8)) + "+(" + pair_name.replace("}{", " / ").replace("{", "").replace("}", "").replace("'", "") + " - INT_CLARIFY = " + updateRow[2] + " - POINT ID = " + str(pointLatLongDict[XYLink][2]) + ")"
updateRows.updateRow(updateRow)
del updateRows
last_time = print_times("Updated INT_CLARIFY and INT_SPREAD", last_time, start_time)
# Process: Select Layer By Attribute (4)...
arcpy.SelectLayerByAttribute_management(CL_INTERSECTIONS_Cross1_View, "NEW_SELECTION", "\"STNAMES\" LIKE '%}{%'")
# Process: Add RID Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_Cross1_View, "RID", "RIDIND", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add MEAS Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_Cross1_View, "MEAS", "MEASIND", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add STNAMES Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_Cross1_View, "STNAMES", "STNAMESIND", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add STNAME Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_Cross1_View, "STNAME", "I613STNAME", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add X_Y_STNAME Index (2)...
arcpy.AddIndex_management(CL_INTERSECTIONS_Cross1_View, "X_Y_STNAME", "IND864XYSTNAME", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add X_Y_ROUTE Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_Cross1_View, "X_Y_ROUTE", "I613X_Y_ROUTE", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add CROSS_STREET Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_Cross1_View, "CROSS_STREET", "I613CROSS_STREET", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add X_Y_CROSS Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_Cross1_View, "X_Y_CROSS", "IND864XYCROSS", "NON_UNIQUE", "NON_ASCENDING")
last_time = print_times("Added 6 Indexes", last_time, start_time)
if arcpy.Exists(CL_INTERSECTIONS_PAIRS_OLD_Full) and arcpy.Exists(CL_INTERSECTIONS_PAIRS_Full):
# Process: Delete (47)...
arcpy.Delete_management(CL_INTERSECTIONS_PAIRS_OLD_Full, "Table")
if arcpy.Exists(CL_INTERSECTIONS_PAIRS_Full):
# Process: Rename (5)...
arcpy.Rename_management(CL_INTERSECTIONS_PAIRS_Full, CL_INTERSECTIONS_PAIRS_OLD_Full, "Table")
# Process: Sort
arcpy.Sort_management(CL_INTERSECTIONS_Cross1_View, CL_INTERSECTIONS_PAIRS_Full, "RID ASCENDING;MEAS ASCENDING;CROSS_STREET ASCENDING;X_Y_LINK ASCENDING", "UR")
last_time = print_times("Created CL_INTERSECTIONS_PAIRS", last_time, start_time)
# Process: Add RID Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "RID", "RIDIND", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add MEAS Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "MEAS", "MEASIND", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add STNAMES Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "STNAMES", "STNAMESIND", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add X_COORD Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "X_COORD", "X_COORDIND", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add Y_COORD Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "Y_COORD", "Y_COORDIND", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add X_Y_LINK Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "X_Y_LINK", "X_Y_LINKIND", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add STNAME Index (2)...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "STNAME", "STNAME", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add X_Y_STNAME Index (3)...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "X_Y_STNAME", "IND864XYSTNAME", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add X_Y_ROUTE Index (2)...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "X_Y_ROUTE", "I613X_Y_ROUTE", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add CROSS_STREET Index (2)...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "CROSS_STREET", "INDCROSS_ST", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add X_Y_CROSS Index (2)...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "X_Y_CROSS", "IND864XYCROSS", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add PAIRS_STNAMES Index...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "PAIR_STNAMES", "PAIRS_STNAMES", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add X_Y_PAIRS Index (2)...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "X_Y_PAIRS", "IND864XYPAIRS", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add RDNUMBER Index (2)...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "RDNUMBER", "INDRDNUMBER", "NON_UNIQUE", "NON_ASCENDING")
# Process: Add CROSS_RDNUMBER Index (2)...
arcpy.AddIndex_management(CL_INTERSECTIONS_PAIRS_Full, "CROSS_RDNUMBER", "INDCROSS_RDNUMBER", "NON_UNIQUE", "NON_ASCENDING")
last_time = print_times("Added 15 Indexes", last_time, start_time)
except Exception as e:
print e.message
logging.warning("")
logging.warning(e.message)
... View more
06-22-2015
02:40 PM
|
0
|
1
|
5703
|
|
POST
|
I am not sure I understand what you are doing with the SQL. Is the SQL result representing a concatenated field, or two separate fields? Is it showing something like a Calculated field in Access? As far as the Python processing are you wanting separate points for each single street name paired with the intersection ID value? So if 3 names meet at the intersection you would want 3 points? Is it OK if FULLNAME continues to contain the names in brackets and the single name goes in a new separate field or do you want to drop the names in brackets? If you dropped the bracketed names how would you select the "Main St" intersection at "Second Ave"? As I mentioned, I create a derivative of the output of this script to show all intersection pair names in all orders. So 12 points get created if there are 4 names that meet at an intersection, with each of the 4 unbracketed names being used as a primary street name and paired with each of the other 3 unbracketed names in a cross street field. The pair of names is also kept as a bracketed pair for joins so that all possible name pairings in all possible orders have a join field. However, my script that extracts the name pairs points is not a standalone script. It is part of an integrated weekly update script specific to my data. To separate out the code that creates the derivative output and make it more generic for anyone to adapt to their own data would take more time and effort than I can give to it right now.
... View more
06-22-2015
12:56 PM
|
0
|
3
|
5703
|
|
POST
|
I am looking for some conceptual guidance to make sure I am on the right track in how I should deal with spatial references in a geoprocessing tool. Any .Net code is appreciated, especially if it is in VB.Net, but at least initially I am more concerned that I am making the best overall design decisions for how my tool should behave. I have to provide a fair amount of background before getting to my questions, so I have highlighted my questions so that they are easier to pick out. I want my custom geoprocessing tool to behave as much as possible like any other Esri geoprocessing tool, but I am struggling with some complexities related to the spatial reference requirements of the tool. The tool is ultimately supposed to buffer a polyline input using a custom buffering routine that prevents overlap of the buffers where the lines intersect in the polygon output. I developed the code to do the buffering in a VS Project that was not designed as a geoprocessing tool, and currently it only works with hard coded inputs and outputs that use the same Projected Coordinate System (PCS) spatial reference that my data commonly uses. I want to change this code to use a custom tool interface that will let the user configure the input and output work spaces and feature class names and I want it to work for a wide variety of spatial references. I have separately developed a basic custom geoprocessing tool interface that can take any polyline input in any spatial reference and output an empty polygon feature class to any work space and feature class name. The output polygon field schema is derived from the polyline field schema and validated for the output work space. The output spatial reference will be the same as the input spatial reference except when the user either chooses an output Feature Dataset with a different spatial reference from the input or chooses a work space that is not a Feature Dataset and sets the Output Coordinate System of the geoprocessing environment settings to a different spatial reference from the input. Do these behaviors sound like they follow the common geoprocessing tool behaviors that users expect in Esri's geoprocessing tools, or have I overlooked something? I can provide the code in a later post if you are interested in how I got the tool to do these behaviors. Currently my basic custom geoprocessing tool can have both the input and output in a Geographic Coordinate System (GCS), but unlike Esri's tools I do not want to support that option in my completed tool, since I want my buffers to be based on Linear Units. I do not want to have to figure out how to implement Linear Buffers and determine bearings in a coordinate system that uses angular units. Should I alert the user that they cannot make the input and output both use a GCS by adding an error message on the output feature class in the UpdateMessages portion of the tool when I detect that both the input and output will use a GCS? I would like my tool to work if either the input or output uses a Projected Coordinate System (PCS). If the input is in a PCS I plan on using a search cursor to directly read the polyline geometry into a dictionary and use the input coordinate system for the buffering steps. I would only project the polygon geometry created in memory right before storing it in the output shape field if the output is in a different coordinate system. If the input is in a GCS, then the output would have to be in a PCS. I would want to project the polyline geometry of the input after reading it using a cursor and prior to storing it in the dictionary. After that the buffering and output polygon would all use the output spatial reference. Assuming I figure out how to control the logic of choosing the spatial reference the code will use for the buffering, I was thinking of using the IGeometry2.ProjectEx method to actually do the projection of either the output polygon or the input polyline when that step is necessary. I would like to use that method to handle densification of the geometry. Does that sound like the approach I should take? If the user specifies a different spatial reference for the input and output I have developed code that can read the environment settings to configure any geographic transformation chosen by the user. However, since the geographic transformation list presented in the environment settings contains so many irrelevant transformations, should I instead add an optional parameter to the tool interface where the user would choose a transformation from a narrower list of transformations that fit the input and output spatial references? I have seen some code that I should be able to adapt that would do that. If I set up that list, then the user should only be required to choose a transformation if the input/output spatial references have different underlying GCS's. Is there a help reference for how to make a parameter only appear when it is required? Or should I just make my transformation parameter an optional parameter that is always present and then only add an error message when the input/output require it? Thanks in advance for any input you can provide.
... View more
06-20-2015
12:45 PM
|
0
|
13
|
9162
|
| Title | Kudos | Posted |
|---|---|---|
| 1 | 03-24-2026 11:37 PM | |
| 1 | 03-24-2026 08:01 PM | |
| 7 | 02-23-2026 08:34 AM | |
| 1 | 03-31-2025 03:25 PM | |
| 1 | 03-28-2025 06:54 PM |
| Online Status |
Offline
|
| Date Last Visited |
2 weeks ago
|