source: MGET/Branches/Jason/PythonPackage/src/GeoEco/SpatialAnalysis/Points.py @ 1030

Revision 1030, 167.7 KB checked in by jjr8, 5 years ago (diff)

Working on enhancements to the CreatePointsAlongLines? tool for bbest.

Line 
1# Points.py - Provides methods for general-purpose operations with points.
2#
3# Copyright (C) 2007 Jason J. Roberts
4#
5# This program is free software; you can redistribute it and/or
6# modify it under the terms of the GNU General Public License
7# as published by the Free Software Foundation; either version 2
8# of the License, or (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License (available in the file LICENSE.TXT)
14# for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19
20import datetime
21import math
22import os.path
23
24from GeoEco.DynamicDocString import DynamicDocString
25from GeoEco.Internationalization import _
26from GeoEco.Logging import Logger, ProgressReporter
27
28
29class ArcGISPoints(object):
30    __doc__ = DynamicDocString()
31
32    @classmethod
33    def _InsertPoint(cls, gp, insertCursor, x, y, z=None, m=None, attributes=None, shapeFieldName=u'Shape'):
34
35        # Set the geometry field.
36
37        point = gp.CreateObject(u'Point')
38       
39        point.X = x
40        point.Y = y
41        if z is not None:
42            point.Z = z
43        if m is not None:
44            point.M = m
45           
46        insertCursor.SetValue(shapeFieldName, point)
47
48        # If the caller provided any attributes, set them.
49
50        if attributes is not None:
51            for field, value in attributes.items():
52                insertCursor.SetValue(field, value)
53
54        # Insert the row.
55
56        insertCursor.InsertRow()
57
58    @classmethod
59    def InsertPoint(cls, insertCursor, x, y, z=None, m=None, attributes=None, shapeFieldName=u'Shape'):
60        cls.__doc__.Obj.ValidateMethodInvocation()
61
62        # Perform additional parameter validation.
63
64        if not insertCursor.IsOpen:
65            Logger.RaiseException(ValueError(_(u'The insert cursor must be open.')))
66
67        # Insert the point.
68
69        from GeoEco.ArcGIS import GeoprocessorManager
70        gp = GeoprocessorManager.GetWrappedGeoprocessor()
71
72        cls._InsertPoint(gp, insertCursor, x, y, z, m, attributes, shapeFieldName)
73
74    @classmethod
75    def AppendPointsToFeatureClass(cls, featureClass, coordinates, hasZ=False, hasM=False, attributes=None):
76        cls.__doc__.Obj.ValidateMethodInvocation()
77
78        # Validate the length of the coordinates list.
79
80        if not hasZ and not hasM:
81            if len(coordinates) % 2 != 0:
82                Logger.RaiseException(ValueError(_(u'The length of the input list of point coordinates must be evenly divisible by 2, because each point must have an X and a Y coordinate.')))
83            numPoints = len(coordinates) / 2
84           
85        elif hasZ and not hasM:
86            if len(coordinates) % 3 != 0:
87                Logger.RaiseException(ValueError(_(u'The length of the input list of point coordinates must be evenly divisible by 3, because each point must have X, Y, and Z coordinates.')))
88            numPoints = len(coordinates) / 3
89           
90        elif not hasZ and hasM:
91            if len(coordinates) % 3 != 0:
92                Logger.RaiseException(ValueError(_(u'The length of the input list of point coordinates must be evenly divisible by 3, because each point must have X and Y coordinates and a measure value.')))
93            numPoints = len(coordinates) / 3
94           
95        else:
96            if len(coordinates) % 4 != 0:
97                Logger.RaiseException(ValueError(_(u'The length of the input list of point coordinates must be evenly divisible by 4, because each point must have X, Y, and Z coordinates and a measure value.')))
98            numPoints = len(coordinates) / 4
99
100        # Validate that the caller provided or did not provide Z or M
101        # values.
102
103        from GeoEco.ArcGIS import GeoprocessorManager
104        gp = GeoprocessorManager.GetWrappedGeoprocessor()
105
106        describe = gp.Describe(featureClass)
107
108        if hasZ and not describe.HasZ:
109            Logger.RaiseException(ValueError(_(u'The input list of point coordinates includes Z coordinates but the feature class %(fc)s does not have Z coordinates. Please specify a different feature class or remove the Z coordinates from the input list of point coordinates.') % {u'fc': featureClass}))
110        if not hasZ and describe.HasZ:
111            Logger.RaiseException(ValueError(_(u'The input list of point coordinates does not include Z coordinates but the feature class %(fc)s has Z coordinates. Please specify a different feature class or add Z coordinates to the input list of point coordinates.') % {u'fc': featureClass}))
112        if hasM and not describe.HasM:
113            Logger.RaiseException(ValueError(_(u'The input list of point coordinates includes measure values but the feature class %(fc)s does not have measure values. Please specify a different feature class or remove the measure values from the input list of point coordinates.') % {u'fc': featureClass}))
114        if not hasM and describe.HasM:
115            Logger.Warning(_(u'The feature class %(fc)s has measure values but the input list of point coordinates does not include measure values. The points will be added to the feature class with NULL measure values.') % {u'fc': featureClass})
116
117        # Determine the name of the shape field.
118
119        shapeFieldName = describe.ShapeFieldName
120
121        # Open an insert cursor to the feature class and insert the points.
122
123        Logger.Info(_(u'Inserting %(count)i points into feature class %(fc)s...') % {u'count': numPoints, u'fc': featureClass})
124
125        from GeoEco.DatabaseAccess.ArcGIS import ArcGIS91DatabaseConnection
126       
127        connection = ArcGIS91DatabaseConnection()
128        cursor = connection.OpenInsertCursor(featureClass, rowCount=numPoints)
129        try:
130            for i in range(numPoints):
131                if attributes is not None and i < len(attributes):
132                    attrs = attributes[i]
133                else:
134                    attrs = None
135                   
136                if not hasZ and not hasM:
137                    cls._InsertPoint(gp, cursor, x=coordinates[i*2], y=coordinates[i*2 + 1], attributes=attrs, shapeFieldName=shapeFieldName)
138                elif hasZ and not hasM:
139                    cls._InsertPoint(gp, cursor, x=coordinates[i*3], y=coordinates[i*3 + 1], z=coordinates[i*3 + 2], attributes=attrs, shapeFieldName=shapeFieldName)
140                elif not hasZ and hasM:
141                    cls._InsertPoint(gp, cursor, x=coordinates[i*3], y=coordinates[i*3 + 1], m=coordinates[i*3 + 2], attributes=attrs, shapeFieldName=shapeFieldName)
142                else:
143                    cls._InsertPoint(gp, cursor, x=coordinates[i*4], y=coordinates[i*4 + 1], z=coordinates[i*4 + 2], m=coordinates[i*4 + 3], attributes=attrs, shapeFieldName=shapeFieldName)
144        finally:
145            del cursor
146
147    @classmethod
148    def AppendPointsToFeatureClass2(cls, featureClass, xyCoordinates, zCoordinates=None, mValues=None, intAttrsToSet=None, intAttrValues=None, floatAttrsToSet=None, floatAttrValues=None, stringAttrsToSet=None, stringAttrValues=None, dateAttrsToSet=None, dateAttrValues=None):
149        cls.__doc__.Obj.ValidateMethodInvocation()
150
151        # Parse the coordinates into lists.
152
153        coords = []
154       
155        for i in range(len(xyCoordinates)):
156            x, y = PointTypeMetadata.ParseFromArcGISString(xyCoordinates[i])
157            coords.append(x)
158            coords.append(y)
159            if zCoordinates is not None:
160                coords.append(zCoordinates[i])
161            if mValues is not None:
162                coords.append(mValues[i])
163
164        # Build a dictionary of attributes to set.
165
166        attributesDict = {}
167
168        if intAttrsToSet is not None:
169            for i in range(len(intAttrsToSet)):
170                attributesDict[intAttrsToSet[i]] = intAttrValues[i]
171
172        if floatAttrsToSet is not None:
173            for i in range(len(floatAttrsToSet)):
174                attributesDict[floatAttrsToSet[i]] = floatAttrValues[i]
175
176        if stringAttrsToSet is not None:
177            for i in range(len(stringAttrsToSet)):
178                attributesDict[stringAttrsToSet[i]] = stringAttrValues[i]
179
180        if dateAttrsToSet is not None:
181            for i in range(len(dateAttrsToSet)):
182                attributesDict[dateAttrsToSet[i]] = dateAttrValues[i]
183
184        # If the caller provided multiple points, all of them will
185        # receive the same attribute values. Build a list of attribute
186        # dictionaries. Each element of the list will point to the
187        # same dictionary.
188
189        if len(attributesDict) > 0:
190            attributes = []
191            for i in range(len(xyCoordinates)):
192                attributes.append(attributesDict)
193               
194        else:
195            attributes = None
196
197        # Append the points to the feature class.
198
199        cls.AppendPointsToFeatureClass(featureClass, coords, zCoordinates is not None, mValues is not None, attributes)
200
201        # Return successfully.
202
203        return featureClass
204           
205    @classmethod
206    def CreateFeatureClassWithPoints(cls, featureClass, spatialReference, coordinates, hasZ=False, hasM=False, attributes=None, configKeyword=None, spatialGrid1=0, spatialGrid2=0, spatialGrid3=0, overwriteExisting=False):
207        cls.__doc__.Obj.ValidateMethodInvocation()
208
209        # Create a temporary directory to hold the output feature
210        # class while we are constructing it. If the output
211        # feature class is not a shapefile, create a temporary
212        # personal geodatabase to hold the feature class.
213
214        from GeoEco.ArcGIS import GeoprocessorManager
215        gp = GeoprocessorManager.GetWrappedGeoprocessor()
216
217        from GeoEco.DataManagement.Directories import TemporaryDirectory
218        tempDir = TemporaryDirectory()
219        outputIsShapefile = featureClass.lower().endswith(u'.shp')
220
221        if outputIsShapefile:
222            tempGeoDB = None
223            tempWorkspace = tempDir.Path
224            tempExt = u'.shp'
225        else:
226            gp.CreatePersonalGDB_management(tempDir.Path, u'Temp.mdb')
227            tempGeoDB = os.path.join(tempDir.Path, u'Temp.mdb')
228            tempWorkspace = tempGeoDB
229            tempExt = u''
230
231        try:
232            # Create the temporary output feature class.
233
234            if not hasZ and not hasM:
235                gp.CreateFeatureClass_management(tempWorkspace, u'Temp', u'POINT', u'', u'DISABLED', u'DISABLED', spatialReference, configKeyword, spatialGrid1, spatialGrid2, spatialGrid3)
236            elif hasZ and not hasM:
237                gp.CreateFeatureClass_management(tempWorkspace, u'Temp', u'POINT', u'', u'DISABLED', u'ENABLED', spatialReference, configKeyword, spatialGrid1, spatialGrid2, spatialGrid3)
238            elif not hasZ and hasM:
239                gp.CreateFeatureClass_management(tempWorkspace, u'Temp', u'POINT', u'', u'ENABLED', u'DISABLED', spatialReference, configKeyword, spatialGrid1, spatialGrid2, spatialGrid3)
240            else:
241                gp.CreateFeatureClass_management(tempWorkspace, u'Temp', u'POINT', u'', u'ENABLED', u'ENABLED', spatialReference, configKeyword, spatialGrid1, spatialGrid2, spatialGrid3)
242
243            tempFC = os.path.join(tempWorkspace, u'Temp' + tempExt)
244
245            # Append the points.
246
247            cls.AppendPointsToFeatureClass(tempFC, coordinates, hasZ, hasM, attributes)
248
249            # Extract the output feature class from the temporary
250            # directory or geodb.
251
252            oldLogInfoAsDebug = Logger.GetLogInfoAsDebug()
253            Logger.SetLogInfoAsDebug(True)
254            try:
255                GeoprocessorManager.CopyArcGISObject(tempFC, featureClass, overwriteExisting, [u'featureclass', u'shapefile'], _(u'feature class'))
256            finally:
257                Logger.SetLogInfoAsDebug(oldLogInfoAsDebug)
258
259        finally:
260            # If we created a temporary personal geodatabase,
261            # delete it now. If we do not explicitly delete it
262            # here, the geoprocessor will keep a handle open
263            # through MS Access APIs, and the temporary directory
264            # will not be able to be deleted.
265
266            if tempGeoDB is not None:
267                try:
268                    gp.Delete_management(tempGeoDB)
269                except:
270                    pass
271           
272    @classmethod
273    def CreateFeatureClassWithPoints2(cls, featureClass, spatialReference, xyCoordinates, zCoordinates=None, mValues=None, configKeyword=None, spatialGrid1=0, spatialGrid2=0, spatialGrid3=0, overwriteExisting=False):
274        cls.__doc__.Obj.ValidateMethodInvocation()
275
276        # Create a temporary directory to hold the output feature
277        # class while we are constructing it. If the output
278        # feature class is not a shapefile, create a temporary
279        # personal geodatabase to hold the feature class.
280
281        from GeoEco.ArcGIS import GeoprocessorManager
282        gp = GeoprocessorManager.GetWrappedGeoprocessor()
283
284        from GeoEco.DataManagement.Directories import TemporaryDirectory
285        tempDir = TemporaryDirectory()
286        outputIsShapefile = featureClass.lower().endswith(u'.shp')
287
288        if outputIsShapefile:
289            tempGeoDB = None
290            tempWorkspace = tempDir.Path
291            tempExt = u'.shp'
292        else:
293            gp.CreatePersonalGDB_management(tempDir.Path, u'Temp.mdb')
294            tempGeoDB = os.path.join(tempDir.Path, u'Temp.mdb')
295            tempWorkspace = tempGeoDB
296            tempExt = u''
297
298        try:
299            # Create the temporary output feature class.
300
301            hasZ = zCoordinates is not None
302            hasM = mValues is not None
303
304            if not hasZ and not hasM:
305                gp.CreateFeatureClass_management(tempWorkspace, u'Temp', u'POINT', u'', u'DISABLED', u'DISABLED', spatialReference, configKeyword, spatialGrid1, spatialGrid2, spatialGrid3)
306            elif hasZ and not hasM:
307                gp.CreateFeatureClass_management(tempWorkspace, u'Temp', u'POINT', u'', u'DISABLED', u'ENABLED', spatialReference, configKeyword, spatialGrid1, spatialGrid2, spatialGrid3)
308            elif not hasZ and hasM:
309                gp.CreateFeatureClass_management(tempWorkspace, u'Temp', u'POINT', u'', u'ENABLED', u'DISABLED', spatialReference, configKeyword, spatialGrid1, spatialGrid2, spatialGrid3)
310            else:
311                gp.CreateFeatureClass_management(tempWorkspace, u'Temp', u'POINT', u'', u'ENABLED', u'ENABLED', spatialReference, configKeyword, spatialGrid1, spatialGrid2, spatialGrid3)
312
313            tempFC = os.path.join(tempWorkspace, u'Temp' + tempExt)
314
315            # Append the points.
316
317            cls.AppendPointsToFeatureClass2(tempFC, xyCoordinates, zCoordinates, mValues)
318
319            # Extract the output feature class from the temporary
320            # directory or geodb.
321
322            oldLogInfoAsDebug = Logger.GetLogInfoAsDebug()
323            Logger.SetLogInfoAsDebug(True)
324            try:
325                GeoprocessorManager.CopyArcGISObject(tempFC, featureClass, overwriteExisting, [u'featureclass', u'shapefile'], _(u'feature class'))
326            finally:
327                Logger.SetLogInfoAsDebug(oldLogInfoAsDebug)
328
329        finally:
330            # If we created a temporary personal geodatabase,
331            # delete it now. If we do not explicitly delete it
332            # here, the geoprocessor will keep a handle open
333            # through MS Access APIs, and the temporary directory
334            # will not be able to be deleted.
335
336            if tempGeoDB is not None:
337                try:
338                    gp.Delete_management(tempGeoDB)
339                except:
340                    pass
341
342    @classmethod
343    def CreatePointsAlongLines(cls, inputLines, outputFeatureClass, interval, unit, trackIDField=None, orderByField=None, startTimeField=None, endTimeField=None, pointsAtLineEnds=False, distributeRemainder=True, where=None, fieldsToCopy=None, distanceField=u'Distance', pointTimeField=u'PointTime', elapsedField=u'Elapsed', elapsedUnit=u'Hours', overwriteExisting=False):
344        cls.__doc__.Obj.ValidateMethodInvocation()
345        try:
346
347            # Perform additional validation.
348
349            if startTimeField is not None and endTimeField is not None and startTimeField.lower() == endTimeField.lower():
350                raise ValueError(_(u'The Start Time Field and End Time Field parameters must be different.'))
351
352            temporalInterval = unit in [u'days', u'hours', u'minutes', u'seconds']
353            if temporalInterval:
354                if startTimeField is None:
355                    raise ValueError(_(u'A value must be provided for the Start Time Field parameter when the Interval Unit is %(unit)s.') % {u'unit': unit})
356                if endTimeField is None:
357                    raise ValueError(_(u'A value must be provided for the End Time Field parameter when the Interval Unit is %(unit)s.') % {u'unit': unit})
358
359            if startTimeField is None or endTimeField is None:
360                startTimeField = None
361                endTimeField = None
362                pointTimeField = None
363                elapsedField = None
364
365            if fieldsToCopy is not None:
366                fieldsToCopy2 = [f.lower() for f in fieldsToCopy]
367                if distanceField is not None and distanceField.lower() in fieldsToCopy2:
368                    raise ValueError(_(u'The Distance Traveled Field To Create may not be named %(name)s because that is a field of the input lines that you requested be copied to the points. Please specify a different name for the Distance Traveled Field To Create.') % {u'name': distanceField})
369                if pointTimeField is not None and pointTimeField.lower() in fieldsToCopy2:
370                    raise ValueError(_(u'The Time Field To Create may not be named %(name)s because that is a field of the input lines that you requested be copied to the points. Please specify a different name for the Time Field To Create.') % {u'name': pointTimeField})
371                if elapsedField is not None and elapsedField.lower() in fieldsToCopy2:
372                    raise ValueError(_(u'The Elapsed Time Field To Create may not be named %(name)s because that is a field of the input lines that you requested be copied to the points. Please specify a different name for the Elapsed Time Field To Create.') % {u'name': elapsedField})
373               
374            if distanceField is not None:
375                if pointTimeField is not None and pointTimeField.lower() == distanceField.lower():
376                    raise ValueError(_(u'The Distance Traveled Field To Create field and the Time Field To Create field may not have the same name.'))
377                if elapsedField is not None and elapsedField.lower() == distanceField.lower():
378                    raise ValueError(_(u'The Distance Traveled Field To Create field and the Elapsed Time Field To Create field may not have the same name.'))
379               
380            if pointTimeField is not None and elapsedField is not None and elapsedField.lower() == pointTimeField.lower():
381                raise ValueError(_(u'The Time Field To Create field and the Elapsed Time Field To Create field may not have the same name.'))
382
383            from GeoEco.Datasets.ArcGIS import ArcGISWorkspace, ArcGISTable
384           
385            linesTable = ArcGISTable(inputLines)
386            linesSR = linesTable.GetSpatialReference('obj')
387
388            if linesSR.IsProjected() and unit == u'decimal degrees':
389                raise ValueError(_(u'The input lines are in a projected coordinate system but the Interval Unit is decimal degrees. This is not allowed. When the lines are projected, the Interval Unit must be a linear or temporal unit (e.g. meters or days).'))
390
391            # If this the interval unit is temporal, convert the
392            # interval to a datetime.timedelta instance.
393
394            if temporalInterval:
395                if unit == u'days':
396                    interval = datetime.timedelta(interval)
397                elif unit == u'hours':
398                    interval = datetime.timedelta(seconds=interval * 3600)
399                elif unit == u'minutes':
400                    interval = datetime.timedelta(seconds=interval * 60)
401                else:
402                    interval = datetime.timedelta(seconds=interval)
403
404            # Otherwise, if it is linear, convert it to meters.
405
406            elif unit != u'decimal degrees':
407                if unit == u'feet':
408                    interval *= 0.3048
409                elif unit == u'kilometers':
410                    interval *= 1000
411                elif unit == u'meters':
412                    pass
413                elif unit == u'miles':
414                    interval *= 1609.344
415                elif unit == u'nautical miles':
416                    interval *= 1852
417                elif unit == u'yards':
418                    interval *= 0.9144
419                else:
420                    raise ValueError(_(u'"%(unit)s" is an unknown unit. Please use days, decimal degrees, feet, hours, kilometers, meters, miles, minutes, nautical miles, seconds, or yards.') % {u'unit': unit})
421
422            # Create the output feature class.
423
424            Logger.Info(_(u'Creating output feature class %(output)s.') % {u'output': outputFeatureClass})
425
426            from GeoEco.ArcGIS import GeoprocessorManager
427            from GeoEco.Datasets import QueryableAttribute
428
429            workspace = ArcGISWorkspace(os.path.dirname(outputFeatureClass), ArcGISTable, pathParsingExpressions=[u'(?P<TableName>.+)'], queryableAttributes=(QueryableAttribute(u'TableName', _(u'Table name'), UnicodeStringTypeMetadata()),))
430            outputTable = workspace.CreateTable(os.path.basename(outputFeatureClass), u'Point', linesSR)
431
432            sourceFields = [trackIDField, orderByField]
433            if fieldsToCopy is not None:
434                sourceFields.extend(fieldsToCopy)
435
436            gp = GeoprocessorManager.GetWrappedGeoprocessor()
437            destFields = []
438            i = 0
439            while i < len(sourceFields):
440                if sourceFields[i] is None:
441                    del sourceFields[i]
442                    continue
443
444                field = linesTable.GetFieldByName(sourceFields[i])
445                dataType = field.DataType
446                if dataType == u'oid':
447                    dataType = u'int32'
448                    destFields.append(sourceFields[i] + '_')
449                else:
450                    destFields.append(gp.ValidateFieldName(sourceFields[i], outputFeatureClass))
451                   
452                outputTable.AddField(destFields[i], dataType, field.Length, field.Precision, field.IsNullable)
453
454                i += 1
455
456            if distanceField is not None:
457                distanceField = gp.ValidateFieldName(distanceField, outputFeatureClass)
458                outputTable.AddField(distanceField, u'float64')
459                destFields.append(distanceField)
460
461            if pointTimeField is not None:
462                pointTimeField = gp.ValidateFieldName(pointTimeField, outputFeatureClass)
463                outputTable.AddField(pointTimeField, u'datetime')
464                destFields.append(pointTimeField)
465
466            if elapsedField is not None:
467                elapsedField = gp.ValidateFieldName(elapsedField, outputFeatureClass)
468                outputTable.AddField(elapsedField, u'float64')
469                destFields.append(elapsedField)
470
471            # If the caller requested that the remaining distance on a
472            # track should be distributed evenly among all points,
473            # query the lines and tally the length of each track.
474
475            orderLinesBy = []
476            if trackIDField is not None:
477                orderLinesBy.append(trackIDField + ' ASC')
478            if orderByField is not None:
479                orderLinesBy.append(orderByField + ' ASC')
480            else:
481                orderLinesBy.append(inputLines.OIDFieldName + ' ASC')
482            orderLinesBy = ', '.join(orderLinesBy)
483
484            if distributeRemainder:
485                Logger.Info(_(u'Querying %(lines)s and tallying the length of each track.') % {u'lines': inputLines})
486
487                trackLengths = {}
488                trackDurations = {}
489                gotLine = False
490                trackID = None
491                selectCursor = linesTable.OpenSelectCursor(where=where, orderBy=orderLinesBy)
492                try:
493                    while selectCursor.NextRow():
494                        if trackIDField is not None:
495                            newTrackID = selectCursor.GetValue(trackIDField)
496                            if newTrackID != trackID and gotLine:
497                                trackLengths[trackID] = trackLength
498
499                        if not gotLine or trackIDField is not None and newTrackID != trackID:
500                            gotLine = True
501                           
502                            if trackIDField is not None:
503                                trackID = newTrackID
504                            trackIsValid = True
505
506                            trackLength = 0.
507                            trackDuration = datetime.timedelta(0)
508
509                        if not trackIsValid:
510                            continue
511
512                        trackLength += cls._GetLineLength(linesSR, unit, selectCursor, temporalInterval)
513
514                        if startTimeField is not None and endTimeField is not None:
515                            duration = selectCursor.GetValue(endTimeField) - selectCursor.GetValue(startTimeField)
516                            if duration < datetime.timedelta(0):
517                                trackIsValid = False            # The duration of a line is not allowed to be less than zero. We will report a warning about this later.
518                                continue
519                            trackDuration += duration
520                finally:
521                    del selectCursor
522
523                if gotLine:
524                    trackLengths[trackID] = trackLength
525                    if startTimeField is not None and endTimeField is not None:
526                        trackDurations[trackID] = trackDuration
527
528                # Now, for each track, calculate the interval that
529                # accounts for the remainder.
530
531                perTrackInterval = {}
532
533                if temporalInterval:
534                    for trackID, length in trackDurations.items():
535                        intervalInSeconds = interval.microseconds * 0.000001 + interval.seconds + interval.days * 86400.
536                        lengthInSeconds = length.microseconds * 0.000001 + length.seconds + length.days * 86400.
537                        if lengthInSeconds % intervalInSeconds == 0:
538                            perTrackInterval[trackID] = datetime.timedelta(seconds = lengthInSeconds / intervalInSeconds)
539                        else:
540                            perTrackInterval[trackID] = datetime.timedelta(seconds = lengthInSeconds / (lengthInSeconds // intervalInSeconds + 1))
541                        Logger.Debug(_(u'Track %r: length = %r, interval = %r') % (trackID, length, perTrackInterval[trackID]))
542                else:
543                    for trackID, length in trackLengths.items():
544                        if length % interval == 0:
545                            perTrackInterval[trackID] = length / interval
546                        else:
547                            perTrackInterval[trackID] = length / (length // interval + 1)
548                        Logger.Debug(_(u'Track %r: length = %r, interval = %r') % (trackID, length, perTrackInterval[trackID]))
549
550            # Open an insert cursor on the output feature class and a
551            # select cursor on the lines.
552
553            Logger.Info(_(u'Inserting points into %(output)s.') % {u'output': outputFeatureClass})
554
555            insertCursor = outputTable.OpenInsertCursor()
556            try:
557                gotLine = False
558                trackID = None
559                trackIsValid = False
560                selectCursor = linesTable.OpenSelectCursor(where=where, orderBy=orderLinesBy, reportProgress=False)
561                try:
562                    while selectCursor.NextRow():
563
564                        # Read field values for this line.
565
566                        fieldValues = {}
567                        for i in range(len(sourceFields)):
568                            fieldValues[destFields[i]] = selectCursor.GetValue(sourceFields[i])
569
570                        if distanceField is not None:
571                            fieldValues[distanceField] = None
572                        if pointTimeField is not None:
573                            fieldValues[pointTimeField] = None
574                        if elapsedField is not None:
575                            fieldValues[elapsedField] = None
576
577                        if startTimeField is not None and endTimeField is not None:
578                            startTime = selectCursor.GetValue(startTimeField)
579                            endTime = selectCursor.GetValue(endTimeField)
580                            currentTime = startTime
581                        else:
582                            startTime = None
583                            endTime = None
584                            currentTime = None
585
586                        # If there are multiple tracks, get the track
587                        # ID of this line. If this line is the start
588                        # of a new track, and the caller requested
589                        # that we place lines at the end of tracks,
590                        # and we have not done so already, insert the
591                        # end point.
592                       
593                        if trackIDField is not None:
594                            newTrackID = selectCursor.GetValue(trackIDField)
595                            if newTrackID != trackID and pointsAtLineEnds and trackIsValid and (temporalInterval and durationSincePoint > datetime.timedelta(0) or not temporalInterval and distanceSincePoint > 0) and endX is not None:
596                                Logger.Debug('Track %r: Writing end point' % trackID)
597
598                                if distanceField is not None:
599                                    fieldValues[distanceField] = distanceSinceStart
600                                if pointTimeField is not None:
601                                    fieldValues[pointTimeField] = endTime
602                                if elapsedField is not None:
603                                    fieldValues[elapsedField] = durationSinceStart
604
605                                cls._InsertPointForLine(endX, endY, insertCursor, destFields, fieldValues, elapsedUnit)
606
607                        # If this line is the start of a new track,
608                        # initialize our variables.
609
610                        if not gotLine or trackIDField is not None and newTrackID != trackID:
611                            gotLine = True
612                           
613                            if trackIDField is not None:
614                                trackID = newTrackID
615                            trackIsValid = True
616                            startOfTrack = True
617
618                            distanceSinceStart = 0.
619                            durationSinceStart = datetime.timedelta(0)
620                            distanceSincePoint = 0.
621                            durationSincePoint = datetime.timedelta(0)
622
623                            Logger.Debug('Track %r: Started' % trackID)
624
625                            # Calculate the interval to the first
626                            # point (excluding the point that is on
627                            # the starting end of the line, if the
628                            # caller requested points on the ends). If
629                            # the caller caller requested that the
630                            # remaining interval on a track should be
631                            # distributed evenly among all points, the
632                            # the interval will be specific to this
633                            # track. If there are points at the line
634                            # ends, the interval will simply be the
635                            # track-specific point interval. If not,
636                            # then the distace will be one-half of
637                            # that interval.
638                            #
639                            # If the remaining interval should not be
640                            # distributed evenly, then the interval to
641                            # the first point is simply the point
642                            # interval requested by the caller.
643           
644                            if distributeRemainder:
645                                if pointsAtLineEnds:
646                                    intervalToNextPoint = perTrackInterval[trackID]
647                                else:
648                                    intervalToNextPoint = perTrackInterval[trackID] / 2
649                            else:
650                                intervalToNextPoint = interval
651
652                        # If the remainder of the track is invalid,
653                        # skip this line.
654
655                        if not trackIsValid:
656                            Logger.Debug('Track %r: Skipping line; remainder of track is not valid.' % trackID)
657                            continue
658
659                        # If the caller specified time fields but
660                        # either one is NULL, the remainder of this
661                        # track is invalid.
662                       
663                        if startTimeField is not None and startTime is None:
664                            Logger.Warning(_(u'For the track with %(trackIDField)s of %(trackID)s, the line with %(oid)s of %(value)r has a %(f1)s that is NULL. This is not allowed; it prevents the calculation of the duration of this line. No further points will not be generated along this track.') % {u'trackIDField': trackIDField, u'trackID': str(trackID), u'oid': linesTable.OIDFieldName, u'value': selectCursor.GetOID(), u'f1': startTimeField})
665                            trackIsValid = False
666                            continue
667                           
668                        if endTimeField is not None and endTime is None:
669                            Logger.Warning(_(u'For the track with %(trackIDField)s of %(trackID)s, the line with %(oid)s of %(value)r has a %(f1)s that is NULL. This is not allowed; it prevents the calculation of the duration of this line. No further points will not be generated along this track.') % {u'trackIDField': trackIDField, u'trackID': str(trackID), u'oid': linesTable.OIDFieldName, u'value': selectCursor.GetOID(), u'f1': endTimeField})
670                            trackIsValid = False
671                            continue
672
673                        # If the caller specified time fields,
674                        # calculate the duration of this line.
675
676                        if endTime is not None and startTime is not None:
677                            duration = endTime - startTime
678                            Logger.Debug('Track %r: Duration of line = %r.' % (trackID, duration))
679
680                            # If the duration is zero or negative,
681                            # flag this track as invalid and generate
682                            # no further points along it.
683                           
684                            if duration <= datetime.timedelta(0):
685                                if trackIDField is None:
686                                    Logger.Warning(_(u'The line with %(oid)s of %(value)r has a %(f1)s that is greater than or equal to %(f2)s. This is not allowed; it would mean the line had a negative or zero duration. No further points will be generated.') % {u'oid': linesTable.OIDFieldName, u'value': selectCursor.GetOID(), u'f1': startTimeField, u'f2': endTimeField})
687                                else:
688                                    Logger.Warning(_(u'For the track with %(trackIDField)s of %(trackID)s, the line with %(oid)s of %(value)r has a %(f1)s that is greater than or equal to %(f2)s. This is not allowed; it would mean the line had a negative or zero duration. No further points will not be generated along this track.') % {u'trackIDField': trackIDField, u'trackID': str(trackID), u'oid': linesTable.OIDFieldName, u'value': selectCursor.GetOID(), u'f1': startTimeField, u'f2': endTimeField})
689                                trackIsValid = False
690                                continue
691
692                            # Divide the length of this line by its
693                            # duration to get the rate of travel.
694
695                            rate = cls._GetLineLength(linesSR, unit, selectCursor, temporalInterval) / (duration.microseconds * 0.000001 + duration.seconds + duration.days * 86400.)
696
697                            # If we are using a temporal interval,
698                            # multiply this by the temporal interval
699                            # to the next point to get the spatial
700                            # distance along this line to that point.
701
702                            if temporalInterval:
703                                distanceToNextPoint = rate * (intervalToNextPoint.microseconds * 0.000001 + intervalToNextPoint.seconds + intervalToNextPoint.days * 86400.)
704                                Logger.Debug('Track %r: Rate = %r, temporal interval to next point = %r, spatial distance to next point = %r' % (trackID, rate, intervalToNextPoint, distanceToNextPoint))
705
706                        # Iterate through the line's parts.
707
708                        geometry = selectCursor.GetGeometry()
709                        isMultipart = geometry.GetGeometryName().lower() != u'linestring'
710                        partIndex = 0
711                        endX = None
712                        endY = None
713                       
714                        while not isMultipart or isMultipart and partIndex < geometry.GetGeometryCount():
715                            if not isMultipart:
716                                part = geometry
717                            else:
718                                part = geometry.GetGeometryRef(partIndex)
719
720                            for i in range(1, part.GetPointCount()):
721
722                                # If the caller wants points at the
723                                # ends of the track and we have not
724                                # inserted the first point yet, do it.
725
726                                if startOfTrack and pointsAtLineEnds:
727                                    if distanceField is not None:
728                                        fieldValues[distanceField] = distanceSinceStart     # Will always be 0.
729                                    if pointTimeField is not None:
730                                        fieldValues[pointTimeField] = startTime
731                                    if elapsedField is not None:
732                                        fieldValues[elapsedField] = durationSinceStart      # Will always be datetime.timedelta(0)
733                                       
734                                    cls._InsertPointForLine(part.GetX(i-1), part.GetY(i-1), insertCursor, destFields, fieldValues, elapsedUnit)
735                               
736                                startOfTrack = False
737
738                                # Generate points along this part until the remaining interval on this part is less than
739                                # the interval to the next point.
740
741                                currentX = part.GetX(i-1)
742                                currentY = part.GetY(i-1)
743                                endX = part.GetX(i)
744                                endY = part.GetY(i)
745
746                                while True:
747
748                                    # Calculate the spatial distance to the end of this part.
749                                   
750                                    distanceToEnd = cls._GetDistance(currentX, currentY, endX, endY, linesSR, unit, temporalInterval)
751
752                                    # If the interval is not temporal (i.e. it is spatial):
753                                   
754                                    if not temporalInterval:
755
756                                        # Check whether the distance to the end of the part is less than the distance to
757                                        # the next point. If so, break out of the while loop, so we go on to the next
758                                        # part.
759                                       
760                                        if distanceToEnd < intervalToNextPoint:
761                                            distanceSincePoint += distanceToEnd
762                                            distanceSinceStart += distanceToEnd
763                                            intervalToNextPoint -= distanceToEnd
764
765                                            if endTime is not None and startTime is not None:
766                                                delta = datetime.timedelta(seconds=distanceToEnd / rate)
767                                                durationSincePoint += delta
768                                                durationSinceStart += delta
769                                                currentTime += delta
770
771                                            break
772
773                                        # The distance to the end of the part is greater than or equal to the distance
774                                        # to the next point. We want to create the point. Compute its coords and update
775                                        # our accumulators.
776
777                                        pointX, pointY = cls._GetNextPointCoords(currentX, currentY, endX, endY, intervalToNextPoint, linesSR, unit, temporalInterval)
778
779                                        distanceSinceStart += intervalToNextPoint
780                                        if endTime is not None and startTime is not None:
781                                            delta = datetime.timedelta(seconds=intervalToNextPoint / rate)
782                                            durationSinceStart += delta
783                                            currentTime += delta
784
785                                    # Otherwise (the interval is temporal):
786
787                                    else:
788
789                                        # Although the interval is temporal, we have to proceed along the line in
790                                        # spatial coordinates. Previously we used the rate of travel to compute the
791                                        # spatial distance to the next point from the temporal interval. Check whether
792                                        # the distance to the end of the part is less than the distance to the next
793                                        # point. If so, break out of the while loop, so we go on to the next part.
794                                       
795                                        if distanceToEnd < distanceToNextPoint:
796                                            distanceSincePoint += distanceToEnd
797                                            distanceSinceStart += distanceToEnd
798                                            distanceToNextPoint -= distanceToEnd
799
800                                            delta = datetime.timedelta(seconds=distanceToEnd / rate)
801                                            durationSincePoint += delta
802                                            durationSinceStart += delta
803                                            currentTime += delta
804                                            intervalToNextPoint -= delta
805
806                                            break
807
808                                        # The distance to the end of the part is greater than or equal to the distance
809                                        # to the next point. We want to create the point. Compute its coords and update
810                                        # our accumulators.
811
812                                        pointX, pointY = cls._GetNextPointCoords(currentX, currentY, endX, endY, distanceToNextPoint, linesSR, unit, temporalInterval)
813
814                                        distanceSinceStart += distanceToNextPoint
815                                        durationSinceStart += intervalToNextPoint
816                                        currentTime += intervalToNextPoint
817
818                                    # Insert the point and reset the distance and duration accumulators.
819
820                                    if distanceField is not None:
821                                        fieldValues[distanceField] = distanceSinceStart
822                                    if pointTimeField is not None:
823                                        fieldValues[pointTimeField] = currentTime
824                                    if elapsedField is not None:
825                                        fieldValues[elapsedField] = durationSinceStart
826
827                                    cls._InsertPointForLine(pointX, pointY, insertCursor, destFields, fieldValues, elapsedUnit)
828
829                                    currentX = pointX
830                                    currentY = pointY
831
832                                    distanceSincePoint = 0.
833                                    durationSincePoint = datetime.timedelta(0)
834
835                                    if distributeRemainder:
836                                        intervalToNextPoint = perTrackInterval[trackID]
837                                    else:
838                                        intervalToNextPoint = interval
839
840                                    if temporalInterval:
841                                        distanceToNextPoint = rate * (intervalToNextPoint.microseconds * 0.000001 + intervalToNextPoint.seconds + intervalToNextPoint.days * 86400.)
842
843                            if not isMultipart:
844                                break
845                            partIndex += 1
846
847                    # If the caller requested that we place lines at
848                    # the end of tracks, and we have not done so for
849                    # the last track, insert the end point.
850                   
851                    if pointsAtLineEnds and trackIsValid and (temporalInterval and durationSincePoint > datetime.timedelta(0) or not temporalInterval and distanceSincePoint > 0) and endX is not None:
852                        Logger.Debug('Track %r: Writing end point' % trackID)
853
854                        if distanceField is not None:
855                            fieldValues[distanceField] = distanceSinceStart + distanceSincePoint
856                        if pointTimeField is not None:
857                            fieldValues[pointTimeField] = endTime
858                        if elapsedField is not None:
859                            fieldValues[elapsedField] = durationSinceStart + durationSincePoint
860
861                        cls._InsertPointForLine(endX, endY, insertCursor, destFields, fieldValues, elapsedUnit)
862
863                finally:
864                    del selectCursor
865            finally:
866                del insertCursor
867        except:
868            Logger.LogExceptionAsError()
869            raise
870
871    @classmethod
872    def _InsertPointForLine(cls, pointX, pointY, insertCursor, destFields, fieldValues, elapsedUnit):
873        from GeoEco.Datasets import CollectibleObject
874        insertCursor.SetGeometry(CollectibleObject._ogr().CreateGeometryFromWkt('POINT(%r %r)' % (pointX, pointY)))
875        for field in destFields:
876            if isinstance(fieldValues[field], datetime.timedelta):
877                if elapsedUnit == u'days':
878                    insertCursor.SetValue(field, float(fieldValues[field].days) + fieldValues[field].seconds / 86400. + fieldValues[field].microseconds / 86400. * 0.000001)
879                elif elapsedUnit == u'hours':
880                    insertCursor.SetValue(field, fieldValues[field].days * 24. + fieldValues[field].seconds / 3600. + fieldValues[field].microseconds / 3600. * 0.000001)
881                elif elapsedUnit == u'minutes':
882                    insertCursor.SetValue(field, fieldValues[field].days * 1440. + fieldValues[field].seconds / 60. + fieldValues[field].microseconds / 60. * 0.000001)
883                else:
884                    insertCursor.SetValue(field, fieldValues[field].days * 86400. + float(fieldValues[field].seconds) + fieldValues[field].microseconds * 0.000001)
885            else:
886                insertCursor.SetValue(field, fieldValues[field])
887        insertCursor.InsertRow()
888
889    @classmethod
890    def _GetLineLength(cls, linesSR, unit, selectCursor, temporalInterval):
891        lengthInMeters = 0.
892        geometry = selectCursor.GetGeometry()
893        isMultipart = geometry.GetGeometryName().lower() != u'linestring'
894        partIndex = 0
895
896        while not isMultipart or isMultipart and partIndex < geometry.GetGeometryCount():
897            if not isMultipart:
898                part = geometry
899            else:
900                part = geometry.GetGeometryRef(partIndex)
901
902            # If it is it is a geographic coordinate system and the
903            # unit is linear, compute the length of this part using
904            # the haversine formula.
905
906            if not linesSR.IsProjected() and unit != 'decimal degrees' and not temporalInterval:
907                for i in range(1, part.GetPointCount()):
908                    lengthInMeters += cls._HaversineDistance(part.GetX(i), part.GetY(i), part.GetX(i-1), part.GetY(i-1))
909
910            # Otherwise we can just sum up the Euclidean distance of
911            # the part's segements. OGR provides a convenient function
912            # for this.
913
914            else:
915                if not linesSR.IsProjected():
916                    lengthInMeters += part.Length()
917                else:
918                    lengthInMeters += part.Length() * linesSR.GetLinearUnits()
919
920            if not isMultipart:
921                break
922            partIndex += 1
923
924        return lengthInMeters
925
926    @classmethod
927    def _GetDistance(cls, x1, y1, x2, y2, linesSR, unit, temporalInterval):
928        if not linesSR.IsProjected() and unit != 'decimal degrees' and not temporalInterval:
929            return cls._HaversineDistance(x1, y1, x2, y2)
930        if not linesSR.IsProjected():
931            return ((x2 - x1)**2 + (y2 - y1)**2)**0.5
932        return ((x2 - x1)**2 + (y2 - y1)**2)**0.5 * linesSR.GetLinearUnits()
933
934    @classmethod
935    def _GetNextPointCoords(cls, currentX, currentY, endX, endY, distanceToNextPoint, linesSR, unit, temporalInterval):
936
937        # If it is it is a geographic coordinate system and the unit
938        # is linear, we have to perform this calculation on a great
939        # circle. First calculate the forward azimuth (the initial
940        # bearing) toward the end point.
941
942        if not linesSR.IsProjected() and unit != 'decimal degrees' and not temporalInterval:
943            lat1 = math.radians(currentY)
944            lat2 = math.radians(endY)
945            dlon = math.radians(endX - currentX)
946
947            y = math.sin(dlon) * math.cos(lat2)
948            x = math.cos(lat1) * math.sin(lat2) - math.sin(lat1) * math.cos(lat2) * math.cos(dlon)
949            azimuth = math.atan2(y, x)
950
951            # Now compute the coordinates of the next point, traveling
952            # the specified distance on the forward azimuth.
953
954            distRadians = distanceToNextPoint / 6371009     # 6371009 is mean radius of Earth, in meters
955
956            lon1 = math.radians(currentX)
957
958            lat2 = math.asin(math.sin(lat1) * math.cos(distRadians) + math.cos(lat1) * math.sin(distRadians) * math.cos(azimuth))
959            lon2 = lon1 + math.atan2(math.sin(azimuth) * math.sin(distRadians) * math.cos(lat1), math.cos(distRadians) - math.sin(lat1) * math.sin(lat2))
960            lon2 = (lon2 + 3 * math.pi) % (2 * math.pi) - math.pi;      # Normalize to -180 to +180
961
962            return math.degrees(lon2), math.degrees(lat2)
963
964        # Otherwise we can perform this calculation using basic
965        # trigonometry.
966
967        proportion = distanceToNextPoint / (((currentX - endX)**2 + (currentY - endY)**2)**0.5)
968        return currentX + (endX - currentX)*proportion, currentY + (endY - currentY)*proportion
969
970    @classmethod
971    def _HaversineDistance(cls, lon1, lat1, lon2, lat2):
972        lon1, lat1, lon2, lat2 = map(math.radians, [lon1, lat1, lon2, lat2])
973        dlon = lon2 - lon1
974        dlat = lat2 - lat1
975        a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
976        c = 2 * math.asin(math.sqrt(a))
977        return 6371009 * c      # 6371009 is mean radius of Earth, in meters
978
979    @classmethod
980    def FindNearestFeatures(cls, pointFeatures, nearFeatures, searchRadius=None, fidField=None, distanceField=None, angleField=None, overlappingPolygonDistance=u'Zero', wherePointFeatures=None, whereNearFeatures=None, nearestFieldsToCopy=None, destinationPointFields=None):
981        cls.__doc__.Obj.ValidateMethodInvocation()
982        try:
983            # Validate and/or create the point fields.
984
985            fidField, distanceField, angleField, nearestFieldsToCopy, destinationPointFields, fidFieldNullValue, distanceFieldNullValue, angleFieldNullValue, destinationPointFieldsNullValues, tempJoinFIDField, tempNearFIDField = cls._ValidateAndCreatePointFields(pointFeatures, nearFeatures, fidField, distanceField, angleField, nearestFieldsToCopy, destinationPointFields)
986            try:
987
988                # If the point feature class is empty, return now.
989
990                from GeoEco.ArcGIS import GeoprocessorManager
991                gp = GeoprocessorManager.GetWrappedGeoprocessor()
992
993                if gp.GetCount_management(pointFeatures) <= 0:
994                    Logger.Info(_(u'The point feature class %(points)s is empty. There is no work to do.') % {u'points': pointFeatures})
995                    return pointFeatures
996
997                # Create a temp directory and geodatabase to hold temp
998                # feature classes.
999
1000                from GeoEco.DataManagement.Directories import TemporaryDirectory
1001                tempDir = TemporaryDirectory()
1002
1003                tempGeoDB = os.path.join(tempDir.Path, 'Temp.mdb')
1004                gp.CreatePersonalGDB_management(tempDir.Path, 'Temp')
1005                try:
1006
1007                    # Find the near features.
1008
1009                    cls._FindNearestFeatures(tempDir=tempDir.Path,
1010                                             tempGeoDB=tempGeoDB,
1011                                             pointFeatures=pointFeatures,
1012                                             tempJoinFIDField=tempJoinFIDField,
1013                                             tempNearFIDField=tempNearFIDField,
1014                                             nearFeatures=nearFeatures,
1015                                             searchRadius=searchRadius,
1016                                             fidField=fidField,
1017                                             fidFieldNullValue=fidFieldNullValue,
1018                                             distanceField=distanceField,
1019                                             distanceFieldNullValue=distanceFieldNullValue,
1020                                             angleField=angleField,
1021                                             angleFieldNullValue=angleFieldNullValue,
1022                                             overlappingPolygonDistance=overlappingPolygonDistance,
1023                                             wherePointFeatures=wherePointFeatures,
1024                                             whereNearFeatures=whereNearFeatures,
1025                                             nearestFieldsToCopy=nearestFieldsToCopy,
1026                                             destinationPointFields=destinationPointFields,
1027                                             destinationPointFieldsNullValues=destinationPointFieldsNullValues,
1028                                             skipArgumentValidation=True)
1029
1030                # Delete the temp geodatabase. If we do not explicitly
1031                # delete it here, the geoprocessor will keep a handle open
1032                # through MS Access APIs, and the temporary directory will
1033                # not be able to be deleted.
1034
1035                finally:
1036                    try:
1037                        gp.Delete_management(tempGeoDB)
1038                    except:
1039                        pass
1040
1041            # Delete the temporary fields that
1042            # _ValidateAndCreatePointFields created in the caller's
1043            # points.
1044
1045            finally:
1046                from GeoEco.DatabaseAccess.ArcGIS import ArcGIS91DatabaseConnection
1047                conn = ArcGIS91DatabaseConnection()
1048                try:
1049                    conn.DeleteField(pointFeatures, tempJoinFIDField)
1050                except:
1051                    pass
1052                if fidField is None:
1053                    try:
1054                        conn.DeleteField(pointFeatures, tempNearFIDField)
1055                    except:
1056                        pass
1057
1058        except:
1059            Logger.LogExceptionAsError()
1060            raise
1061
1062        # Return successfully.
1063
1064        return pointFeatures
1065
1066    @classmethod
1067    def _ValidateAndCreatePointFields(cls, pointFeatures, nearFeatures, fidField=None, distanceField=None, angleField=None, nearestFieldsToCopy=None, destinationPointFields=None):
1068
1069        # Validate that the caller provided a sufficient number and
1070        # combination of field parameters.
1071       
1072        if fidField is None and distanceField is None and angleField is None and nearestFieldsToCopy is None:
1073            Logger.RaiseException(ValueError(_(u'The FID field, distance field, angle field, and fields to copy parameters cannot all be omitted. You must specify at least one of these parameters.')))
1074
1075        if nearestFieldsToCopy is not None and fidField is None:
1076            Logger.RaiseException(ValueError(_(u'You specified fields to copy from the near features but did not specify a field for the FID fo the near features. In order to copy fields, you must specify a field for the FID.')))
1077
1078        # If the caller provided nearestFieldsToCopy, pointFeatures
1079        # and nearFeatures cannot have the same basename, because we
1080        # have to join them.
1081
1082        if nearestFieldsToCopy is not None and os.path.basename(pointFeatures) == os.path.basename(nearFeatures):
1083            Logger.RaiseException(ValueError(_(u'You requested that fields be copied from the near features to the point features, but the point features and near features have the same base name %(name)s. In order to copy fields from one to the other, they must have different base names (this constraint is imposed by the ArcGIS Add Join tool, which is used to implement the copying).') % {u'name': os.path.basename(pointFeatures)}))
1084
1085        # Validate or create the FID field.
1086
1087        from GeoEco.DatabaseAccess.ArcGIS import ArcGIS91DatabaseConnection
1088        conn = ArcGIS91DatabaseConnection()
1089
1090        from GeoEco.ArcGIS import GeoprocessorManager
1091        gp = GeoprocessorManager.GetWrappedGeoprocessor()
1092
1093        fidFieldNullValue = 'NULL'
1094        if fidField is not None:
1095            addedField = False
1096            if conn.FieldExists(pointFeatures, fidField):
1097                dt = conn.GetFieldDataType(pointFeatures, fidField)
1098                if dt != u'LONG':
1099                    Logger.RaiseException(ValueError(_(u'The field %(field)s of the point features cannot be used to store the feature ID of the near feature because it has the data type %(dt)s. This field must have the data type of LONG. Please specify the name of a different field. If you specify a non-existing field, it will be created for you.') % {u'field': fidField, u'dt': dt}))
1100            else:
1101                fidField = conn.AddField(pointFeatures, fidField, u'LONG')
1102                addedField = True
1103               
1104            if not conn.FieldIsNullable(pointFeatures, fidField):
1105                fidFieldNullValue = -1
1106                if addedField:
1107                    gp.CalculateField_management(pointFeatures, fidField, str(fidFieldNullValue))
1108
1109        # Validate or create the distance field.
1110
1111        distanceFieldNullValue = 'NULL'
1112        if distanceField is not None:
1113            addedField = False
1114            if conn.FieldExists(pointFeatures, distanceField):
1115                dt = conn.GetFieldDataType(pointFeatures, distanceField)
1116                if dt not in [u'FLOAT', u'DOUBLE']:
1117                    Logger.RaiseException(ValueError(_(u'The field %(field)s of the point features cannot be used to store the distance to the near feature because it has the data type %(dt)s. This field must have the data type FLOAT or DOUBLE. Please specify the name of a different field. If you specify a non-existing field, it will be created for you.') % {u'field': distanceField, u'dt': dt}))
1118            else:
1119                distanceField = conn.AddField(pointFeatures, distanceField, u'DOUBLE')
1120                addedField = True
1121               
1122            if not conn.FieldIsNullable(pointFeatures, distanceField):
1123                distanceFieldNullValue = -999999999999.
1124                if addedField:
1125                    gp.CalculateField_management(pointFeatures, distanceField, str(distanceFieldNullValue))
1126
1127        # Validate or create the angle field.
1128
1129        angleFieldNullValue = 'NULL'
1130        if angleField is not None:
1131            addedField = False
1132            if conn.FieldExists(pointFeatures, angleField):
1133                dt = conn.GetFieldDataType(pointFeatures, angleField)
1134                if dt not in [u'FLOAT', u'DOUBLE']:
1135                    Logger.RaiseException(ValueError(_(u'The field %(field)s of the point features cannot be used to store the angle to the near feature because it has the data type %(dt)s. This field must have the data type FLOAT or DOUBLE. Please specify the name of a different field. If you specify a non-existing field, it will be created for you.') % {u'field': angleField, u'dt': dt}))
1136            else:
1137                angleField = conn.AddField(pointFeatures, angleField, u'DOUBLE')
1138                addedField = True
1139               
1140            if not conn.FieldIsNullable(pointFeatures, angleField):
1141                angleFieldNullValue = -9999.
1142                if addedField:
1143                    gp.CalculateField_management(pointFeatures, angleField, str(angleFieldNullValue))
1144
1145        # Validate or create the destination fields.
1146
1147        if destinationPointFields is not None and (nearestFieldsToCopy is None or len(nearestFieldsToCopy) < len(destinationPointFields)):
1148            Logger.RaiseException(ValueError(_(u'The number of point fields to receive copied values exceeds the number of near feature fields to copy values from. For each point field, you must also specify a near feature field.')))
1149
1150        destinationPointFieldsNullValues = []
1151       
1152        if nearestFieldsToCopy is not None:
1153            if destinationPointFields is None:
1154                destinationPointFields = []
1155            if len(destinationPointFields) < len(nearestFieldsToCopy):
1156                destinationPointFields.extend(nearestFieldsToCopy[len(destinationPointFields):])
1157            for i in range(len(nearestFieldsToCopy)):
1158                if nearFeatures is not None:
1159                    sourceDT = conn.GetFieldDataType(nearFeatures, nearestFieldsToCopy[i])
1160                addedField = False
1161                if conn.FieldExists(pointFeatures, destinationPointFields[i]):
1162                    destDT = conn.GetFieldDataType(pointFeatures, destinationPointFields[i])
1163                    if nearFeatures is not None and \
1164                       (sourceDT == u'SHORT' and destDT not in [u'SHORT', u'LONG', u'FLOAT', u'DOUBLE', u'TEXT'] or \
1165                        sourceDT == u'LONG' and destDT not in [u'LONG', u'FLOAT', u'DOUBLE', u'TEXT'] or \
1166                        sourceDT == u'FLOAT' and destDT not in [u'FLOAT', u'DOUBLE', u'TEXT'] or \
1167                        sourceDT == u'DOUBLE' and destDT not in [u'DOUBLE', u'TEXT'] or \
1168                        sourceDT == u'TEXT' and destDT not in [u'TEXT'] or \
1169                        sourceDT == u'DATE' and destDT not in [u'DATE', u'TEXT']):
1170                        Logger.RaiseException(ValueError(_(u'Field %(nf)s of the near features cannot be copied to field %(pf)s of the point features because the data types are incompatible: %(ndt)s values cannot be coerced into %(pdt)s values, at least not without a loss of data. Please specify a different destination field of the point features. If you specify a non-existing field, it will be created for you.') % {u'nf': nearestFieldsToCopy[i], u'pf': destinationPointFields[i], u'ndt': sourceDT, u'pdt': destDT}))
1171                else:
1172                    if nearFeatures is None:
1173                        Logger.RaiseException(ValueError(_(u'You specified that the %(source)s field of the near features should be copied to the %(dest)s field of the point features, but that point field does not exist. In this situation, this tool is designed to create the point field automatically. But you did not provide a near feature class or layer, so the tool cannot determine what data type should be used to create the field. Please create the field yourself and try again, or provide a near feature class or layer that contains the field.') % {u'source': nearestFieldsToCopy[i], u'dest': destinationPointFields[i]}))
1174                    destDT = sourceDT
1175                    destinationPointFields[i] = conn.AddField(pointFeatures, destinationPointFields[i], destDT)     # TODO: copy length, precision, etc.
1176                    addedField = True
1177
1178                if not conn.FieldIsNullable(pointFeatures, destinationPointFields[i]):
1179                    if destDT == u'SHORT':
1180                        destinationPointFieldsNullValues.append(-999)
1181                    elif destDT == u'LONG':
1182                        destinationPointFieldsNullValues.append(-99999999)
1183                    elif destDT in [u'FLOAT', u'DOUBLE']:
1184                        destinationPointFieldsNullValues.append(-1e+030)
1185                    elif destDT in [u'TEXT']:
1186                        destinationPointFieldsNullValues.append('""')
1187                    else:
1188                        destinationPointFieldsNullValues.append(None)
1189                    if addedField:
1190                        gp.CalculateField_management(pointFeatures, destinationPointFields[i], str(destinationPointFieldsNullValues[i]))
1191                else:
1192                    destinationPointFieldsNullValues.append('NULL')
1193
1194                if destinationPointFieldsNullValues[i] != 'NULL':
1195                    Logger.Warning(_(u'Because field %(field)s of the point features cannot receive NULL values, the value %(val)s will be stored instead of NULL.') % {u'field': destinationPointFields[i], u'val': str(destinationPointFieldsNullValues[i])})
1196
1197        # Create a couple of temporary fields to hold FIDs. We will
1198        # delete these when our processing is complete.
1199        #
1200        # tempJoinFIDField contains a copy of the FID of the point
1201        # features. We need this field so we are assured of a stable
1202        # copy of the original FID that survives geoprocessing
1203        # operations.
1204        #
1205        # tempNearFIDField contains the FID of the near feature. We
1206        # only create this field of the caller did not specify a
1207        # destination FID field. We need this, ultimately, because the
1208        # ArcGIS Calculate Field tool cannot copy a NULL value from
1209        # one field to another. When a near feature is not found
1210        # within the search radius, Near_analysis stores -1 for the
1211        # FID. We need to copy it to the caller's points and THEN set
1212        # it to NULL.
1213
1214        import random
1215       
1216        tempJoinFIDField = None
1217        tempNearFIDField = fidField
1218
1219        while tempJoinFIDField is None or conn.FieldExists(pointFeatures, tempJoinFIDField):
1220            tempJoinFIDField = 'OFID_' + str(random.randint(10000,99999))
1221        conn.AddField(pointFeatures, tempJoinFIDField, 'LONG')
1222        try:
1223            gp.CalculateField_management(pointFeatures, tempJoinFIDField, '[' + gp.Describe(pointFeatures).OIDFieldName + ']')
1224
1225            if fidField is not None:
1226                tempNearFIDField = fidField
1227            else:
1228                while tempNearFIDField is None or conn.FieldExists(pointFeatures, tempNearFIDField):
1229                    tempNearFIDField = 'NFID_' + str(random.randint(10000,99999))
1230                conn.AddField(pointFeatures, tempNearFIDField, 'LONG')
1231                if not conn.FieldIsNullable(pointFeatures, tempNearFIDField):
1232                    fidFieldNullValue = -1
1233                    gp.CalculateField_management(pointFeatures, tempNearFIDField, str(fidFieldNullValue))
1234        except:
1235            try:
1236                conn.DeleteField(pointFeatures, tempJoinFIDField)
1237            except:
1238                pass
1239            raise
1240
1241        # Return successfully.
1242
1243        return fidField, distanceField, angleField, nearestFieldsToCopy, destinationPointFields, fidFieldNullValue, distanceFieldNullValue, angleFieldNullValue, destinationPointFieldsNullValues, tempJoinFIDField, tempNearFIDField
1244
1245    @classmethod
1246    def _FindNearestFeatures(cls, tempDir, tempGeoDB, pointFeatures, tempJoinFIDField, tempNearFIDField, nearFeatures, searchRadius=None, fidField=None, fidFieldNullValue=None, distanceField=None, distanceFieldNullValue=None, angleField=None, angleFieldNullValue=None, overlappingPolygonDistance=u'Zero', wherePointFeatures=None, whereNearFeatures=None, nearestFieldsToCopy=None, destinationPointFields=None, destinationPointFieldsNullValues=None, skipArgumentValidation=False):
1247        if not skipArgumentValidation:
1248            cls.__doc__.Obj.ValidateMethodInvocation()
1249
1250        # Obtain metadata about the points and near features.
1251
1252        from GeoEco.ArcGIS import GeoprocessorManager
1253        gp = GeoprocessorManager.GetWrappedGeoprocessor()
1254
1255        pointFeaturesDescribe = gp.Describe(pointFeatures)
1256        pointFeaturesInShapefile = pointFeaturesDescribe.DataType.lower() == 'shapefile'
1257        pointFeaturesCS = gp.CreateSpatialReference_management(pointFeatures).split(';')[0]
1258        if GeoprocessorManager.GetArcGISMajorVersion() == 9 and GeoprocessorManager.GetArcGISMinorVersion() == 1:
1259            pointsAreProjected = hasattr(pointFeaturesDescribe.SpatialReference, 'Classification')
1260        else:
1261            pointsAreProjected = pointFeaturesDescribe.SpatialReference.ProjectionCode != 0
1262
1263        nearFeaturesDescribe = gp.Describe(nearFeatures)
1264        nearFeaturesInShapefile = nearFeaturesDescribe.DataType.lower() == 'shapefile'
1265        nearFeaturesCS = gp.CreateSpatialReference_management(nearFeatures).split(';')[0]
1266        if GeoprocessorManager.GetArcGISMajorVersion() == 9 and GeoprocessorManager.GetArcGISMinorVersion() == 1:
1267            nearAreProjected = hasattr(nearFeaturesDescribe.SpatialReference, 'Classification')
1268        else:
1269            nearAreProjected = nearFeaturesDescribe.SpatialReference.ProjectionCode != 0
1270
1271        # Warn the user if neither the points or the near features are
1272        # projected.
1273
1274        if not pointsAreProjected and not nearAreProjected:
1275            Logger.Warning(_(u'Neither the point features (%(points)s) nor the near features (%(near)s) use a projected coordinate system. Because ArcGIS finds the nearest feature in Euclidean distance, not geodetic distance, the features identified as nearest may not actually be the nearest geodetically.') % {u'points': pointFeatures, u'near': nearFeatures})
1276
1277        # Copy the caller's points into the temp geodatabase,
1278        # selecting the points specified by the caller's where clause
1279        # (if any) and only copying the tempJoinFIDField field.
1280
1281        pointsLayer = GeoprocessorManager.GetUniqueLayerName()
1282
1283        fieldInfo = []
1284        visibleFields = [pointFeaturesDescribe.ShapeFieldName, pointFeaturesDescribe.OIDFieldName, tempJoinFIDField]
1285        fields = gp.ListFields(pointFeatures)
1286        f = fields.Next()
1287        while f is not None:
1288            if f.Name not in visibleFields:
1289                fieldInfo.append(f.Name + ' ' + f.Name + ' HIDDEN')
1290            elif f.Name == tempJoinFIDField:
1291                fieldInfo.append(f.Name + ' ' + f.Name + ' VISIBLE')
1292            f = fields.Next()
1293        fieldInfo = ';'.join(fieldInfo)
1294
1295        gp.MakeFeatureLayer_management(pointFeatures, pointsLayer, wherePointFeatures, None, fieldInfo)
1296        try:
1297            if gp.GetCount_management(pointsLayer) <= 0:
1298                if wherePointFeatures is None:
1299                    Logger.Info(_(u'The point feature class %(points)s is empty. There is no work to do.') % {u'points': pointFeatures})
1300                else:
1301                    Logger.Info(_(u'The where clause %(where)s did not select any points from feature class %(points)s. There is no work to do.') % {u'where': wherePointFeatures, u'points': pointFeatures})
1302                return
1303
1304            Logger.Info(_(u'Copying the points.'))
1305           
1306            if os.path.splitext(os.path.basename(pointFeatures))[0].lower() != 'copiedpoints':       # Our temp name has to be different than pointFeatures, so we can join the two.
1307                copiedPoints = os.path.join(tempGeoDB, 'CopiedPoints')
1308            else:
1309                copiedPoints = os.path.join(tempGeoDB, 'CopiedPoints1')
1310            if gp.Exists(copiedPoints):
1311                gp.Delete(copiedPoints)
1312               
1313            gp.CopyFeatures_management(pointsLayer, copiedPoints)
1314        finally:
1315            try:
1316                gp.Delete(pointsLayer)
1317            except:
1318                pass
1319
1320        # If the points are not projected, or are projected but
1321        # not in the near features' coordinate system, project
1322        # them to the near features coordinate system.
1323        #
1324        # Note: Do not try to combine this with the copying step
1325        # above. Unfortunately, for shapefiles, the ArcGIS 9.2
1326        # Project tool ignores the where clause specified to Make
1327        # Feature Layer and projects the entire shapefile, not
1328        # just the features selected by the where clause.
1329
1330        if not pointsAreProjected or pointsAreProjected and nearAreProjected and pointFeaturesCS.lower() != nearFeaturesCS.lower():
1331            Logger.Info(_(u'Projecting the points to the near features\' coordinate system.'))
1332           
1333            if os.path.splitext(os.path.basename(pointFeatures))[0].lower() != 'projectedpoints':       # Our temp name has to be different than pointFeatures, so we can join the two.
1334                projectedPoints = os.path.join(tempGeoDB, 'ProjectedPoints')
1335            else:
1336                projectedPoints = os.path.join(tempGeoDB, 'ProjectedPoints1')
1337            if gp.Exists(projectedPoints):
1338                gp.Delete(projectedPoints)
1339               
1340            gp.Project_management(copiedPoints, projectedPoints, nearFeaturesCS)
1341
1342        # Otherwise, the points are already in the desired coordinate
1343        # system and we can just use the ones that we copied.
1344
1345        else:
1346            projectedPoints = copiedPoints
1347
1348        # We are now going to do any necessary processing with the
1349        # near features prior to running the Near_analysis tool. If
1350        # the near features are a shapefile, we have an important
1351        # constraint: ArcGIS tools such as Select_analysis and
1352        # CopyFeatures_management, when provided a where clause, do
1353        # not assign the FIDs of the output features to those of the
1354        # input shapefile features. They renumber the FIDs starting
1355        # with 0. But we must keep the original FIDs throughout our
1356        # processing because we need Near_analysis to populate the
1357        # NEAR_FID field with the original FIDs. (We got around this
1358        # problem with the point features by requiring the caller to
1359        # copy the FIDs to the tempJoinFIDField field, but we cannot
1360        # do that with the near features because we want to treat them
1361        # as read-only.)
1362        #
1363        # First, project the near features, if necessary. To maximize
1364        # performance and minimize space utilization, we can omit
1365        # fields that are not referenced by the caller's where clause
1366        # (if any).
1367
1368        if not nearAreProjected:
1369            Logger.Info(_(u'Projecting the near features to the points\' coordinate system.'))
1370
1371            nearFeaturesLayer = GeoprocessorManager.GetUniqueLayerName()
1372
1373            fieldInfo = []
1374            fieldsToIgnore = [pointFeaturesDescribe.ShapeFieldName, pointFeaturesDescribe.OIDFieldName]
1375            fields = gp.ListFields(nearFeatures)
1376            f = fields.Next()
1377            while f is not None:
1378                if f.Name not in fieldsToIgnore:
1379                    if whereNearFeatures is None or f.Name.lower() not in whereNearFeatures.lower():
1380                        fieldInfo.append(f.Name + ' ' + f.Name + ' HIDDEN')
1381                    else:
1382                        fieldInfo.append(f.Name + ' ' + f.Name + ' VISIBLE')
1383                f = fields.Next()
1384            fieldInfo = ';'.join(fieldInfo)
1385
1386            if not nearFeaturesInShapefile:
1387                gp.MakeFeatureLayer_management(nearFeatures, nearFeaturesLayer, whereNearFeatures, None, fieldInfo)     # If not a shapefile, Project_management will honor the where clause and maintain original OBJECTIDs in the output, so we might as well apply whereNearFeatures
1388            else:
1389                gp.MakeFeatureLayer_management(nearFeatures, nearFeaturesLayer, None, None, fieldInfo)                  # If a shapefile, Project_management will ignore the where clause and project all the points anyway
1390            try:
1391                if nearFeaturesInShapefile:
1392                    projectedNearFeatures = os.path.join(tempDir, 'ProjectedNear.shp')
1393                else:
1394                    projectedNearFeatures = os.path.join(tempGeoDB, 'ProjectedNear')
1395                if gp.Exists(projectedNearFeatures):
1396                    gp.Delete(projectedNearFeatures)
1397                   
1398                gp.Project_management(nearFeatures, projectedNearFeatures, pointFeaturesCS)
1399            finally:
1400                try:
1401                    gp.Delete(nearFeaturesLayer)
1402                except:
1403                    pass
1404
1405        # If we did not need to project the near features, we can just
1406        # use them directly.
1407
1408        else:
1409            projectedNearFeatures = nearFeatures
1410
1411        # Create a feature layer that selects the near features
1412        # specified by the caller's where clause (if any).
1413
1414        convertedNearFeaturesToLines = False
1415        projectedNearLayer = GeoprocessorManager.GetUniqueLayerName()
1416        gp.MakeFeatureLayer_management(projectedNearFeatures, projectedNearLayer, whereNearFeatures)
1417        try:
1418
1419            # Determine if the near features are empty. If they are,
1420            # create the NEAR_FID, NEAR_DIST, and (if needed)
1421            # NEAR_ANGLE fields but leave them empty.
1422
1423            totalNearFeatures = gp.GetCount_management(projectedNearLayer)
1424            if totalNearFeatures <= 0:
1425                if whereNearFeatures is not None:
1426                    Logger.Info(_(u'The where clause %(where)s did not select any features from near feature class %(near)s. All points will be updated with NULL values (or the equivalent).') % {u'where': whereNearFeatures, u'near': nearFeatures})
1427                else:
1428                    Logger.Info(_(u'The near feature class %(near)s is empty. All points will be updated with NULL values (or the equivalent).') % {u'near': nearFeatures})
1429
1430                from GeoEco.DatabaseAccess.ArcGIS import ArcGIS91DatabaseConnection
1431                conn = ArcGIS91DatabaseConnection()
1432           
1433                conn.AddField(projectedPoints, 'NEAR_FID', 'LONG')
1434                if fidFieldNullValue is not None:
1435                    gp.CalculateField_management(projectedPoints, 'NEAR_FID', str(fidFieldNullValue))
1436
1437                conn.AddField(projectedPoints, 'NEAR_DIST', 'DOUBLE')
1438                if distanceFieldNullValue is not None:
1439                    gp.CalculateField_management(projectedPoints, 'NEAR_DIST', str(distanceFieldNullValue))
1440
1441                if angleField is not None:
1442                    conn.AddField(projectedPoints, 'NEAR_ANGLE', 'DOUBLE')
1443                    if angleFieldNullValue is not None:
1444                        gp.CalculateField_management(projectedPoints, 'NEAR_ANGLE', str(angleFieldNullValue))
1445
1446            # Otherwise (the near features are not empty), we need to
1447            # run Near_analysis.
1448
1449            else:
1450               
1451                # If they are polylines or polygons, we need to do
1452                # some special processing. If they are polygons, we
1453                # need to convert them to polylines, because the
1454                # ArcGIS 9.2 and lower Near_analysis tool cannot
1455                # operate on polygons. Also, regardless of whether the
1456                # caller provided polylines or we got some by
1457                # converting the caller's polygons, we have to work
1458                # around an apparent bug that exists in ArcGIS 9.2
1459                # that causes Near_analysis to not work right on
1460                # polylines that contain more than two vertices (i.e.
1461                # polylines that contain multiple line segments). With
1462                # Arc 9.2 SP6, I noticed Near was calculating the
1463                # distance to the wrong polylines. This seemed to be
1464                # rectified if I split the lines using the Split Lines
1465                # at Vertices tool.
1466                #
1467                # So: First call Feature To Line. If the near features
1468                # are polygons, it will convert them to polylines. If
1469                # they are polylines already, it will copy them. In
1470                # both cases, Feature To Line will add a FID field
1471                # that we can reference later, to obtain the FID of
1472                # the original feature (polygon or polyline).
1473                #
1474                # Next: Call Split Lines at Vertices to produce a
1475                # feature class of lines having only two vertices.
1476                #
1477                # TODO: Check whether we can omit these steps with Arc
1478                # 9.3. The 9.3 Near tool appears to have been
1479                # completely rewritten.
1480                #
1481                # NOTE: It does seem to be fixed in ArcGIS 10.0.
1482               
1483                if nearFeaturesDescribe.ShapeType.lower() in ['polygon', 'polyline'] and GeoprocessorManager.GetArcGISMajorVersion() < 10:
1484                    if nearFeaturesDescribe.ShapeType.lower() == 'polygon':
1485                        Logger.Info(_(u'Converting the polygons to lines.'))
1486                    else:
1487                        Logger.Info(_(u'Copying the near features.'))
1488
1489                    convertedLinesBeforeSplit = os.path.join(tempGeoDB, 'ConvertedLinesBeforeSplit')
1490                    if gp.Exists(convertedLinesBeforeSplit):
1491                        gp.Delete(convertedLinesBeforeSplit)
1492
1493                    gp.FeatureToLine_management(projectedNearLayer, convertedLinesBeforeSplit)
1494
1495                    Logger.Info(_(u'Splitting the lines to work around a bug in the ArcGIS Near tool.'))
1496
1497                    convertedLines = os.path.join(tempGeoDB, 'ConvertedLines')
1498                    if gp.Exists(convertedLines):
1499                        gp.Delete(convertedLines)
1500
1501                    gp.SplitLine_management(convertedLinesBeforeSplit, convertedLines)
1502
1503                    convertedNearFeaturesToLines = True
1504               
1505                Logger.Info(_(u'Finding the near features nearest to the points.'))
1506
1507                # Run Near_analysis.
1508
1509                if angleField is not None:
1510                    angleParam = u'ANGLE'
1511                else:
1512                    angleParam = u'NO_ANGLE'
1513
1514                if convertedNearFeaturesToLines:
1515                    gp.Near_analysis(projectedPoints, convertedLines, searchRadius, u'NO_LOCATION', angleParam)
1516                else:
1517                    gp.Near_analysis(projectedPoints, projectedNearLayer, searchRadius, u'NO_LOCATION', angleParam)
1518       
1519        # Delete the near features layer.
1520
1521        finally:
1522            try:
1523                gp.Delete(projectedNearLayer)
1524            except:
1525                pass
1526
1527        # If the near features are polygons or polylines, we converted
1528        # them to single-segment lines prior to running Near_analysis.
1529        # Near_analysis stored the line FIDs in the NEAR_FID field.
1530        # But we need the FIDs of the original polygons or polylines.
1531        # FeatureToLine_management added them to the lines as an
1532        # attribute. Now, add a field called NEAR_FID2 and populate it
1533        # with the original FIDs.
1534
1535        NEAR_FID_FieldName = 'NEAR_FID'
1536        if convertedNearFeaturesToLines:
1537
1538            # Add NEAR_FID2 to the temporary projected points in the
1539            # geodatabase, initializing it to -1.
1540
1541            from GeoEco.DatabaseAccess.ArcGIS import ArcGIS91DatabaseConnection
1542            conn = ArcGIS91DatabaseConnection()
1543           
1544            NEAR_FID_FieldName = 'NEAR_FID2'
1545            conn.AddField(projectedPoints, 'NEAR_FID2', 'LONG')
1546            gp.CalculateField_management(projectedPoints, 'NEAR_FID2', '-1')
1547
1548            # Join the projected points to the lines and copy the
1549            # original FIDs to NEAR_FID2.
1550
1551            projectedPointsLayer = GeoprocessorManager.GetUniqueLayerName()
1552            gp.MakeFeatureLayer_management(projectedPoints, projectedPointsLayer)
1553            try:
1554                gp.AddJoin_management(projectedPointsLayer, 'NEAR_FID', convertedLines, gp.Describe(convertedLines).OIDFieldName, 'KEEP_COMMON')
1555                try:
1556                    if gp.GetCount_management(projectedPointsLayer) > 0:
1557                        gp.CalculateField_management(projectedPointsLayer, os.path.basename(projectedPoints) + '.NEAR_FID2', '[ConvertedLines.' + conn.GetFieldNameAtIndex(convertedLines, 2) + ']')
1558                finally:
1559                    gp.RemoveJoin_management(projectedPointsLayer, 'ConvertedLines')
1560
1561                # If the near features are polygons, it is possible
1562                # that some of the points overlap them. We need to
1563                # detect this, for two reasons:
1564                #
1565                # 1) Near_analysis calculated the distance to the edge of
1566                #    the polygon. The caller might want us to set the
1567                #    distance to zero or negative.
1568                #
1569                # 2) If the caller's polygons shared borders (e.g.
1570                #    adjacent cells of a grid), those borders were
1571                #    converted to overlapping lines. We don't know which
1572                #    line Near_analysis picked as the nearest. If it
1573                #    picked the line corresponding to the adjacent
1574                #    polygon, it would cause us to obtain the FID and
1575                #    other desired fields from that polygon, rather than
1576                #    the one that the point overlaps. The caller wants the
1577                #    overlapped polygon.
1578                #
1579                # So: intersect the points with the polygons, join the
1580                # resulting feature class back to the points, copy the
1581                # FIDs from the intersecting polygons to the NEAR_FID2
1582                # field fo the points, and adjust the NEAR_DIST field as
1583                # requested by the caller.
1584
1585                if nearFeaturesDescribe.ShapeType.lower() == 'polygon':
1586                    Logger.Info(_(u'Intersecting the points with the polygons to detect points that overlap the polygons.'))
1587
1588                    intersectedPoints = os.path.join(tempGeoDB, 'IntersectedPoints')
1589                    if gp.Exists(intersectedPoints):
1590                        gp.Delete(intersectedPoints)
1591
1592                    projectedNearLayer = GeoprocessorManager.GetUniqueLayerName()
1593                    gp.MakeFeatureLayer_management(projectedNearFeatures, projectedNearLayer, whereNearFeatures)
1594                    try:
1595                       
1596                        # Unlike previous releases, the Intersect tool
1597                        # in ArcGIS 9.3 will fail with the error
1598                        # "ERROR 000953: Extent of the overlay
1599                        # operation will be empty." if no points
1600                        # intersect the polygons. This problem can be
1601                        # avoided by explicitly specifying the Extent
1602                        # environment setting. I consider this a bug
1603                        # in ArcGIS since it worked in the previous
1604                        # release.
1605
1606                        if GeoprocessorManager.GetArcGISMajorVersion() > 9 or GeoprocessorManager.GetArcGISMajorVersion() == 9 and GeoprocessorManager.GetArcGISMinorVersion() >= 3:
1607                            oldExtent = gp.Extent
1608                            gp.Extent = 'MAXOF'
1609                        try:
1610                            gp.Intersect_analysis("'" + projectedPoints + "';'" + projectedNearLayer + "'", intersectedPoints, 'ONLY_FID')
1611                        finally:
1612                            if GeoprocessorManager.GetArcGISMajorVersion() > 9 or GeoprocessorManager.GetArcGISMajorVersion() == 9 and GeoprocessorManager.GetArcGISMinorVersion() >= 3:
1613                                gp.Extent = oldExtent
1614                       
1615                    finally:
1616                        try:
1617                            gp.Delete(projectedNearLayer)
1618                        except:
1619                            pass
1620
1621                    gp.AddJoin_management(projectedPointsLayer, gp.Describe(projectedPoints).OIDFieldName, intersectedPoints, conn.GetFieldNameAtIndex(intersectedPoints, 2), 'KEEP_COMMON')
1622                    try:
1623                        if gp.GetCount_management(projectedPointsLayer) > 0:
1624                            pointsFCName = os.path.basename(projectedPoints)
1625                            gp.CalculateField_management(projectedPointsLayer, pointsFCName + '.NEAR_FID2', '[IntersectedPoints.' + conn.GetFieldNameAtIndex(convertedLines, 2) + ']')
1626                            if distanceField is not None:
1627                                if overlappingPolygonDistance == 'zero':
1628                                    gp.CalculateField_management(projectedPointsLayer, pointsFCName + '.NEAR_DIST', '0')
1629                                elif overlappingPolygonDistance == 'negative':
1630                                    gp.CalculateField_management(projectedPointsLayer, pointsFCName + '.NEAR_DIST', '0 - [' + pointsFCName + '.NEAR_DIST]')
1631                    finally:
1632                        gp.RemoveJoin_management(projectedPointsLayer, 'IntersectedPoints')
1633
1634            # Delete the projected point features layer that we had to
1635            # create to accomplish the joins.
1636
1637            finally:
1638                try:
1639                    gp.Delete(projectedPointsLayer)
1640                except:
1641                    pass
1642
1643        # We are now ready to populate the caller's specified fields
1644        # of pointFeatures. Join projectedPoints to pointFeatures on
1645        # tempJoinFIDField.
1646
1647        Logger.Info(_(u'Updating the points\' fields.'))
1648
1649        pointsLayer = GeoprocessorManager.GetUniqueLayerName()
1650        gp.MakeFeatureLayer_management(pointFeatures, pointsLayer)
1651        try:
1652            pointsFCName = os.path.splitext(os.path.basename(pointFeatures))[0]
1653            projectedPointsFCName = os.path.basename(projectedPoints)
1654            gp.AddJoin_management(pointsLayer, tempJoinFIDField, projectedPoints, tempJoinFIDField, 'KEEP_COMMON')
1655            try:
1656
1657                # Copy the NEAR_FID (or NEAR_FID2), NEAR_DIST, and
1658                # NEAR_ANGLE to the callers specified fields of
1659                # pointFeatures. If the caller did not specify a
1660                # fidField, we copy NEAR_FID (or NEAR_FID2) to a
1661                # temporary field that we created.
1662               
1663                gp.CalculateField_management(pointsLayer, pointsFCName + '.' + tempNearFIDField, '[' + projectedPointsFCName + '.' + NEAR_FID_FieldName + ']')      # Note: if fidField was specified by the caller, tempNearFIDField == fidField
1664                if distanceField is not None:
1665                    gp.CalculateField_management(pointsLayer, pointsFCName + '.' + distanceField, '[' + projectedPointsFCName + '.NEAR_DIST]')
1666                if angleField is not None:
1667                    gp.CalculateField_management(pointsLayer, pointsFCName + '.' + angleField, '[' + projectedPointsFCName + '.NEAR_ANGLE]')
1668
1669                # If the caller specified fields to copy from the near
1670                # features to the point features, join the near
1671                # features to pointsLayer on fidField (or
1672                # tempNearFIDField) and copy the fields.
1673
1674                if nearestFieldsToCopy is not None:
1675                    gp.SelectLayerByAttribute_management(pointsLayer, 'NEW_SELECTION', pointsFCName + '.' + tempNearFIDField + ' >= 0')
1676                    if gp.GetCount_management(pointsLayer) > 0:
1677                        nearFCName = os.path.splitext(os.path.basename(nearFeatures))[0]
1678                        gp.AddJoin_management(pointsLayer, tempNearFIDField, nearFeatures, nearFeaturesDescribe.OIDFieldName, 'KEEP_COMMON')
1679                        try:
1680                            for i in range(len(nearestFieldsToCopy)):
1681                                gp.CalculateField_management(pointsLayer, destinationPointFields[i], '[' + nearFCName + '.' + nearestFieldsToCopy[i] + ']')
1682                        finally:
1683                            gp.RemoveJoin_management(pointsLayer, nearFCName)
1684
1685                # At this point, fidField (or tempNearFIDField) of
1686                # pointFeatures has -1 for all points for which there
1687                # was no near feature. Select these points and set
1688                # their various fields to NULL, or the equivalent.
1689
1690                gp.SelectLayerByAttribute_management(pointsLayer, 'NEW_SELECTION', pointsFCName + '.' + tempNearFIDField + ' = -1')
1691                if gp.GetCount_management(pointsLayer) > 0:
1692                    if distanceField is not None:
1693                        gp.CalculateField_management(pointsLayer, pointsFCName + '.' + distanceField, str(distanceFieldNullValue))
1694                    if angleField is not None:
1695                        gp.CalculateField_management(pointsLayer, pointsFCName + '.' + angleField, str(angleFieldNullValue))
1696
1697                    if nearestFieldsToCopy is not None:
1698                        for i in range(len(nearestFieldsToCopy)):
1699                            gp.CalculateField_management(pointsLayer, pointsFCName + '.' + destinationPointFields[i], str(destinationPointFieldsNullValues[i]))
1700                       
1701                    if fidField is not None and fidFieldNullValue != -1:
1702                        gp.CalculateField_management(pointsLayer, pointsFCName + '.' + fidField, str(fidFieldNullValue))      # Not sure if this must be done last, but doing it anyway, for safety
1703
1704            finally:
1705                gp.RemoveJoin_management(pointsLayer, projectedPointsFCName)
1706        finally:
1707            try:
1708                gp.Delete(pointsLayer)
1709            except:
1710                pass
1711
1712    @classmethod
1713    def FindNearestFeaturesListedInField(cls, pointFeatures, nearFeaturesField, searchRadius=None, fidField=None, distanceField=None, angleField=None, overlappingPolygonDistance=u'Zero', wherePointFeatures=None, whereNearFeaturesField=None, nearestFieldsToCopy=None, destinationPointFields=None):
1714        cls.__doc__.Obj.ValidateMethodInvocation()
1715        try:
1716
1717            # If the caller did not specify any fields to copy from
1718            # the near features to the point features, validate and/or
1719            # create the point fields now.
1720            #
1721            # If they did specify fields to copy, we can't perform
1722            # this validation until we obtain one of the near feature
1723            # classes or layers, so we can examine the data types of
1724            # the fields.
1725
1726            if nearestFieldsToCopy is None:
1727                fidField, distanceField, angleField, nearestFieldsToCopy, destinationPointFields, fidFieldNullValue, distanceFieldNullValue, angleFieldNullValue, destinationPointFieldsNullValues, tempJoinFIDField, tempNearFIDField = cls._ValidateAndCreatePointFields(pointFeatures, None, fidField, distanceField, angleField, None, None)
1728            try:
1729
1730                # Use the Frequency_analysis tool to count the unique
1731                # combinations of near features and where clauses present
1732                # in the caller's table. We must call _FindNearestFeatures
1733                # once with each unique combination.
1734
1735                if whereNearFeaturesField is None:
1736                    Logger.Info(_(u'Enumerating the unique near feature classes listed in the %(field)s field.') % {u'field': nearFeaturesField})
1737                else:
1738                    Logger.Info(_(u'Enumerating the unique combinations of near feature classes listed in the %(field1)s field and where clauses listed in the %(field2)s field.') % {u'field1': nearFeaturesField, u'field2': whereNearFeaturesField})
1739
1740                from GeoEco.ArcGIS import GeoprocessorManager
1741                gp = GeoprocessorManager.GetWrappedGeoprocessor()
1742
1743                from GeoEco.DatabaseAccess.ArcGIS import ArcGIS91DatabaseConnection
1744                conn = ArcGIS91DatabaseConnection()
1745
1746                from GeoEco.DataManagement.Directories import TemporaryDirectory
1747                tempDir = TemporaryDirectory()
1748
1749                nearFeatureCombos = None
1750                pointsLayer = GeoprocessorManager.GetUniqueLayerName()
1751                gp.MakeFeatureLayer_management(pointFeatures, pointsLayer, wherePointFeatures)
1752                try:
1753                    if gp.GetCount_management(pointsLayer) > 0:
1754                        nearFeatureCombos = []
1755
1756                        # Run Frequency_analysis to produce DBF file in a
1757                        # temp directory.
1758                       
1759                        freqTable = os.path.join(tempDir.Path, GeoprocessorManager.GetUniqueLayerName() + '.dbf')
1760                        if whereNearFeaturesField is None:
1761                            gp.Frequency_analysis(pointsLayer, freqTable, nearFeaturesField)
1762                        else:
1763                            gp.Frequency_analysis(pointsLayer, freqTable, nearFeaturesField + ';' + whereNearFeaturesField)
1764
1765                        # Read the DBF file and populate nearFeatureCombos.
1766
1767                        nfField = conn.GetFieldNameAtIndex(freqTable, 2)
1768                        if whereNearFeaturesField is not None:
1769                            wcField = conn.GetFieldNameAtIndex(freqTable, 3)
1770                        warnedAboutNull = False
1771                           
1772                        cur = conn.OpenSelectCursor(freqTable)
1773                        try:
1774                            while cur.NextRow():
1775                                nf = cur.GetValue(nfField)
1776                                if nf is not None:
1777                                    nf = nf.strip()
1778                                    if len(nf) <= 0:
1779                                        nf = None
1780
1781                                if nf is None:
1782                                    if not warnedAboutNull:
1783                                        Logger.Warning(_(u'At least one point contains NULL or an empty string in the %(field)s field. These points will be ignored.') % {u'field': nearFeaturesField})
1784                                        warnedAboutNull = True
1785                                else:
1786                                    if whereNearFeaturesField is None:
1787                                        nearFeatureCombos.append([nf, None])
1788                                    else:
1789                                        wc = cur.GetValue(wcField)      # Note: do not convert empty strings or sequences of just whitespace to None here. This will be handled later.
1790                                        nearFeatureCombos.append([nf, wc])
1791                            pass
1792                        finally:
1793                            del cur
1794
1795                        # Sort nearFeatureCombos, so we process them in a
1796                        # deterministic order.
1797
1798                        nearFeatureCombos.sort()
1799                       
1800                finally:
1801                    try:
1802                        gp.Delete(pointsLayer)
1803                    except:
1804                        pass
1805
1806                # If we didn't get any points with near features, return
1807                # now.
1808
1809                if nearFeatureCombos is None or len(nearFeatureCombos) <= 0:
1810                    if nearFeatureCombos is None:
1811                        if wherePointFeatures is None:
1812                            Logger.Info(_('The point feature class does not contain any points. There is no work to do.'))
1813                        else:
1814                            Logger.Info(_('The where clause did not select any points from the point feature class. There is no work to do.'))
1815                        if nearestFieldsToCopy is not None:
1816                            Logger.Warning(_(u'You requested that fields be copied from the near features to the points, but you did not provide any points. As a consequence, this tool could not examine the data types of those fields in the near features for the purpose of creating those fields in the point features, or, if the fields already exist, validating their data types. The point fields were not checked or created.'))
1817                    else:
1818                        if wherePointFeatures is None:
1819                            Logger.Info(_('The point feature class does not contain any points that contain a near feature class in the %(field)s field. There is no work to do.') % {u'field': nearFeaturesField})
1820                        else:
1821                            Logger.Info(_('The where clause did not select any points from the point feature class that contain a near feature class in the %(field)s field. There is no work to do.') % {u'field': nearFeaturesField})
1822                        if nearestFieldsToCopy is not None:
1823                            Logger.Warning(_(u'You requested that fields be copied from the near features to the points, but none of the points contained near features. As a consequence, this tool could not examine the data types of those fields in the near features for the purpose of creating those fields in the point features, or, if the fields already exist, validating their data types. The point fields were not checked or created.'))
1824                       
1825                    return pointFeatures
1826
1827                # Create a temp geodatabase to hold temp feature classes.
1828
1829                tempGeoDB = os.path.join(tempDir.Path, 'Temp.mdb')
1830                gp.CreatePersonalGDB_management(tempDir.Path, 'Temp')
1831                try:
1832
1833                    # Process each unique combination in nearFeatureCombos.
1834                   
1835                    for i in range(len(nearFeatureCombos)):
1836
1837                        # If the caller asked us to copy fields from the
1838                        # near features to the points and this is the
1839                        # first time through the loop, we will not have
1840                        # validated the point fields yet. Do it now.
1841
1842                        if i <= 0 and nearestFieldsToCopy is not None:
1843                            if not gp.Exists(nearFeatureCombos[i][0]):
1844                                Logger.RaiseException(ValueError(_(u'The near feature class %(fc)s does not exist.') % {u'fc': nearFeatureCombos[i][0]}))
1845                            fidField, distanceField, angleField, nearestFieldsToCopy, destinationPointFields, fidFieldNullValue, distanceFieldNullValue, angleFieldNullValue, destinationPointFieldsNullValues, tempJoinFIDField, tempNearFIDField = cls._ValidateAndCreatePointFields(pointFeatures, nearFeatureCombos[i][0], fidField, distanceField, angleField, nearestFieldsToCopy, destinationPointFields)
1846
1847                        # If this is the first time through the loop,
1848                        # start the progress reporter.
1849
1850                        if i <= 0:
1851                            if whereNearFeaturesField is None:
1852                                Logger.Info(_(u'Finding nearest features for %(count)i unique feature classes listed in the %(field)s field.') % {u'count': len(nearFeatureCombos), u'field': nearFeaturesField})
1853                            else:
1854                                Logger.Info(_(u'Finding nearest features for %(count)i unique combinations of near feature classes listed in the %(field1)s field and where clauses listed in the %(field2)s field.') % {u'count': len(nearFeatureCombos), u'field1': nearFeaturesField, u'field2': whereNearFeaturesField})
1855                            progressReporter = ProgressReporter()
1856                            progressReporter.Start(len(nearFeatureCombos))
1857
1858                        # Call _FindNearestFeatures on this combination.
1859
1860                        where1 = u'%s = \'%s\'' % (nearFeaturesField, nearFeatureCombos[i][0])
1861                        if whereNearFeaturesField is not None:
1862                            if nearFeatureCombos[i][1] is not None:
1863                                where1 = u' AND '.join([where1, u'%s = \'%s\'' % (whereNearFeaturesField, nearFeatureCombos[i][1].replace("'", "''"))])
1864                            else:
1865                                where1 = u' AND '.join([where1, u'%s IS NULL' % whereNearFeaturesField])
1866                        if wherePointFeatures is not None:
1867                            where1 = u' AND '.join([where1, wherePointFeatures])
1868
1869                        where2 = nearFeatureCombos[i][1]
1870                        if where2 is not None and len(where2.strip()) <= 0:     # Now treat empty strings and sequences of whitespace as None. We didn't want to do it in where1 above, because that would cause us to not select the proper points.
1871                            where2 = None
1872
1873                        oldLogInfoAsDebug = Logger.GetLogInfoAsDebug()
1874                        Logger.SetLogInfoAsDebug(True)
1875                        try:
1876                            cls._FindNearestFeatures(tempDir=tempDir.Path,
1877                                                     tempGeoDB=tempGeoDB,
1878                                                     pointFeatures=pointFeatures,
1879                                                     tempJoinFIDField=tempJoinFIDField,
1880                                                     tempNearFIDField=tempNearFIDField,
1881                                                     nearFeatures=nearFeatureCombos[i][0],
1882                                                     searchRadius=searchRadius,
1883                                                     fidField=fidField,
1884                                                     fidFieldNullValue=fidFieldNullValue,
1885                                                     distanceField=distanceField,
1886                                                     distanceFieldNullValue=distanceFieldNullValue,
1887                                                     angleField=angleField,
1888                                                     angleFieldNullValue=angleFieldNullValue,
1889                                                     overlappingPolygonDistance=overlappingPolygonDistance,
1890                                                     wherePointFeatures=where1,
1891                                                     whereNearFeatures=where2,
1892                                                     nearestFieldsToCopy=nearestFieldsToCopy,
1893                                                     destinationPointFields=destinationPointFields,
1894                                                     destinationPointFieldsNullValues=destinationPointFieldsNullValues)
1895                        finally:
1896                            Logger.SetLogInfoAsDebug(oldLogInfoAsDebug)
1897
1898                        # Report our progress.
1899
1900                        progressReporter.ReportProgress()
1901
1902                # Delete the temp geodatabase. If we do not explicitly
1903                # delete it here, the geoprocessor will keep a handle
1904                # open through MS Access APIs, and the temporary
1905                # directory will not be able to be deleted.
1906
1907                finally:
1908                    try:
1909                        pass
1910                        gp.Delete_management(tempGeoDB)
1911                    except:
1912                        pass
1913
1914            # Delete the temporary fields that
1915            # _ValidateAndCreatePointFields created in the caller's
1916            # points.
1917
1918            finally:
1919                from GeoEco.DatabaseAccess.ArcGIS import ArcGIS91DatabaseConnection
1920                conn = ArcGIS91DatabaseConnection()
1921                try:
1922                    conn.DeleteField(pointFeatures, tempJoinFIDField)       # DeleteField succeeds silently if the field does not exist.
1923                except:
1924                    pass
1925                if fidField is None:
1926                    try:
1927                        conn.DeleteField(pointFeatures, tempNearFIDField)
1928                    except:
1929                        pass
1930
1931        except:
1932            Logger.LogExceptionAsError()
1933            raise
1934
1935        # Return successfully.
1936
1937        return pointFeatures
1938
1939
1940###############################################################################
1941# Metadata: module
1942###############################################################################
1943
1944from GeoEco.ArcGIS import ArcGISDependency, ArcGISProductDependency
1945from GeoEco.DatabaseAccess import InsertCursor
1946from GeoEco.Metadata import *
1947from GeoEco.Types import *
1948
1949AddModuleMetadata(shortDescription=_(u'Provides methods for general-purpose operations with points.'))
1950
1951###############################################################################
1952# Metadata: ArcGISPoints class
1953###############################################################################
1954
1955AddClassMetadata(ArcGISPoints,
1956    shortDescription=_(u'Provides methods for general-purpose operations with ArcGIS points.'),
1957    isExposedAsCOMServer=True,
1958    comIID=u'{AB7A5328-0568-448B-9F7B-1E969F9F4241}',
1959    comCLSID=u'{E640B02D-4A18-480F-A458-543AA360E153}')
1960
1961# Public method: ArcGISPoints.InsertPoint
1962
1963AddMethodMetadata(ArcGISPoints.InsertPoint,
1964    shortDescription=_(u'Inserts a point with an insert cursor opened to an ArcGIS point feature class.'),
1965    isExposedToPythonCallers=True,
1966    dependencies=[ArcGISDependency(9, 1)])
1967
1968AddArgumentMetadata(ArcGISPoints.InsertPoint, u'cls',
1969    typeMetadata=ClassOrClassInstanceTypeMetadata(cls=ArcGISPoints),
1970    description=_(u'%s class or an instance of it.') % ArcGISPoints.__name__)
1971
1972AddArgumentMetadata(ArcGISPoints.InsertPoint, u'insertCursor',
1973    typeMetadata=ClassInstanceTypeMetadata(cls=InsertCursor),
1974    description=_(
1975u"""Insert cursor opened to the feature class that will receive the
1976point. The cursor will still be open when this function returns."""))
1977
1978AddArgumentMetadata(ArcGISPoints.InsertPoint, u'x',
1979    typeMetadata=FloatTypeMetadata(),
1980    description=_(u"""X coordinate of the point."""))
1981
1982AddArgumentMetadata(ArcGISPoints.InsertPoint, u'y',
1983    typeMetadata=FloatTypeMetadata(),
1984    description=_(u"""Y coordinate of the point."""))
1985
1986AddArgumentMetadata(ArcGISPoints.InsertPoint, u'z',
1987    typeMetadata=FloatTypeMetadata(canBeNone=True),
1988    description=_(
1989u"""Z coordinate of the point. Only provide this if the feature class
1990has Z coordinates."""))
1991
1992AddArgumentMetadata(ArcGISPoints.InsertPoint, u'm',
1993    typeMetadata=FloatTypeMetadata(canBeNone=True),
1994    description=_(
1995u"""Meaure value of the point. Only provide this if the feature class
1996has measure values."""))
1997
1998AddArgumentMetadata(ArcGISPoints.InsertPoint, u'attributes',
1999    typeMetadata=DictionaryTypeMetadata(keyType=UnicodeStringTypeMetadata(), valueType=AnyObjectTypeMetadata(), canBeNone=True),
2000    description=_(
2001u"""Dictionary of polygon attributes (a.k.a. fields) to set before
2002inserting the point. The dictionary's keys and values are the
2003attribute's names and values."""))
2004
2005AddArgumentMetadata(ArcGISPoints.InsertPoint, u'shapeFieldName',
2006    typeMetadata=UnicodeStringTypeMetadata(),
2007    description=_(
2008u"""Name of the shape field (the geometry field) for the cursor. This
2009may be obtained from the ShapeFieldName property of object returned by
2010the geoprocessor's Describe method."""))
2011
2012# Public method: ArcGISPoints.AppendPointsToFeatureClass
2013
2014AddMethodMetadata(ArcGISPoints.AppendPointsToFeatureClass,
2015    shortDescription=_(u'Appends points to an existing ArcGIS point feature class.'),
2016    isExposedToPythonCallers=True,
2017    dependencies=[ArcGISDependency(9, 1)])
2018
2019CopyArgumentMetadata(ArcGISPoints.InsertPoint, u'cls', ArcGISPoints.AppendPointsToFeatureClass, u'cls')
2020
2021AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass, u'featureClass',
2022    typeMetadata=ArcGISFeatureClassTypeMetadata(allowedShapeTypes=[u'Point'], mustExist=True),
2023    description=_(u"""Feature class that will receive the points."""),
2024    arcGISDisplayName=_(u'Point feature class'))
2025
2026AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass, u'coordinates',
2027    typeMetadata=ListTypeMetadata(elementType=FloatTypeMetadata(), minLength=2, canBeNone=True),
2028    description=_(
2029u"""List of coordinates for the points to insert. If the points do not
2030have Z coordinates nor M values, the list must be of the form::
2031
2032    [x1, y1, x2, y2, x3, y3, ... ]
2033
2034If the points have X, Y and Z coordinates but not M values, the list
2035must be of the form::
2036
2037    [x1, y1, z1, x2, y2, z2, x3, y3, z3, ... ]
2038
2039If the points have X and Y coordinates and M values but no Z
2040coordinates, the list must be of the form::
2041
2042    [x1, y1, m1, x2, y2, m2, x3, y3, m3, ... ]
2043
2044If the points have X, Y and Z coordinates and M values, the list must
2045be of the form::
2046
2047    [x1, y1, z1, m1, x2, y2, z2, m2, x3, y3, z3, m3, ... ]
2048"""))
2049
2050AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass, u'hasZ',
2051    typeMetadata=BooleanTypeMetadata(),
2052    description=_(
2053u"""Indicates whether or not the input list of coordinates contains Z
2054coordinates. If it does, you must pass True for this parameter."""))
2055
2056AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass, u'hasM',
2057    typeMetadata=BooleanTypeMetadata(),
2058    description=_(
2059u"""Indicates whether or not the input list of coordinates contains
2060measure values. If it does, you must pass True for this
2061parameter."""))
2062
2063AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass, u'attributes',
2064    typeMetadata=ListTypeMetadata(elementType=DictionaryTypeMetadata(keyType=UnicodeStringTypeMetadata(), valueType=AnyObjectTypeMetadata(), canBeNone=True), canBeNone=True),
2065    description=_(
2066u"""List of dictionaries that parallels the list of coordinates. Each
2067dictionary defines the attributes (a.k.a. fields) to set for a point
2068before it is inserted. The dictionary's keys and values are the
2069attribute's names and values."""))
2070
2071# Public method: ArcGISPoints.AppendPointsToFeatureClass2
2072
2073AddMethodMetadata(ArcGISPoints.AppendPointsToFeatureClass2,
2074    shortDescription=_(u'Appends points to an existing ArcGIS point feature class.'),
2075    isExposedToPythonCallers=True,
2076    isExposedByCOM=True,
2077    isExposedAsArcGISTool=True,
2078    arcGISDisplayName=_(u'Append Points'),
2079    arcGISToolCategory=_(u'Spatial and Temporal Analysis\\Create Points'),
2080    dependencies=[ArcGISDependency(9, 1)])
2081
2082CopyArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass, u'cls', ArcGISPoints.AppendPointsToFeatureClass2, u'cls')
2083CopyArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass, u'featureClass', ArcGISPoints.AppendPointsToFeatureClass2, u'featureClass')
2084
2085AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'xyCoordinates',
2086    typeMetadata=ListTypeMetadata(elementType=PointTypeMetadata(), minLength=1),
2087    description=_(
2088u"""X and Y coordinates of the points to create.
2089
2090If you are invoking this tool programmatically, you must specify each
2091point as a string, as must be done when invoking an ArcGIS tool that
2092accepts a point as input. For example, to create three points when
2093invoking this tool from Python, you use the list::
2094
2095    [u'1 2', u'3 4', u'5 6']
2096"""),
2097    arcGISDisplayName=_(u'Points'))
2098
2099AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'zCoordinates',
2100    typeMetadata=ListTypeMetadata(elementType=FloatTypeMetadata(), canBeNone=True, mustBeSameLengthAsArgument=u'xyCoordinates'),
2101    description=_(
2102u"""Z coordinates of the points to create.
2103
2104If you are invoking this tool programmatically, specify these
2105coordinates as floating point numbers, not strings as is done with the
2106xyCoordinates parameter."""),
2107    arcGISDisplayName=_(u'Z coordinates'),
2108    arcGISCategory=_(u'Point options'))
2109
2110
2111AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'mValues',
2112    typeMetadata=ListTypeMetadata(elementType=FloatTypeMetadata(), canBeNone=True, mustBeSameLengthAsArgument=u'xyCoordinates'),
2113    description=_(
2114u"""Measure values of the points to create.
2115
2116If you are invoking this tool programmatically, specify these values
2117as floating point numbers, not strings as is done with the
2118xyCoordinates parameter."""),
2119    arcGISDisplayName=_(u'M values'),
2120    arcGISCategory=_(u'Point options'))
2121
2122AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'intAttrsToSet',
2123    typeMetadata=ListTypeMetadata(elementType=ArcGISFieldTypeMetadata(mustExist=True, allowedFieldTypes=[u'SHORT', u'LONG']), canBeNone=True),
2124    description=_(
2125u"""LONG and SHORT integer attributes of the points that should be set
2126to the values you specify. This parameter specifies the names of the
2127attributes to set and the Integer Attribute Values parameter specifies
2128their values. If you create multiple points, they will all receive the
2129same attribute values.
2130
2131This parameter is optional. If you do not set a particular attribute,
2132ArcGIS determines its default value. For shapefiles, it will be 0. For
2133geodatabases, it will be the default value specified by the database
2134schema, or NULL if the field is nullable and no default is
2135specified."""),
2136    arcGISDisplayName=_(u'Integer attributes to set'),
2137    arcGISCategory=_(u'Point options'),
2138    arcGISParameterDependencies=[u'featureClass'])
2139
2140AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'intAttrValues',
2141    typeMetadata=ListTypeMetadata(elementType=IntegerTypeMetadata(), canBeNone=True, mustBeSameLengthAsArgument=u'intAttrsToSet'),
2142    description=_(
2143u"""Values for the LONG and SHORT integer attributes to set for the
2144points. You must provide a value for each attribute you specified for
2145the Integer Attributes to Set parameter."""),
2146    arcGISDisplayName=_(u'Integer attribute values'),
2147    arcGISCategory=_(u'Point options'))
2148
2149AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'floatAttrsToSet',
2150    typeMetadata=ListTypeMetadata(elementType=ArcGISFieldTypeMetadata(mustExist=True, allowedFieldTypes=[u'FLOAT', u'DOUBLE']), canBeNone=True),
2151    description=_(
2152u"""FLOAT and DOUBLE attributes of the points that should be set to
2153the values you specify. This parameter specifies the names of the
2154parameters to set and the Floating Point Attribute Values parameter
2155specifies their values. If you create multiple points, they will all
2156receive the same attribute values.
2157
2158This parameter is optional. If you do not set a particular attribute,
2159ArcGIS determines its default value. For shapefiles, it will be 0. For
2160geodatabases, it will be the default value specified by the database
2161schema, or NULL if the field is nullable and no default is
2162specified."""),
2163    arcGISDisplayName=_(u'Floating point attributes to set'),
2164    arcGISCategory=_(u'Point options'),
2165    arcGISParameterDependencies=[u'featureClass'])
2166
2167AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'floatAttrValues',
2168    typeMetadata=ListTypeMetadata(elementType=FloatTypeMetadata(), canBeNone=True, mustBeSameLengthAsArgument=u'floatAttrsToSet'),
2169    description=_(
2170u"""Values for the FLOAT and DOUBLE attributes to set for the points.
2171You must provide a value for each attribute you specified for the
2172Floating Point Attributes to Set parameter."""),
2173    arcGISDisplayName=_(u'Floating point attribute values'),
2174    arcGISCategory=_(u'Point options'))
2175
2176AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'stringAttrsToSet',
2177    typeMetadata=ListTypeMetadata(elementType=ArcGISFieldTypeMetadata(mustExist=True, allowedFieldTypes=[u'TEXT']), canBeNone=True),
2178    description=_(
2179u"""TEXT attributes of the points that should be set to the values you
2180specify. This parameter specifies the names of the parameters to set
2181and the Text Attribute Values parameter specifies their values. If you
2182create multiple points, they will all receive the same attribute
2183values.
2184
2185This parameter is optional. If you do not set a particular attribute,
2186ArcGIS determines its default value. For shapefiles, it will be a
2187zero-length string. For geodatabases, it will be the default value
2188specified by the database schema, or NULL if the field is nullable and
2189no default is specified."""),
2190    arcGISDisplayName=_(u'Text attributes to set'),
2191    arcGISCategory=_(u'Point options'),
2192    arcGISParameterDependencies=[u'featureClass'])
2193
2194AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'stringAttrValues',
2195    typeMetadata=ListTypeMetadata(elementType=UnicodeStringTypeMetadata(), canBeNone=True, mustBeSameLengthAsArgument=u'stringAttrsToSet'),
2196    description=_(
2197u"""Values for the TEXT attributes to set for the points. You must
2198provide a value for each attribute you specified for the Text
2199Attributes to Set parameter."""),
2200    arcGISDisplayName=_(u'Text attribute values'),
2201    arcGISCategory=_(u'Point options'))
2202
2203AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'dateAttrsToSet',
2204    typeMetadata=ListTypeMetadata(elementType=ArcGISFieldTypeMetadata(mustExist=True, allowedFieldTypes=[u'DATE']), canBeNone=True),
2205    description=_(
2206u"""DATE attributes of the points that should be set to the values you
2207specify. This parameter specifies the names of the parameters to set
2208and the Date Attribute Values parameter specifies their values. If you
2209create multiple points, they will all receive the same attribute
2210values.
2211
2212This parameter is optional. If you do not set a particular attribute,
2213ArcGIS determines its default value. For shapefiles, it will be NULL.
2214For geodatabases, it will be the default value specified by the
2215database schema, or NULL if the field is nullable and no default is
2216specified."""),
2217    arcGISDisplayName=_(u'Date attributes to set'),
2218    arcGISCategory=_(u'Point options'),
2219    arcGISParameterDependencies=[u'featureClass'])
2220
2221AddArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'dateAttrValues',
2222    typeMetadata=ListTypeMetadata(elementType=DateTimeTypeMetadata(), canBeNone=True, mustBeSameLengthAsArgument=u'dateAttrsToSet'),
2223    description=_(
2224u"""Values for the DATE attributes to set for the points. You must
2225provide a value for each attribute you specified for the Date
2226Attributes to Set parameter. Your date may include a time if the
2227underlying feature class supports it."""),
2228    arcGISDisplayName=_(u'Date attribute values'),
2229    arcGISCategory=_(u'Point options'),
2230    arcGISParameterDependencies=[u'featureClass'])
2231
2232AddResultMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'updatedFeatureClass',
2233    typeMetadata=ArcGISFeatureClassTypeMetadata(),
2234    description=_(u"""Updated feature class containing the new points."""),
2235    arcGISDisplayName=_(u'Updated feature class'),
2236    arcGISParameterDependencies=[u'featureClass'])
2237
2238# Public method: ArcGISPoints.CreateFeatureClassWithPoints
2239
2240AddMethodMetadata(ArcGISPoints.CreateFeatureClassWithPoints,
2241    shortDescription=_(u'Creates points in a new ArcGIS point feature class.'),
2242    isExposedToPythonCallers=True,
2243    dependencies=[ArcGISDependency(9, 1)])
2244
2245CopyArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass, u'cls', ArcGISPoints.CreateFeatureClassWithPoints, u'cls')
2246
2247AddArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'featureClass',
2248    typeMetadata=ArcGISFeatureClassTypeMetadata(deleteIfParameterIsTrue=u'overwriteExisting', createParentDirectories=True),
2249    description=_(u"""Output point feature class to create."""),
2250    direction = u'Output',
2251    arcGISDisplayName=_(u'Output point feature class'))
2252
2253AddArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'spatialReference',
2254    typeMetadata=SpatialReferenceTypeMetadata(),
2255    description=_(u'Spatial reference for the output feature class.'),
2256    arcGISDisplayName=_(u'Spatial reference'))
2257
2258CopyArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass, u'coordinates', ArcGISPoints.CreateFeatureClassWithPoints, u'coordinates')
2259CopyArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass, u'hasZ', ArcGISPoints.CreateFeatureClassWithPoints, u'hasZ')
2260CopyArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass, u'hasM', ArcGISPoints.CreateFeatureClassWithPoints, u'hasM')
2261CopyArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass, u'attributes', ArcGISPoints.CreateFeatureClassWithPoints, u'attributes')
2262
2263AddArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'configKeyword',
2264    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
2265    description=_(
2266u"""The configuration keyword that determines the storage parameters
2267of the table in a Relational Database Management System
2268(RDBMS)--ArcSDE only."""),
2269    arcGISDisplayName=_(u'Configuration keyword'),
2270    arcGISCategory=_(u'Geodatabase settings for output feature class'))
2271
2272AddArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'spatialGrid1',
2273    typeMetadata=IntegerTypeMetadata(canBeNone=True, minValue=0),
2274    description=_(
2275u"""The size of the output feature class's spatial grid index. The
2276following formats support spatial grid index grids: personal
2277geodatabase, file geodatabase or ArcSDE geodatabase. If not provided,
2278or if the value 0 is provided, a valid grid size will be calculated
2279automatically by ArcGIS."""),
2280    arcGISDisplayName=_(u'Output spatial grid 1'),
2281    arcGISCategory=_(u'Geodatabase settings for output feature class'))
2282
2283AddArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'spatialGrid2',
2284    typeMetadata=IntegerTypeMetadata(canBeNone=True, minValue=0),
2285    description=_(
2286u"""The size of the output feature class's second spatial grid index.
2287The value must be at least three times larger than the first index
2288grid. The following formats support spatial grid index grids: personal
2289geodatabase, file geodatabase or ArcSDE geodatabase. Note that
2290personal geodatabases support only one spatial index grid."""),
2291    arcGISDisplayName=_(u'Output spatial grid 2'),
2292    arcGISCategory=_(u'Geodatabase settings for output feature class'))
2293
2294AddArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'spatialGrid3',
2295    typeMetadata=IntegerTypeMetadata(canBeNone=True, minValue=0),
2296    description=_(
2297u"""The size of the output feature class's second spatial grid index.
2298The value must be at least three times larger than the second index
2299grid. The following formats support spatial grid index grids: personal
2300geodatabase, file geodatabase or ArcSDE geodatabase. Note that
2301personal geodatabases support only one spatial index grid."""),
2302    arcGISDisplayName=_(u'Output spatial grid 3'),
2303    arcGISCategory=_(u'Geodatabase settings for output feature class'))
2304
2305AddArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'overwriteExisting',
2306    typeMetadata=BooleanTypeMetadata(),
2307    description=_(
2308u"""If True, the output feature class will be overwritten, if it
2309exists. If False, a ValueError will be raised if the output feature
2310class exists."""),
2311    initializeToArcGISGeoprocessorVariable=u'OverwriteOutput')
2312
2313# Public method: ArcGISPoints.CreateFeatureClassWithPoints2
2314
2315AddMethodMetadata(ArcGISPoints.CreateFeatureClassWithPoints2,
2316    shortDescription=_(u'Creates points in a new ArcGIS point feature class.'),
2317    isExposedToPythonCallers=True,
2318    isExposedByCOM=True,
2319    isExposedAsArcGISTool=True,
2320    arcGISDisplayName=_(u'Create Points'),
2321    arcGISToolCategory=_(u'Spatial and Temporal Analysis\\Create Points'),
2322    dependencies=[ArcGISDependency(9, 1)])
2323
2324CopyArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'cls', ArcGISPoints.CreateFeatureClassWithPoints2, u'cls')
2325CopyArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'featureClass', ArcGISPoints.CreateFeatureClassWithPoints2, u'featureClass')
2326CopyArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'spatialReference', ArcGISPoints.CreateFeatureClassWithPoints2, u'spatialReference')
2327CopyArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'xyCoordinates', ArcGISPoints.CreateFeatureClassWithPoints2, u'xyCoordinates')
2328CopyArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'zCoordinates', ArcGISPoints.CreateFeatureClassWithPoints2, u'zCoordinates')
2329CopyArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass2, u'mValues', ArcGISPoints.CreateFeatureClassWithPoints2, u'mValues')
2330CopyArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'configKeyword', ArcGISPoints.CreateFeatureClassWithPoints2, u'configKeyword')
2331CopyArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'spatialGrid1', ArcGISPoints.CreateFeatureClassWithPoints2, u'spatialGrid1')
2332CopyArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'spatialGrid2', ArcGISPoints.CreateFeatureClassWithPoints2, u'spatialGrid2')
2333CopyArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'spatialGrid3', ArcGISPoints.CreateFeatureClassWithPoints2, u'spatialGrid3')
2334CopyArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'overwriteExisting', ArcGISPoints.CreateFeatureClassWithPoints2, u'overwriteExisting')
2335
2336# Public method: ArcGISPoints.CreatePointsAlongLines
2337
2338AddMethodMetadata(ArcGISPoints.CreatePointsAlongLines,
2339    shortDescription=_(u'Creates points along lines at a specified interval.'),
2340    isExposedToPythonCallers=True,
2341    isExposedByCOM=True,
2342    isExposedAsArcGISTool=True,
2343    arcGISDisplayName=_(u'Create Points Along Lines'),
2344    arcGISToolCategory=_(u'Spatial and Temporal Analysis\\Create Points'),
2345    dependencies=[ArcGISDependency(9, 2)])
2346
2347CopyArgumentMetadata(ArcGISPoints.AppendPointsToFeatureClass, u'cls', ArcGISPoints.CreatePointsAlongLines, u'cls')
2348
2349AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'inputLines',
2350    typeMetadata=ArcGISFeatureClassTypeMetadata(allowedShapeTypes=[u'Polyline'], mustExist=True),
2351    description=_(
2352u"""Lines to generate points along.
2353
2354By default, the tool assumes that all of the lines in this layer form
2355a single track (e.g. the path traveled by a single animal or vessel).
2356If this layer contains multiple tracks, set the Track ID Field
2357parameter to the field that identifies the track.
2358
2359By default, when generating points along the lines, the tool orders
2360the lines by the FID or OBJECTID field in ascending order. If another
2361field should be used, set the Order By Field parameter to that
2362field."""),
2363    arcGISDisplayName=_(u'Input lines'))
2364
2365AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'outputFeatureClass',
2366    typeMetadata=ArcGISFeatureClassTypeMetadata(deleteIfParameterIsTrue=u'overwriteExisting', createParentDirectories=True, mustBeDifferentThanArguments=[u'inputLines']),
2367    description=_(
2368u"""Output point feature class to create.
2369
2370By default, the tool will create a field named Distance on the feature
2371class and populate it with the total distance traveled from the start
2372of the track. The field name is specified by the Distance Traveled
2373Field To Create parameter (under Additional Options). If a temporal
2374unit is specified for the Interval Unit parameter, the distance
2375traveled will be in the units of the underlying coordinate system:
2376either decimal degrees for angular (a.k.a. geographic) coordinate
2377systems, or the linear unit (typically meters) for projected
2378coordinate systems. If a non-temporal unit is specified, the distance
2379traveled will be in that unit.
2380
2381Depending on what you specify for other parameters of the tool,
2382various additional fields will be created and populated."""),
2383    direction = u'Output',
2384    arcGISDisplayName=_(u'Output point feature class'))
2385
2386AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'interval',
2387    typeMetadata=FloatTypeMetadata(mustBeGreaterThan=0.),
2388    description=_(
2389u"""Interval for generating points. This must be a number greater than
2390zero. You must also specify the interval unit."""),
2391    arcGISDisplayName=_(u'Interval'))
2392
2393AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'unit',
2394    typeMetadata=UnicodeStringTypeMetadata(makeLowercase=True, allowedValues=[u'Days', u'Decimal degrees', u'Feet', u'Kilometers', u'Hours', u'Meters', u'Miles', u'Minutes', u'Nautical miles', u'Seconds', u'Yards']),
2395    description=_(
2396u"""Unit of the interval. The unit may be angular (i.e. decimal
2397degrees), linear (e.g. meters), or temporal (e.g. hours).
2398
2399If an angular unit is used, the input lines must be in a geographic
2400(unprojected) coordinate system. Points will be placed exactly along
2401the lines at the specified interval.
2402
2403If a linear unit is used, the input lines may be in a projected or
2404geographic coordinate system. If they are in a projected coordinate
2405system, points will be placed exactly along the lines at the specified
2406interval. If the lines are in a geographic coordinate system, points
2407will be placed along the great circles that connect the lines'
2408vertices and spaced according to haversine distance. Because of this,
2409the points will not occur exactly along the lines unless the lines are
2410themselves great circles (i.e. they exactly follow the equator or a
2411meridian).
2412
2413If a temporal unit is used, the input lines may be in a projected or
2414geographic coordinate system. Points will be placed exactly along the
2415lines. The lines are assumed to be tracks traversed by an object
2416moving at constant velocity. You must also specify fields for the
2417start time and end time of the line. The rate of travel will be
2418calculated for each line by dividing the line length (angular units
2419for geographic coordinate systems, linear units for projected systems)
2420by the duration of the line (the difference between the end time and
2421start time). A spatial interval will be computed from this rate by
2422multiplying the rate by the temporal interval, and points will be
2423placed along the line at the spatial interval.
2424
2425The spatial interval will be angular for geographic coordinate systems
2426and linear for projected systems. Therefore, if the object was moving
2427at a constant velocity over the surface of the Earth, the points will
2428be placed more realistically if a projected coordinate system is used.
2429
2430If the start and end time fields can only represent dates with no time
2431component (e.g. the lines are in a shapefile), then the time component
2432of these fields is assumed to be 00:00:00. If a time component is
2433stored in a separate field (e.g. text such as '12:34:56'), you should
2434import the lines into a geodatabase, create a new field that will hold
2435both date and time components (use the DATE data type), and use the
2436Calculate Field tool to populate the new field from both components.
2437
2438If the start time and end time are the same, the tool will fail
2439because it will not be able to calculate a rate of travel."""),
2440    arcGISDisplayName=_(u'Interval unit'))
2441
2442AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'trackIDField',
2443    typeMetadata=ArcGISFieldTypeMetadata(mustExist=True, canBeNone=True),
2444    description=_(
2445u"""Field of the lines that represents track identifier when multiple
2446tracks are stored in the input line layer.
2447
2448The tool uses this field to determine when it needs to restart
2449distance calculations when multiple tracks are contained in the input
2450line layer.
2451
2452If all of the lines in the input layer are part of a single track
2453(e.g. the path traveled by a single animal or vessel), do not provide
2454a value for this parameter. This is the default.
2455
2456If each line represents a different track, provide the field that
2457identifies the lines. If your dataset does not have an ID field
2458already, use the FID or OBJECTID field.
2459
2460If you do provide a field, it will be copied to the output points. If
2461you use the FID or OBJECTID field, the corresponding field of the
2462points will be named FID\\_ or OBJECTID\\_, to avoid conflicting with the
2463FID or OBJECTID of the points themselves."""),
2464    arcGISDisplayName=_(u'Track ID field'),
2465    arcGISParameterDependencies=[u'inputLines'])
2466
2467AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'orderByField',
2468    typeMetadata=ArcGISFieldTypeMetadata(mustExist=True, canBeNone=True),
2469    description=_(
2470u"""Field of the lines for ordering them in sequence.
2471
2472When the tool lays out points along the lines, it will first order
2473them in ascending order using this field. If this field is omitted,
2474the FID or OBJECTID will be used.
2475
2476If you do provide a field, it will be copied to the output points. If
2477you explicitly use the FID or OBJECTID field, the corresponding field
2478of the points will be named FID\\_ or OBJECTID\\_, to avoid
2479conflicting with the FID or OBJECTID of the points themselves."""),
2480    arcGISDisplayName=_(u'Order by field'),
2481    arcGISParameterDependencies=[u'inputLines'])
2482
2483AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'startTimeField',
2484    typeMetadata=ArcGISFieldTypeMetadata(mustExist=True, allowedFieldTypes=[u'DATE'], canBeNone=True),
2485    description=_(
2486u"""Field of the lines that represents the date and time that the line
2487started.
2488
2489This parameter must be specified if the Interval Unit is set to a
2490temporal unit (i.e. Day, Hour, Minute, or Second). This parameter is
2491optional when a non-temporal unit is used.
2492
2493By default, when this parameter and the End Time Field parameter are
2494both specified, two additional fields will be created on the output points:
2495
2496* PointTime - time of the point.
2497
2498* Elapsed - time elapsed since the start of the track, in hours by
2499  default.
2500
2501The names of these fields and the units of the Elapsed field are
2502specified by other parameters (see Additional Options)."""),
2503    arcGISDisplayName=_(u'Start time field'),
2504    arcGISParameterDependencies=[u'inputLines'])
2505
2506AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'endTimeField',
2507    typeMetadata=ArcGISFieldTypeMetadata(mustExist=True, allowedFieldTypes=[u'DATE'], canBeNone=True, mustBeDifferentThanArguments=[u'startTimeField']),
2508    description=_(
2509u"""Field of the lines that represents the date and time that the line
2510ended.
2511
2512This parameter must be specified if the Interval Unit is set to a
2513temporal unit (i.e. Day, Hour, Minute, or Second). This parameter is
2514optional when a non-temporal unit is used."""),
2515    arcGISDisplayName=_(u'End time field'),
2516    arcGISParameterDependencies=[u'inputLines'])
2517
2518AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'pointsAtLineEnds',
2519    typeMetadata=BooleanTypeMetadata(),
2520    description=_(
2521u"""This option determines whether points should be generated at the
2522ends of tracks or offset from the ends of them.
2523
2524If disabled, the default, the first point generated on the track will
2525be offset from the first vertex of the first line of the track by
2526one-half of the point interval you specified. Successive points will
2527be placed along the track at that interval until the remaining track
2528is shorter than the interval. If the total length of the track is not
2529evenly divisible by the interval, the distance between the last point
2530and the ending vertex of the last line in the track will be less than
2531one-half of the interval. This behavior can be overridden by enabling
2532the Distribute Remaining Distance Among All Points option, in which
2533case the remainder distance will be equally distributed between all
2534points, making them all slightly closer together (but equally spaced),
2535and the distance between the first vertex of the track and the first
2536point equal to the distance between the last vertex and the last
2537point.
2538
2539If enabled, the first point on the track will be placed at the first
2540vertex of the first line of the track. Successive points will be
2541placed along the track at the point interval until the remaining track
2542is shorter than the interval. Then the final point will be placed at
2543the last vertex of the last line of the track. If the total length of
2544the track is not evenly divisible by the interval, the distance
2545between the next-to-last point and the last point (at the ending
2546vertex of the track) will be less than the interval. This behavior can
2547be overridden by enabling the Distribute Remaining Distance Among All
2548Points option, in which case the remainder distance will be equally
2549distributed between all points, making them all slightly closer
2550together (but equally spaced)."""),
2551    arcGISDisplayName=_(u'Place points at ends of tracks'),
2552    arcGISCategory=_(u'Additional options'))
2553
2554AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'distributeRemainder',
2555    typeMetadata=BooleanTypeMetadata(),
2556    description=_(
2557u"""Determines how points are distributed on tracks that are not
2558evenly divisible by the point interval. Please see the documentation
2559for the Place Points At Ends Of Tracks parameter for more
2560information."""),
2561    arcGISDisplayName=_(u'Distribute remaining distance among all points'),
2562    arcGISCategory=_(u'Additional options'))
2563
2564AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'where',
2565    typeMetadata=SQLWhereClauseTypeMetadata(canBeNone=True),
2566    description=_(
2567u"""SQL WHERE clause expression that specifies the subset of the lines
2568to to use. If this parameter is not provided, all of the lines will be
2569used.
2570
2571The exact syntax of this expression depends on the type of feature
2572class that contains the near features. ESRI recommends you reference
2573fields using the following syntax:
2574
2575* If the lines are stored in an ArcInfo coverage or a shapefile,
2576  enclose field names in double quotes in the SQL expression:
2577  "MY_FIELD".
2578
2579* If the lines are stored in a personal geodatabase (.mdb file),
2580  enclose field names in square brackets: [MY_FIELD].
2581
2582* If the points are stored in an ArcSDE or ArcIMS feature
2583  class, don't enclose field names: MY_FIELD."""),
2584    arcGISDisplayName=_(u'Where clause'),
2585    arcGISCategory=_(u'Additional options'),
2586    arcGISParameterDependencies=[u'inputLines'])
2587
2588AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'fieldsToCopy',
2589    typeMetadata=ListTypeMetadata(elementType=ArcGISFieldTypeMetadata(mustExist=True), minLength=1, canBeNone=True),
2590    description=_(
2591u"""Fields of the lines to copy to the points generated in the output
2592feature class."""),
2593    arcGISDisplayName=_(u'Line fields to copy to the points'),
2594    arcGISCategory=_(u'Additional options'),
2595    arcGISParameterDependencies=[u'inputLines'])
2596
2597AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'distanceField',
2598    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
2599    description=_(
2600u"""Point field to receive the total distance traveled from the start
2601of the track.
2602
2603If a temporal unit is specified for the Interval Unit parameter, the
2604distance traveled will be in the units of the underlying coordinate
2605system: either decimal degrees for angular (a.k.a. geographic)
2606coordinate systems, or the linear unit (typically meters) for
2607projected coordinate systems. If a non-temporal unit is specified, the
2608distance traveled will be in that unit.
2609
2610The field will be created with the double-precision floating point
2611data type."""),
2612    arcGISDisplayName=_(u'Distance traveled field to create'),
2613    arcGISCategory=_(u'Additional options'))
2614
2615AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'pointTimeField',
2616    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
2617    description=_(
2618u"""Point field to receive the time of the point.
2619
2620If either the Start Time Field or End Time Field parameters are not
2621specified, this parameter is ignored and no field will be
2622created."""),
2623    arcGISDisplayName=_(u'Time field to create'),
2624    arcGISCategory=_(u'Additional options'))
2625
2626AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'elapsedField',
2627    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
2628    description=_(
2629u"""Point field to receive the elapsed time since the start of the
2630track.
2631
2632If either the Start Time Field or End Time Field parameters are not
2633specified, this parameter is ignored and no field will be
2634created.
2635
2636The Elapsed Time Units parameter specifies the units of this
2637parameter.
2638
2639The field will be created with the double-precision floating point
2640data type."""),
2641    arcGISDisplayName=_(u'Elapsed time field to create'),
2642    arcGISCategory=_(u'Additional options'))
2643
2644AddArgumentMetadata(ArcGISPoints.CreatePointsAlongLines, u'elapsedUnit',
2645    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True, makeLowercase=True, allowedValues=[u'Days', u'Hours', u'Minutes', u'Seconds']),
2646    description=_(
2647u"""Unit of the Elapsed Time Field."""),
2648    arcGISDisplayName=_(u'Elapsed time unit'),
2649    arcGISCategory=_(u'Additional options'))
2650
2651CopyArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'overwriteExisting', ArcGISPoints.CreatePointsAlongLines, u'overwriteExisting')
2652
2653# Public method: ArcGISPoints.FindNearestFeatures
2654
2655AddMethodMetadata(ArcGISPoints.FindNearestFeatures,
2656    shortDescription=_(u'For each point, finds the nearest feature and writes its ID, distance, angle, and/or values of specified fields to fields of the point.'),
2657    isExposedToPythonCallers=True,
2658    isExposedByCOM=True,
2659    isExposedAsArcGISTool=True,
2660    arcGISDisplayName=_(u'Find Nearest Features'),
2661    arcGISToolCategory=_(u'Spatial and Temporal Analysis\\Find Features Nearest to Points'),
2662    dependencies=[ArcGISDependency(9, 1), ArcGISProductDependency(u'ArcInfo')])
2663
2664CopyArgumentMetadata(ArcGISPoints.CreateFeatureClassWithPoints, u'cls', ArcGISPoints.FindNearestFeatures, u'cls')
2665
2666AddArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'pointFeatures',
2667    typeMetadata=ArcGISFeatureClassTypeMetadata(allowedShapeTypes=[u'Point'], mustExist=True),
2668    description=_(
2669u"""Feature class containing points for which the nearest point, line,
2670or polygon feature should be found.
2671
2672You must have an ArcInfo product license to use this tool. This tool
2673finds the nearest feature using the ArcGIS Near tool, which requires
2674ArcInfo.
2675
2676Distances between features are calculated using the Pythagorean
2677theorem. This is known as Euclidean distance. The nearest feature is
2678the one with the smallest Euclidean distance.
2679
2680Either the point features or the near features must be in a projected
2681coordinate system, so that Euclidean distance may be properly
2682calculated. If neither is in a projected coordinate system, an error
2683will be reported. If just one is in a projected coordinate system, the
2684other will be automatically projected to that system prior to
2685performing distance calculations. If they are in two different
2686projected coordinate systems, the points will be automatically
2687projected to the near feature's coordinate system. An error will be
2688reported if this is not possible without performing a geographic
2689transformation (e.g. the features use different datums). In this case,
2690you must manually project the points or the near features yourself,
2691prior to running this tool."""),
2692    arcGISDisplayName=_(u'Point features'))
2693
2694AddArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'nearFeatures',
2695    typeMetadata=ArcGISFeatureClassTypeMetadata(allowedShapeTypes=[u'Point', u'Polyline', u'Polygon'], mustExist=True),         # Do not change this to ArcGISFeatureLayerTypeMetadata
2696    description=_(
2697u"""Feature class containing points, lines, or polygons.
2698
2699This tool is not designed to operate on overlapping near features. If
2700the near features overlap, the behavior of this tool is undefined. It
2701may report a strange error, or it may appear to run successfully but
2702the results may not be correct. Please check the results carefully
2703when using overlapping near features.
2704
2705It is ok if the points overlap the near features."""),
2706    arcGISDisplayName=_(u'Near features'))
2707
2708AddArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'searchRadius',
2709    typeMetadata=LinearUnitTypeMetadata(canBeNone=True),
2710    description=_(
2711u"""The maximum distance from a point to search for the nearest
2712feature. If not specified, the search radius is assumed to be
2713infinite."""),
2714    arcGISDisplayName=_(u'Search radius'))
2715
2716AddArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'fidField',
2717    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
2718    description=_(
2719u"""Name of the point field that will receive the feature ID of the
2720nearest feature. In shapefiles, this is the value of the FID field. In
2721geodatabases, it is the value of the OBJECTID field.
2722
2723This field must have the LONG data type. This field will be created if
2724it does not already exist.
2725
2726If no feature is found within the specified search radius, NULL will
2727be stored in this field. If the field does not allow NULL (e.g. the
2728point features are a shapefile), -1 will be stored instead."""),
2729    arcGISDisplayName=_(u'Field to receive nearest feature ID'))
2730
2731AddArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'distanceField',
2732    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
2733    description=_(
2734u"""Name of the point field that will receive the Euclidean distance
2735to the nearest feature.
2736
2737This field must have the FLOAT or DOUBLE data type. This field will be
2738created with the DOUBLE data type if it does not already exist.
2739
2740If no feature is found within the specified search radius, NULL will
2741be stored in this field. If the field does not allow NULL (e.g. the
2742point features are a shapefile), -999999999999 will be stored instead.
2743If the field is has the FLOAT data type, it may not be able to store
2744this value at full precision, so the value may be slightly
2745different."""),
2746    arcGISDisplayName=_(u'Field to receive nearest feature distance'))
2747
2748AddArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'angleField',
2749    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
2750    description=_(
2751u"""Name of the point field that will receive the angle to the nearest
2752feature.
2753
2754This field must have the FLOAT or DOUBLE data type. This field will be
2755created with the DOUBLE data type if it does not already exist.
2756
2757Angles are measured in degrees, where one degree represents 1/360 of a
2758circle, and fractions of a degree are represented as decimal points.
2759Angles are measured from 180 to -180; 0 to the east, 90 to the north,
2760180 (or -180) to the west, and -90 to the south.
2761
2762If no feature is found within the specified search radius, NULL will
2763be stored in this field. If the field does not allow NULL (e.g. the
2764point features are a shapefile), -9999 will be stored instead."""),
2765    arcGISDisplayName=_(u'Field to receive nearest feature angle'))
2766
2767AddArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'overlappingPolygonDistance',
2768    typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'Zero', u'Positive', u'Negative'], makeLowercase=True),
2769    description=_(
2770u"""Distance algorithm that should be used for points that overlap
2771polygons.
2772
2773This parameter is only used when the input near features are polygons
2774and you specify a field to receive the distance to the nearest
2775polygon.
2776
2777This parameter only applies to points that overlap polygons. It
2778controls how distance is calculated for those points and may be one of
2779the following:
2780
2781* Zero - the distance will be zero. This is the default. This is
2782  appropriate when you want to know that the point intersected the
2783  polygon; for example, when the points represent sightings of marine
2784  animals and the polygons represent land and you want to know far the
2785  animals are from land, with the value 0 meaning the animal is on or
2786  over land.
2787
2788* Positive - the distance will be positive. This is appropriate when
2789  you want the distance to the polygon edge, regardless of whether the
2790  point overlaps the polygon or not; for example, when the points
2791  represent sightings of marine animals and the polygons represent
2792  land and you want to know how far the animals are from the
2793  shoreline, regardless of whether they are on water or land.
2794
2795* Negative - the distance will be negative. This is appropriate when
2796  you want the distance to the polygon edge, but need to know whether
2797  the point overlapped the polygon or not. Overlapping points will
2798  have negative distances and positive points will have positive
2799  distances.
2800
2801When a point does not overlap a polygon, this parameter is ignored and
2802the distance will be positive.
2803
2804**WARNING:** If you specify a value for the Search Radius parameter,
2805points that overlap polygons and are farther than this radius from the
2806nearest polygon edge will have a distance of zero, regardless of what
2807distance algorithm you select here. To work around this problem, omit
2808your search radius. Then, after running this tool, construct a feature
2809layer that omits points with distances that exceed your desired
2810radius."""),
2811    arcGISDisplayName=_(u'Distance to use when points overlap polygons'),
2812    arcGISCategory=_(u'Additional options'))
2813
2814AddArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'wherePointFeatures',
2815    typeMetadata=SQLWhereClauseTypeMetadata(canBeNone=True),
2816    description=_(
2817u"""SQL WHERE clause expression that specifies the subset of the
2818points to to use. If this parameter is not provided, all of the points
2819will be used.
2820
2821**WARNING:** You should not reference FID, OID, or OBJECTID fields in
2822this expression. This tool uses the ArcGIS Add Join tool internally.
2823Add Join suffers some limitations that prevent correct operation when
2824those fields are referenced. To work around this problem, use the
2825ArcGIS Select tool to extract your desired points to a new feature
2826class and use that feature class instead.
2827
2828The exact syntax of this expression depends on the type of feature
2829class that contains the near features. ESRI recommends you reference
2830fields using the following syntax:
2831
2832* If the points are stored in an ArcInfo coverage or a shapefile,
2833  enclose field names in double quotes in the SQL expression:
2834  "MY_FIELD".
2835
2836* If the points are stored in a personal geodatabase (.mdb file),
2837  enclose field names in square brackets: [MY_FIELD].
2838
2839* If the points are stored in an ArcSDE or ArcIMS feature
2840  class, don't enclose field names: MY_FIELD."""),
2841    arcGISDisplayName=_(u'Point features where clause'),
2842    arcGISCategory=_(u'Additional options'),
2843    arcGISParameterDependencies=[u'pointFeatures'])
2844
2845AddArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'whereNearFeatures',
2846    typeMetadata=SQLWhereClauseTypeMetadata(canBeNone=True),
2847    description=_(
2848u"""SQL WHERE clause expression that specifies the subset of the near
2849features to to use. If this parameter is not provided, all of the near
2850features will be used.
2851
2852The exact syntax of this expression depends on the type of feature
2853class that contains the near features. ESRI recommends you reference
2854fields using the following syntax:
2855
2856* If the features are stored in an ArcInfo coverage or a shapefile,
2857  enclose field names in double quotes in the SQL expression:
2858  "MY_FIELD".
2859
2860* If the features are stored in a personal geodatabase (.mdb file),
2861  enclose field names in square brackets: [MY_FIELD].
2862
2863* If the features are stored in an ArcSDE or ArcIMS feature
2864  class, don't enclose field names: MY_FIELD."""),
2865    arcGISDisplayName=_(u'Near features where clause'),
2866    arcGISCategory=_(u'Additional options'),
2867    arcGISParameterDependencies=[u'nearFeatures'])
2868
2869AddArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'nearestFieldsToCopy',
2870    typeMetadata=ListTypeMetadata(elementType=ArcGISFieldTypeMetadata(mustExist=True), canBeNone=True, minLength=1),
2871    description=_(
2872u"""Fields of the near features to copy to the points.
2873
2874If you provide this parameter, you must also provide a field to
2875receive the feature ID of the near feature. An error will be reported
2876if you provide this parameter but omit that one.
2877
2878WARNING: This tool uses the ArcGIS Calculate Fields tool to copy the
2879values from the near features' fields to the points' fields. Calculate
2880Fields cannot copy database NULL values and will report a strange
2881warning when asked to do so. As a result, for a given point, if a near
2882feature field contains NULL, the destination field of the point will
2883remain unchanged."""),
2884    arcGISDisplayName=_(u'Fields of the near features to copy'),
2885    arcGISCategory=_(u'Additional options'),
2886    arcGISParameterDependencies=[u'nearFeatures'])
2887
2888AddArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'destinationPointFields',
2889    typeMetadata=ListTypeMetadata(elementType=UnicodeStringTypeMetadata(canBeNone=True), canBeNone=True, minLength=1),
2890    description=_(
2891u"""Names of the point fields to receive values copied from the near
2892feature fields.
2893
2894If you omit this parameter, the tool will use the near feature field
2895names. If the point fields do not already exist, they will be created
2896using the data type and characteristics of the near feature fields. If
2897the point fields do already exist, the best results will be obtained
2898if they same data type and other characteristics as the near feature
2899fields. If the data type and other characteristics are not the same,
2900the value will be coerced to the destination field if possible. If
2901this is not possible, or it would result in a loss of data, an error
2902will be reported."""),
2903    arcGISDisplayName=_(u'Fields to receive copied values'),
2904    arcGISCategory=_(u'Additional options'))
2905
2906AddResultMetadata(ArcGISPoints.FindNearestFeatures, u'updatedPointFeatures',
2907    typeMetadata=ArcGISFeatureClassTypeMetadata(allowedShapeTypes=[u'Point']),
2908    description=_(u"""Updated point features."""),
2909    arcGISDisplayName=_(u'Updated point features'))
2910
2911# Public method: ArcGISPoints._FindNearestFeatures
2912
2913AddMethodMetadata(ArcGISPoints._FindNearestFeatures,
2914    shortDescription=_(u'This private function implements FindNearestFeatures.'))
2915
2916CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'cls', ArcGISPoints._FindNearestFeatures, u'cls')
2917
2918AddArgumentMetadata(ArcGISPoints._FindNearestFeatures, u'tempDir',
2919    typeMetadata=UnicodeStringTypeMetadata(),
2920    description=_(
2921u"""Path to the temp directory."""))
2922
2923AddArgumentMetadata(ArcGISPoints._FindNearestFeatures, u'tempGeoDB',
2924    typeMetadata=UnicodeStringTypeMetadata(),
2925    description=_(
2926u"""Path to the temp geodatabase."""))
2927
2928CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'pointFeatures', ArcGISPoints._FindNearestFeatures, u'pointFeatures')
2929
2930AddArgumentMetadata(ArcGISPoints._FindNearestFeatures, u'tempJoinFIDField',
2931    typeMetadata=UnicodeStringTypeMetadata(),
2932    description=_(
2933u"""Name of the temp FID field for joining to the point features."""))
2934
2935AddArgumentMetadata(ArcGISPoints._FindNearestFeatures, u'tempNearFIDField',
2936    typeMetadata=UnicodeStringTypeMetadata(),
2937    description=_(
2938u"""Name of the temp field of the near feature FID."""))
2939
2940CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'nearFeatures', ArcGISPoints._FindNearestFeatures, u'nearFeatures')
2941CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'searchRadius', ArcGISPoints._FindNearestFeatures, u'searchRadius')
2942CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'fidField', ArcGISPoints._FindNearestFeatures, u'fidField')
2943
2944AddArgumentMetadata(ArcGISPoints._FindNearestFeatures, u'fidFieldNullValue',
2945    typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
2946    description=_(
2947u"""Value to be written to fidField to represent NULL."""))
2948
2949CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'distanceField', ArcGISPoints._FindNearestFeatures, u'distanceField')
2950
2951AddArgumentMetadata(ArcGISPoints._FindNearestFeatures, u'distanceFieldNullValue',
2952    typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
2953    description=_(
2954u"""Value to be written to distanceField to represent NULL."""))
2955
2956CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'angleField', ArcGISPoints._FindNearestFeatures, u'angleField')
2957
2958AddArgumentMetadata(ArcGISPoints._FindNearestFeatures, u'angleFieldNullValue',
2959    typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
2960    description=_(
2961u"""Value to be written to angleField to represent NULL."""))
2962
2963CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'overlappingPolygonDistance', ArcGISPoints._FindNearestFeatures, u'overlappingPolygonDistance')
2964CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'wherePointFeatures', ArcGISPoints._FindNearestFeatures, u'wherePointFeatures')
2965CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'whereNearFeatures', ArcGISPoints._FindNearestFeatures, u'whereNearFeatures')
2966CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'nearestFieldsToCopy', ArcGISPoints._FindNearestFeatures, u'nearestFieldsToCopy')
2967CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'destinationPointFields', ArcGISPoints._FindNearestFeatures, u'destinationPointFields')
2968
2969AddArgumentMetadata(ArcGISPoints._FindNearestFeatures, u'destinationPointFieldsNullValues',
2970    typeMetadata=ListTypeMetadata(elementType=AnyObjectTypeMetadata(canBeNone=True), canBeNone=True),
2971    description=_(
2972u"""Values to be written to the destinationPointFields to represent NULL."""))
2973
2974AddArgumentMetadata(ArcGISPoints._FindNearestFeatures, u'skipArgumentValidation',
2975    typeMetadata=BooleanTypeMetadata(),
2976    description=_(
2977u"""If True, this function will skip the validation of the input
2978arguments against their GeoEco metadata. For efficiency, it is
2979intended that FindNearestFeatures will pass True to skip the
2980validation, because it already performs the validation."""))
2981
2982# Public method: ArcGISPoints.FindNearestFeaturesListedInField
2983
2984AddMethodMetadata(ArcGISPoints.FindNearestFeaturesListedInField,
2985    shortDescription=_(u'For each point, using the feature class or layer listed in a field, finds the nearest feature and writes its ID, distance, angle, and/or values of specified fields to fields of the point. Use this tool when you have a single point feature class but need to find distances to different near feature classes or layers for different points.'),
2986    isExposedToPythonCallers=True,
2987    isExposedByCOM=True,
2988    isExposedAsArcGISTool=True,
2989    arcGISDisplayName=_(u'Find Nearest Features Listed in Field'),
2990    arcGISToolCategory=_(u'Spatial and Temporal Analysis\\Find Features Nearest to Points'),
2991    dependencies=[ArcGISDependency(9, 1), ArcGISProductDependency(u'ArcInfo')])
2992
2993CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'cls', ArcGISPoints.FindNearestFeaturesListedInField, u'cls')
2994CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'pointFeatures', ArcGISPoints.FindNearestFeaturesListedInField, u'pointFeatures')
2995
2996AddArgumentMetadata(ArcGISPoints.FindNearestFeaturesListedInField, u'nearFeaturesField',
2997    typeMetadata=ArcGISFieldTypeMetadata(mustExist=True, allowedFieldTypes=[u'TEXT']),
2998    description=_(
2999u"""Field of the point features that lists the feature classes
3000containing near features to be used for each point. The near features
3001may be points, lines, or polygons.
3002
3003Typically, you use the ArcGIS Calculate Field tool to populate this
3004field based on the other fields of the points. For example, if your
3005points represent observations of a marine animal and your near
3006features are polygons that show monthly sea ice extents, you would use
3007Calculate Field to compute the path and name of the of the sea ice
3008polygon feature class using the date that the animal was observed. If
3009you use a Visual Basic expression with Calculate Field too, the VB
3010date processing functions will often be useful; these include
3011DatePart, DateAdd, and DateDiff. If you use a Python expression, the
3012strptime function and the datetime module will often be useful.
3013
3014This tool is not designed to operate on overlapping near features. If
3015the near features overlap, the behavior of this tool is undefined. It
3016may report a strange error, or it may appear to run successfully but
3017the results may not be correct. Please check the results carefully
3018when using overlapping near features.
3019
3020It is ok if the points overlap the near features.
3021
3022This tool is designed for efficient processing. Rather than processing
3023one point at a time, it processes points in batches, where each batch
3024consists of the points that reference the same near features."""),
3025    arcGISDisplayName=_(u'Field listing near features'),
3026    arcGISParameterDependencies=[u'pointFeatures'])
3027
3028CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'searchRadius', ArcGISPoints.FindNearestFeaturesListedInField, u'searchRadius')
3029CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'fidField', ArcGISPoints.FindNearestFeaturesListedInField, u'fidField')
3030CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'distanceField', ArcGISPoints.FindNearestFeaturesListedInField, u'distanceField')
3031CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'angleField', ArcGISPoints.FindNearestFeaturesListedInField, u'angleField')
3032CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'overlappingPolygonDistance', ArcGISPoints.FindNearestFeaturesListedInField, u'overlappingPolygonDistance')
3033CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'wherePointFeatures', ArcGISPoints.FindNearestFeaturesListedInField, u'wherePointFeatures')
3034
3035AddArgumentMetadata(ArcGISPoints.FindNearestFeaturesListedInField, u'whereNearFeaturesField',
3036    typeMetadata=ArcGISFieldTypeMetadata(canBeNone=True, mustExist=True, allowedFieldTypes=[u'TEXT'], mustBeDifferentThanArguments=[u'nearFeaturesField']),
3037    description=_(
3038u"""Field of the point features that contains a SQL WHERE clause
3039expression that specifies the subset of the near features to to use.
3040If this parameter is not provided, all of the near features will be
3041used.
3042
3043Typically, you use the ArcGIS Calculate Field tool to populate this
3044field based on the other fields of the points. For example, if your
3045points represent observations of a marine animal and they all
3046reference a single feature class that contains a monthly time series
3047of sea ice polygons, you would use Calculate Field to produce
3048expressions that selected polygons for the date the animals were
3049observed.
3050
3051The exact syntax of WHERE expressions depends on the types of feature
3052classes that contain the near features. ESRI recommends you reference
3053fields using the following syntax:
3054
3055* If the features are stored in an ArcInfo coverages or shapefiles,
3056  enclose field names in double quotes in the SQL expression:
3057  "MY_FIELD".
3058
3059* If the features are stored in personal geodatabases (.mdb files),
3060  enclose field names in square brackets: [MY_FIELD].
3061
3062* If the features are stored in ArcSDE or ArcIMS feature
3063  classes, don't enclose field names: MY_FIELD.
3064"""),
3065    arcGISDisplayName=_(u'Field listing near features where clauses'),
3066    arcGISCategory=_(u'Additional options'),
3067    arcGISParameterDependencies=[u'pointFeatures'])
3068
3069AddArgumentMetadata(ArcGISPoints.FindNearestFeaturesListedInField, u'nearestFieldsToCopy',
3070    typeMetadata=ListTypeMetadata(elementType=UnicodeStringTypeMetadata(), canBeNone=True, minLength=1),
3071    description=_(
3072u"""Fields of the near features to copy to the points.
3073
3074The fields you specify must exist in all of the near feature classes
3075or layers that are processed by the tool. A given field must have the
3076same data type and other characteristics in all of the feature classes
3077or layers. An error will be reported if the tool encounters a feature
3078class or layer that does not include all of the fields, or if the data
3079types and characteristics are not consistent."""),
3080    arcGISDisplayName=_(u'Fields of the near features to copy'),
3081    arcGISCategory=_(u'Additional options'))
3082
3083CopyArgumentMetadata(ArcGISPoints.FindNearestFeatures, u'destinationPointFields', ArcGISPoints.FindNearestFeaturesListedInField, u'destinationPointFields')
3084
3085CopyResultMetadata(ArcGISPoints.FindNearestFeatures, u'updatedPointFeatures', ArcGISPoints.FindNearestFeaturesListedInField, u'updatedPointFeatures')
3086
3087###############################################################################
3088# Names exported by this module
3089###############################################################################
3090
3091__all__ = ['ArcGISPoints']
Note: See TracBrowser for help on using the repository browser.