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

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