ArcGIS+ArcPy制作船舶(车辆)轨迹热力图
1.在NOAA(national oceanic and atmospheric administration)官网下载迈阿密港口船舶进出数据,时间为2008/12/31 23:59:00到2009/1/4 23:59:00,时间间隔为1分钟。以下都是轨迹点数据:
2.打开属性表,最主要的字段为时间BaseDateTime,船舶号MMSI(相当于车辆的id编号)
3.根据船舶号MMSI,利用轨迹追踪工具脚本,根据时间顺序生成轨迹线数据
代码也是由NOAA提供的,船舶轨迹点数据生成轨迹线数据
"""
August 21, 2015
Marine Cadastre
www.marinecadastre.gov
NOAA Office for Coastal Management
1234 South Hobson Avenue
Charleston, SC 29405
843-740-1200
ocm.mmc@noaa.gov
Software design and development by
RPS ASA, Inc.
55 Village Square Drive
Wakefield, RI 02879
IMSG
3206 Tower Oaks Blvd
Rockville, MD 20852
"""
########################################################################################################
#########################################################################################################
#########################################################################################################
## Input Paramaters
#Path to input points (broadcast feature class)
sInputFile = r"D:\Miami.gdb\Broadcast"
#ID field name (can keep as is most likely)
sID_Field = "MMSI"
#DateTime field name (can keep as is most likely)
sDT_Field = "BaseDateTime"
#Path to output workspace (file geodatabase)
sOutWorkspace = r"D:\Miami.gdb"
#name of output trackline feature class
sOutName = "TrackLine"
#Break Track line method option (comment/uncomment the selected option)
sLineMethod = "Maximum Time and Distance"
#sLineMethod = "Time Interval"
#Maximum Time in minutes (keep as string in quotes, use 0 if using Time Interval method, default = 60)
sMaxTimeMinutes = "60"
#Maximum Distance in miles (keep as string in quotes, use 0 if using Time Interval method, default = 6)
sMaxDistMiles = "6"
#Time Interval in hours (keep as string in quotes, use 0 if using Maximum Time and Distance method, default = 24)
sIntervalHours = "0"
#Include Voyage Attributes (comment/uncomment the selected option)
#sAddVoyageData = "true"
sAddVoyageData = "false"
#Voyage Table (path to voyage table) - OPTIONAL
voyageTable = ""
#Voyage Matching Option (comment/uncomment the selected option) - OPTIONAL
#voyageMethod = "Most Frequent"
voyageMethod = "First"
#Include Vessel Attributes (comment/uncomment the selected option)
#sAddVesselData = "true"
sAddVesselData = "false"
#Vessel Table (path to vessel table) - OPTIONAL
vesselTable = ""
#########################################################################################################
#########################################################################################################
#########################################################################################################
import arcpy, os, sys, traceback, datetime, math, re
import multiprocessing
import time as t
from functools import partial
iMaxTimeMinutes = 0
iMaxDistMiles = 0
iIntervalHours = 0
spatialRef_CURSOR = None
#########################################################################################################
class Timer:
def __init__(self):
self.startTime = t.time()
def End(self):
self.endTime = t.time()
self.elapTime = self.endTime - self.startTime
return self.elapTime
class Progress:
def __init__(self, total):
self.iTotal = float(total)
self.prog = 0
sys.stdout.write('\r 0%')
def Update(self, current):
perc = round(current/self.iTotal * 100.0,0)
if perc > self.prog:
self.prog = perc
sys.stdout.write('\r '+str(perc)+"%")
sys.stdout.flush()
def CheckAISDatabase():
"""Performs a series of checks to see if the source points are a true AIS database with vessel and voyage data."""
bAISdb = True
desc = arcpy.Describe(sInputFile)
input_wkspace = desc.Path
sInName = desc.Name
if os.path.splitext(input_wkspace)[1] <> ".gdb":
bAISdb = False
else:
if sInName.find("Broadcast") < 0:
bAISdb = False
else:
sVesselName = re.sub("Broadcast", "Vessel", sInName)
sVoyageName = re.sub("Broadcast", "Voyage", sInName)
if arcpy.Exists(input_wkspace+"\\"+sVesselName) == False:
bAISdb = False
else:
if arcpy.Exists(input_wkspace+"\\"+sVoyageName) == False:
bAISdb = False
else:
if arcpy.Exists(input_wkspace+"\\BroadcastHasVessel") == False:
bAISdb = False
else:
if arcpy.Exists(input_wkspace+"\\BroadcastHasVoyage") == False:
bAISdb = False
return bAISdb
def FormatCounts(iInCount):
"""Formats various counts to strings with comma separators, for use in dialog messages."""
sOutCount = re.sub("(\d)(?=(\d{3})+(?!\d))", r"\1,", "%d" % iInCount)
return sOutCount
def GetVersion():
"""Gets the version of ArcGIS being used. Faster methods to read input datasets are used when 10.1 is the version in use."""
dicInstallInfo = arcpy.GetInstallInfo()
return dicInstallInfo["Version"]
def CreateDataDict_NEW():
"""Process to read through the input point feature class. Point information is stored in a Python dictionary in memory.
All point attributes and coordinates (expect date/time) are read into Numpy Array.
The date information is read using the Data Access module search cursor.
The two parts are then combined based on the OID into the Python dictionary"""
print "\n Reading input point data..."
iTotPoints = int(arcpy.GetCount_management(sInputFile).getOutput(0))
timer = Timer()
prog = Progress(iTotPoints*3) #once for array, date dict, merge
tally = 0
i=0
if bRealAISdb == True:
fields = ["SHAPE@XY",sID_Field,"VoyageID","OID@"]
fields2 = ["OID@",sDT_Field]
##FILTERING MMSI CODES THAT ARE ZERO
where = sID_Field+" > 0"
else:
fields = ["SHAPE@XY",sID_Field,"OID@"]
fields2 = ["OID@",sDT_Field]
where = None
#Store all data (except date) into numpy array
ptData_Array = arcpy.da.FeatureClassToNumPyArray(sInputFile,fields,where,spatialRef_CURSOR)
tally+=iTotPoints
prog.Update(tally)
#Get date using search cursor, store in temporary dictionary
dateDict = {}
with arcpy.da.SearchCursor(sInputFile,fields2,where,spatialRef_CURSOR,False) as rows:
for row in rows:
sOID = row[0]
dDateTime = row[1]
dateDict[sOID] = dDateTime
tally+=1
prog.Update(tally)
#to account for points without MMSI
tally=iTotPoints*2
prog.Update(tally)
#combine array and date dictionary into one final dictionary
dataDict = {}
for i in xrange(0,len(ptData_Array[sID_Field])):
if bRealAISdb == True:
sID, oid, voyageID, geo = ptData_Array[sID_Field][i], ptData_Array["OID@"][i], ptData_Array["VoyageID"][i], ptData_Array["SHAPE@XY"][i]
else:
sID, oid, geo, voyageID = ptData_Array[sID_Field][i], ptData_Array["OID@"][i], ptData_Array["SHAPE@XY"][i], None
if dataDict.has_key(sID):
dataDict[sID][dateDict[oid]] = [geo[0],geo[1],voyageID]
else:
dataDict[sID] = {dateDict[oid]:[geo[0],geo[1],voyageID]}
tally+=1
prog.Update(tally)
del sID, oid, geo, voyageID
del dateDict, ptData_Array
print "\r Read "+FormatCounts(iTotPoints)+" points"
print " Read Complete:" + str(timer.End())
del timer, prog
return dataDict, iTotPoints
def SplitDataDictEqually(input_dict, parts, total_points):
"""Splits the point data dictionary into equal parts, for separate processing."""
return_list = [dict() for idx in xrange(parts)]
min_pts_per_part = math.ceil(total_points/parts)
idx = 0
pts_added = 0
for k,v in input_dict.iteritems():
if pts_added < min_pts_per_part:
return_list[idx][k] = v
pts_added+=len(v.keys())
else:
idx += 1
pts_added = 0
return_list[idx][k] = v
pts_added+=len(v.keys())
return return_list
def CreateOuputDataset(parts=0):
"""Creates an empty feature class to store the track polylines. Adds the required fields, based on the options selected by the user."""
print "\n Building output track line feature class..."
tmp = os.path.join('in_memory', 'template')
#create a feature class in memory, to use as a template for all feature classes created
arcpy.CreateFeatureclass_management('in_memory','template',"POLYLINE","","DISABLED","DISABLED",sInputFile)
arcpy.AddField_management(tmp,sID_Field,"LONG",)
arcpy.AddField_management(tmp,"TrackStartTime","DATE",)
arcpy.AddField_management(tmp,"TrackEndTime","DATE",)
if bRealAISdb == True:
if sAddVoyageData == "true":
arcpy.AddField_management(tmp,"VoyageID","LONG",)
arcpy.AddField_management(tmp,"Destination","TEXT")
arcpy.AddField_management(tmp,"Cargo","LONG",)
arcpy.AddField_management(tmp,"Draught","LONG",)
arcpy.AddField_management(tmp,"ETA","DATE",)
arcpy.AddField_management(tmp,"StartTime","DATE",)
arcpy.AddField_management(tmp,"EndTime","DATE",)
if sAddVesselData == "true":
arcpy.AddField_management(tmp,"IMO","LONG",)
arcpy.AddField_management(tmp,"CallSign","TEXT",)
arcpy.AddField_management(tmp,"Name","TEXT",)
arcpy.AddField_management(tmp,"VesselType","LONG",)
arcpy.AddField_management(tmp,"Length","LONG",)
arcpy.AddField_management(tmp,"Width","LONG",)
arcpy.AddField_management(tmp,"DimensionComponents","TEXT",)
#create a temporary FGDB and feature class for each separate process to avoid locking issues
if parts > 1:
for i in range(1,parts+1):
tmp_wkspc_path = os.path.split(sOutWorkspace)[0]
arcpy.CreateFileGDB_management(tmp_wkspc_path, "temp" + str(i) + ".gdb")
tmp_fgdb = os.path.join(tmp_wkspc_path, "temp" + str(i) + ".gdb")
arcpy.CreateFeatureclass_management(tmp_fgdb,sOutName+str(i),"POLYLINE",tmp,"DISABLED","DISABLED",sInputFile)
#create one feature class since no multiprocessing being used
else:
arcpy.CreateFeatureclass_management(sOutWorkspace,sOutName,"POLYLINE",tmp,"DISABLED","DISABLED",sInputFile)
#delete the temporary template dataset in memory
arcpy.Delete_management(tmp)
del tmp
def GetDistance(prevX,prevY,currX,currY):
"""Calculates the distance between two latitude/longitude coordinate pairs, using the Vincenty formula for ellipsoid based distances.
Returns the distance in miles."""
#Vincenty Formula
#Copyright 2002-2012 Chris Veness
#http://www.movable-type.co.uk/scripts/latlong-vincenty.html
a = 6378137
b = 6356752.314245
f = 1/298.257223563
L = math.radians(currX - prevX)
U1 = math.atan((1-f)*math.tan(math.radians(prevY)))
U2 = math.atan((1-f)*math.tan(math.radians(currY)))
sinU1 = math.sin(U1)
sinU2 = math.sin(U2)
cosU1 = math.cos(U1)
cosU2 = math.cos(U2)
lam = L
lamP = 9999999999
iter_count = 0
while abs(lam-lamP) > 1e-12 and iter_count <= 100:
sinLam = math.sin(lam)
cosLam = math.cos(lam)
sinSigma = math.sqrt((cosU2*sinLam)*(cosU2*sinLam) + (cosU1*sinU2-sinU1*cosU2*cosLam)*(cosU1*sinU2-sinU1*cosU2*cosLam))
if sinSigma == 0:
return 0
cosSigma = sinU1*sinU2+cosU1*cosU2*cosLam
sigma = math.atan2(sinSigma,cosSigma)
sinAlpha = cosU1*cosU2*sinLam/sinSigma
cosSqAlpha = 1 - sinAlpha*sinAlpha
if cosSqAlpha == 0: #catches zero division error if points on equator
cos2SigmaM = 0
else:
cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha
if cos2SigmaM == None:
cos2SigmaM = 0
C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha))
lamP = lam
lam = L+(1-C)*f*sinAlpha*(sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)))
iter_count+=1
uSq = cosSqAlpha*(a*a - b*b)/(b*b)
A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)))
B = uSq/1024*(256+uSq*(-128+uSq*(74-47*uSq)))
deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)))
s = b*A*(sigma-deltaSigma)
#convert s in meters to miles
s_miles = s*0.0006213712
return s_miles
def GetTimeDifference(prevDT,currDT):
"""Calculates the difference between to date and time variables. The difference is returned in minutes."""
timeDelta = currDT - prevDT
totMinutes = (timeDelta.days*1440) + (timeDelta.seconds/60)
return totMinutes
def MultiProcess_Main(dataDict, iTotPoints):
"""Main process to split the data and run seperate processes to build the track lines for each available CPU."""
print "\n Generating track lines..."
timer = Timer()
parts = multiprocessing.cpu_count() - 1
prog = Progress(1 + parts + 2) #split, each sub-process, merge, delete temp
tally = 0
tmp_wkspc_path = os.path.split(sOutWorkspace)[0]
#split the point data into separate dictionaries for each process.
split_dicts = SplitDataDictEqually(dataDict, parts, iTotPoints)
tally+=1
prog.Update(tally)
#start each separate process asynchronously, and wait for all to finish before moving on.
pool = multiprocessing.Pool(parts)
jobs = []
for i in range(1,parts+1):
tmp_fgdb = os.path.join(tmp_wkspc_path, "temp" + str(i) + ".gdb")
sTempFC = tmp_fgdb+"\\"+sOutName+str(i)
splitDict = split_dicts[i-1]
tally+=1
p_callback = partial(MultiProcess_Status,p=prog,t=tally)
if sLineMethod == "Maximum Time and Distance":
jobs.append(pool.apply_async(AddLines_Segmented, (splitDict, sTempFC,iMaxTimeMinutes,iMaxDistMiles,spatialRef_CURSOR), callback=p_callback))
elif sLineMethod == "Time Interval":
jobs.append(pool.apply_async(AddLines_Interval, (splitDict, sTempFC,iIntervalHours,spatialRef_CURSOR), callback=p_callback))
pool.close()
pool.join()
del split_dicts
#Merge the temporary output feature classes in separate FGDBs into one feature class in the selected output geodatabase
to_merge = []
for i in range(1,parts+1):
tmp_fgdb = os.path.join(tmp_wkspc_path, "temp" + str(i) + ".gdb")
to_merge.append(tmp_fgdb+"\\"+sOutName+str(i))
arcpy.Merge_management(to_merge, sOutWorkspace+"\\"+sOutName)
tally+=1
prog.Update(tally)
#Delete the temporary geodatabases
for i in range(1,parts+1):
tmp_fgdb = os.path.join(tmp_wkspc_path, "temp" + str(i) + ".gdb")
arcpy.Delete_management(tmp_fgdb)
tally+=1
prog.Update(tally)
iTotTracks = int(arcpy.GetCount_management(sOutWorkspace+"\\"+sOutName).getOutput(0))
print "\r Unique "+sID_Field+"'s= "+FormatCounts(len(dataDict))
print " Track lines created = "+FormatCounts(iTotTracks)
print " Track lines created in:" + str(timer.End())
del timer
def MultiProcess_Status(x, p, t):
"""Simple function to get the status of each separate track line building process."""
p.Update(t)
def AddLines_Segmented(dataDict,sOutputFC,iMaxTimeMinutes,iMaxDistMiles,spatialRef_CURSOR,progress=False):
"""Creates track polylines and saves them in the ouput feature class. Track lines are created using a maximum distance and
maximum time threshold between points."""
prog = None
tally = 0
if progress:
prog = Progress(len(dataDict))
cur = arcpy.da.InsertCursor(sOutputFC, ("SHAPE@",sID_Field,"TrackStartTime","TrackEndTime"))
pnt1 = arcpy.Point()
pnt2 = arcpy.Point()
lineArray = arcpy.Array()
for key in sorted(dataDict.iterkeys()):
tally+=1
iSegCount = 0
#list of date time dictionary keys
dtTimes = sorted(dataDict[key].keys())
#start and end date variables: start date set with the start of any new line. end date constantly updated with the second point
#so that whenever the line ends the last datetime will already be set.
dtSegStart = None
dtSegEnd = None
#skip MMSI if there is only one point provided
if len(dtTimes) > 1:
for i in range(0,len(dtTimes)-1):
pnt1.X = dataDict[key][dtTimes[i]][0]
pnt1.Y = dataDict[key][dtTimes[i]][1]
pnt2.X = dataDict[key][dtTimes[i+1]][0]
pnt2.Y = dataDict[key][dtTimes[i+1]][1]
distance = GetDistance(pnt1.X,pnt1.Y,pnt2.X,pnt2.Y)
timeDiff = GetTimeDifference(dtTimes[i],dtTimes[i+1])
if distance < iMaxDistMiles and timeDiff < iMaxTimeMinutes:
if iSegCount == 0:
dtSegStart = dtTimes[i]
lineArray.add(pnt1)
lineArray.add(pnt2)
else:
lineArray.add(pnt2)
dtSegEnd = dtTimes[i+1]
iSegCount+=1
#Break the line as the gap exceeds the thresholds
else:
if lineArray.count > 1:
polyline = arcpy.Polyline(lineArray,spatialRef_CURSOR)
cur.insertRow((polyline,key,dtSegStart,dtSegEnd))
del polyline
lineArray.removeAll()
dtSegStart = None
dtSegEnd = None
##reset seg count,since line ended early due to thresholds
iSegCount = 0
#end line since end of this MMSI set
if lineArray.count > 1:
polyline = arcpy.Polyline(lineArray,spatialRef_CURSOR)
cur.insertRow((polyline,key,dtSegStart,dtSegEnd))
del polyline
lineArray.removeAll()
dtSegStart = None
dtSegEnd = None
if progress:
prog.Update(tally)
del prog
del cur
def AddLines_Interval(dataDict,sOutputFC,iIntervalHours,spatialRef_CURSOR,progress=False):
"""Creates track polylines and saves them in the ouput feature class. Track lines are created based on the specified time interval."""
prog = None
tally = 0
if progress:
prog = Progress(len(dataDict))
cur = arcpy.da.InsertCursor(sOutputFC, ("SHAPE@",sID_Field,"TrackStartTime","TrackEndTime"))
pnt1 = arcpy.Point()
pnt2 = arcpy.Point()
lineArray = arcpy.Array()
#create time interval from input parameter hours
tdInterval = datetime.timedelta(hours=iIntervalHours)
for key in sorted(dataDict.iterkeys()):
tally+=1
iSegCount = 0
#list of date time dictionary keys
dtTimes = sorted(dataDict[key].keys())
#start and end date variables: start date set with the start of any new line. end date constatnly updated with the second point
#so that whenever the line ends the last datetime will already be set.
dtSegStart = None
dtSegEnd = None
#initialize time interval using the first point as the start, and the end based on the selected interval
#updated as the interval threshold is reached.
dtIntervalBegin = dtTimes[0]
dtIntervalEnd = dtIntervalBegin + tdInterval
#skip MMSI if there is only one point provided
if len(dtTimes) > 1:
for i in range(0,len(dtTimes)-1):
pnt1.X = dataDict[key][dtTimes[i]][0]
pnt1.Y = dataDict[key][dtTimes[i]][1]
pnt2.X = dataDict[key][dtTimes[i+1]][0]
pnt2.Y = dataDict[key][dtTimes[i+1]][1]
if dtTimes[i] >= dtIntervalBegin and dtTimes[i+1] <= dtIntervalEnd:
if iSegCount == 0:
dtSegStart = dtTimes[i]
lineArray.add(pnt1)
lineArray.add(pnt2)
else:
lineArray.add(pnt2)
dtSegEnd = dtTimes[i+1]
iSegCount+=1
#Break the line as the next point is outside the defined interval
else:
if lineArray.count > 1:
polyline = arcpy.Polyline(lineArray,spatialRef_CURSOR)
cur.insertRow((polyline,key,dtSegStart,dtSegEnd))
del polyline
lineArray.removeAll()
dtSegStart = None
dtSegEnd = None
#update for next time interval
dtIntervalBegin = dtIntervalEnd
dtIntervalEnd = dtIntervalBegin + tdInterval
##reset seg count,since line ended early due to thresholds
iSegCount = 0
#end line since end of this MMSI set
if lineArray.count > 1:
polyline = arcpy.Polyline(lineArray,spatialRef_CURSOR)
cur.insertRow((polyline,key,dtSegStart,dtSegEnd))
del polyline
lineArray.removeAll()
dtSegStart = None
dtSegEnd = None
dtIntervalBegin = None
dtIntervalEnd = None
if progress:
prog.Update(tally)
del prog
del cur
def AddVoyageData_NEW(dataDict,sOutputFC):
"""Adds voyage attribution from Voyage table to the assocatiated track lines. Voyage data is associated with the track lines based on the VoyageID.
Since one MMSI may have multiple VoyageIDs, the matching VoyageIDs can be selected by using the most frequent or the first."""
print "\n Adding Voyage data to output..."
timer = Timer()
prog = Progress(int(arcpy.GetCount_management(sOutputFC).getOutput(0)) + int(arcpy.GetCount_management(voyageTable).getOutput(0)))
tally = 0
iTotTracks = 0
iVoyageDataAdded = 0
#read ALL the voyage data into a python dictionary
dicVoyageAttrs = {}
with arcpy.da.SearchCursor(voyageTable,("VoyageID","Destination","Cargo","Draught","ETA","StartTime","EndTime")) as vRows:
for vRow in vRows:
tally+=1
dicVoyageAttrs[vRow[0]] = [vRow[1],vRow[2],vRow[3],vRow[4],vRow[5],vRow[6]]
prog.Update(tally)
#update the tracklines based on voyage data
with arcpy.da.UpdateCursor(sOutputFC, ("MMSI","TrackStartTime","TrackEndTime","VoyageID","Destination","Cargo","Draught","ETA","StartTime","EndTime")) as rows:
for row in rows:
tally+=1
iTotTracks+=1
lMMSI = row[0]
dtStart = row[1]
dtEnd = row[2]
if voyageMethod == "Most Frequent":
#Go through data dict and create dict of voyage IDs
dicVoyage = {}
for dt in sorted(dataDict[lMMSI].iterkeys()):
if dt >= dtStart and dt <= dtEnd:
iVoyageID = dataDict[lMMSI][dt][2]
if iVoyageID in dicVoyage:
dicVoyage[iVoyageID]+=1
else:
dicVoyage[iVoyageID] = 1
#get most frequent Voyage of Trackline
max_count = 0
for v in dicVoyage.iterkeys():
if dicVoyage[v] > max_count:
max_count = dicVoyage[v]
iVoyageID = v
else:#voyage method = "First"
for dt in sorted(dataDict[lMMSI].iterkeys()):
if dt >= dtStart and dt <= dtEnd:
iVoyageID = dataDict[lMMSI][dt][2]
break
#update row with voyageID
row[3] = iVoyageID
if dicVoyageAttrs.has_key(iVoyageID):
row[4] = dicVoyageAttrs[iVoyageID][0]
row[5] = dicVoyageAttrs[iVoyageID][1]
row[6] = dicVoyageAttrs[iVoyageID][2]
row[7] = dicVoyageAttrs[iVoyageID][3]
row[8] = dicVoyageAttrs[iVoyageID][4]
row[9] = dicVoyageAttrs[iVoyageID][5]
iVoyageDataAdded+=1
rows.updateRow(row)
prog.Update(tally)
print "\r Added to "+FormatCounts(iVoyageDataAdded)+" of "+FormatCounts(iTotTracks)+" track lines"
print " Added Voyage Data in:" + str(timer.End())
del prog
del timer
def AddVesselData_NEW(sOutputFC):
"""Adds vessel attribution from Vessel table to the assocatiated track lines. Vessel data is associated with the track lines based on the MMSI."""
print "\n Adding Vessel data to output..."
timer = Timer()
prog = Progress(int(arcpy.GetCount_management(sOutputFC).getOutput(0)) + int(arcpy.GetCount_management(vesselTable).getOutput(0)))
tally = 0
iTotTracks = 0
iVesselDataAdded = 0
#read ALL the vessel data into a python dictionary
dictVesselData = {}
with arcpy.da.SearchCursor(vesselTable,("MMSI","IMO","CallSign","Name","VesselType","Length","Width","DimensionComponents")) as vRows:
for vRow in vRows:
tally+=1
dictVesselData[vRow[0]] = [vRow[1],vRow[2],vRow[3],vRow[4],vRow[5],vRow[6],vRow[7]]
prog.Update(tally)
#update the tracklines based on vessel data
with arcpy.da.UpdateCursor(sOutputFC, ("MMSI","IMO","CallSign","Name","VesselType","Length","Width","DimensionComponents")) as rows:
for row in rows:
tally+=1
iTotTracks+=1
lMMSI = row[0]
if dictVesselData.has_key(lMMSI):
row[1] = dictVesselData[lMMSI][0]
row[2] = dictVesselData[lMMSI][1]
row[3] = dictVesselData[lMMSI][2]
row[4] = dictVesselData[lMMSI][3]
row[5] = dictVesselData[lMMSI][4]
row[6] = dictVesselData[lMMSI][5]
row[7] = dictVesselData[lMMSI][6]
iVesselDataAdded+=1
rows.updateRow(row)
prog.Update(tally)
print "\r Added to "+FormatCounts(iVesselDataAdded)+" of "+FormatCounts(iTotTracks)+" track lines"
print " Added Vessel Data in:" + str(timer.End())
del prog
del timer
def SetupVoyageCodedDomain(sOutputFC):
"""Setup the Coded Domains for the Cargo field. Creates a temporary table of the Cargo domain from the source database,
and then imports it to the ouput track feature class."""
print "\n Assigning Cargo coded domain values..."
input_wkspace = os.path.split(sInputFile)[0]
arcpy.env.workspace = sOutWorkspace
desc = arcpy.Describe(sOutWorkspace)
domains = desc.domains
#Check if domain already exists
bCargoDomExists = False
for domain in domains:
if domain == "Cargo":
bCargoDomExists = True
if bCargoDomExists == False:
#Copy the domain from the input AIS database to the output database
arcpy.DomainToTable_management(input_wkspace,"Cargo","CargoDom","code","description")
arcpy.TableToDomain_management("CargoDom","code","description",sOutWorkspace,"Cargo")
#Delete the temporary Domain Table
arcpy.Delete_management("CargoDom")
#Assign domain to the field in the track feature class
try:
arcpy.AssignDomainToField_management(sOutName,"Cargo","Cargo")
except:
print " ERROR Assigning Domain - Workspace is read only."
print " Domain can be assigned to the 'Cargo' field manually."
print " See the 'Use Limitations' section in the help documentation for details."
def SetupVesselCodedDomain(sOutputFC):
"""Setup the Coded Domains for the Vessel Type field. Creates a temporary table of the VesselType domain from the source database,
and then imports it to the ouput track feature class."""
print "\n Assigning Vessel Type coded domain values..."
input_wkspace = os.path.split(sInputFile)[0]
arcpy.env.workspace = sOutWorkspace
desc = arcpy.Describe(sOutWorkspace)
domains = desc.domains
#Check if domain already exists
bCargoDomExists = False
for domain in domains:
if domain == "VesselType":
bCargoDomExists = True
if bCargoDomExists == False:
#Copy the domain from the input AIS database to the output database
arcpy.DomainToTable_management(input_wkspace,"VesselType","VesselTypeDom","code","description")
arcpy.TableToDomain_management("VesselTypeDom","code","description",sOutWorkspace,"VesselType")
#Delete the temporary Domain Table
arcpy.Delete_management("VesselTypeDom")
#Assign domain to the field in the track feature class
try:
arcpy.AssignDomainToField_management(sOutName,"VesselType","VesselType")
except:
print " ERROR Assigning Domain - Workspace is read only."
print " Domain can be assigned to the 'VesselType' field manually."
print " See the 'Use Limitations' section in the help documentation for details."
def CreateTracks():
"""Main function that calls all of the other functions. Determines which functions are run, based on the user selected parameters."""
#Create Dictionary of broadcast data Key = ID, Value = Dictionary of {DateTime:[x,y,voyageID]}
dataDict, iTotPoints = CreateDataDict_NEW()
sOutputFC = sOutWorkspace+"\\"+sOutName
#If there are more than 2 CPUs then use multiprocessing approach, otherwise only use 1
# leaving one CPU to run operating system.
if multiprocessing.cpu_count() <= 2:
#Create output feature class
CreateOuputDataset(0)
#build track lines
print "\n Generating track lines..."
if sLineMethod == "Maximum Time and Distance":
AddLines_Segmented(dataDict,sOutputFC,iMaxTimeMinutes,iMaxDistMiles,spatialRef_CURSOR,True)
elif sLineMethod == "Time Interval":
AddLines_Interval(dataDict,sOutputFC,iIntervalHours,spatialRef_CURSOR,True)
iTotTracks = int(arcpy.GetCount_management(sOutputFC).getOutput(0))
print "\r Unique "+sID_Field+"'s= "+FormatCounts(len(dataDict))
print " Track lines created = "+FormatCounts(iTotTracks)
else:
#Create multiple temproary output feature classes
CreateOuputDataset(multiprocessing.cpu_count() - 1)
#Use multiprocessing method to create track lines
MultiProcess_Main(dataDict, iTotPoints)
#Add Voyage info, if table provided:
if sAddVoyageData == "true":
AddVoyageData_NEW(dataDict,sOutputFC)
SetupVoyageCodedDomain(sOutputFC)
#Add Vessel info, if table provided:
if sAddVesselData == "true":
AddVesselData_NEW(sOutputFC)
SetupVesselCodedDomain(sOutputFC)
print "\n"
def CheckParameters():
"""This function performs the checks required to make sure all the input parameters were provided and accurate.
These are the same checks performed within the Toolbox script tool validation code."""
global sAddVoyageData, sAddVesselData, spatialRef_CURSOR
global iMaxTimeMinutes, iMaxDistMiles, iIntervalHours
print "\n Checking Input Parameters..."
bPass = True
Messages = []
bRealAISdb = False
desc = None
#input points checks
if arcpy.Exists(sInputFile) == False:
bPass = False
Messages.append("Input Points do not exist")
else:
desc = arcpy.Describe(sInputFile)
if desc.datasetType <> "FeatureClass":
bPass = False
Messages.append("Input is not a feature class")
else:
if desc.shapeType <> "Point":
bPass = False
Messages.append("Input is not a point feature class")
else:
prjFile = os.path.join(arcpy.GetInstallInfo()["InstallDir"],"Coordinate Systems/Geographic Coordinate Systems/World/WGS 1984.prj")
spatialRef_WGS84 = arcpy.SpatialReference(prjFile)
spatialRef_input = arcpy.Describe(sInputFile).spatialReference
if spatialRef_WGS84.name <> spatialRef_input.name:
spatialRef_CURSOR = spatialRef_WGS84
else:
spatialRef_CURSOR = None
fields = desc.fields
#ID Field checks
bFieldExists = False
for field in fields:
if field.name == sID_Field:
bFieldExists = True
if field.type not in ["SmallInteger","Integer"]:
bPass = False
Messages.append("Input ID field is not an integer field")
break
if bFieldExists == False:
bPass = False
Messages.append("Input ID field does not exist in input feature class")
#DateTime Field checks
bFieldExists = False
for field in fields:
if field.name == sDT_Field:
bFieldExists = True
if field.type <> "Date":
bPass = False
Messages.append("Input Date/Time field is not an date field")
break
if bFieldExists == False:
bPass = False
Messages.append("Input Date/Time field does not exist in input feature class")
#check if adding vessel/voyage data is appropriate (real AIS database)
bRealAISdb = CheckAISDatabase()
#Output Workspace checks
if arcpy.Exists(sOutWorkspace) == False:
bPass = False
Messages.append("Output workspace does not exist")
else:
desc = arcpy.Describe(sOutWorkspace)
if desc.workspaceType <> "LocalDatabase":
bPass = False
Messages.append("Output workspace is not a geodatabase")
#Check output name
if sOutName <> str(arcpy.ValidateTableName(sOutName,sOutWorkspace)):
bPass = False
Messages.append("Output trackline feature class name is invalid")
#Check Break trackline option
if sLineMethod not in ["Maximum Time and Distance","Time Interval"]:
bPass = False
Messages.append("Break trackline method parameter is not a valid option")
#Check Maximum Time in minutes
try:
if sLineMethod == "Maximum Time and Distance":
test = int(sMaxTimeMinutes)
if int(sMaxTimeMinutes) <= 0:
bPass = False
Messages.append("Maximum Time must be greater than zero when using the Maximum Time and Distance option")
else:
iMaxTimeMinutes = int(sMaxTimeMinutes)
except:
bPass = False
Messages.append("Maximum Time is not numeric")
#Check Maximum Distance
try:
if sLineMethod == "Maximum Time and Distance":
test = int(sMaxDistMiles)
if int(sMaxDistMiles) <= 0:
bPass = False
Messages.append("Maximum Distance must be greater than zero when using the Maximum Time and Distance option")
else:
iMaxDistMiles = int(sMaxDistMiles)
except:
bPass = False
Messages.append("Maximum Distance is not numeric")
#Check Time Interval
try:
if sLineMethod == "Time Interval":
test = int(sIntervalHours)
if int(sIntervalHours) <= 0:
bPass = False
Messages.append("Time Interval must be greater than zero when using the Time Interval option")
else:
iIntervalHours = int(sIntervalHours)
except:
bPass = False
Messages.append("Time Interval is not numeric")
#Check Include voyage option
if sAddVoyageData not in ["true","false"]:
bPass = False
Messages.append("Option to include Voyage data must be 'true' or 'false'")
else:
if not bRealAISdb and sAddVoyageData == "true":
print " Ignoring Voyage data since input data is not part of a complete AIS database"
sAddVoyageData = "false"
elif sAddVoyageData == "true":
#check voyage table
if arcpy.Exists(voyageTable) == False:
bPass = False
Messages.append("Voyage table does not exist")
else:
desc = arcpy.Describe(voyageTable)
if desc.datasetType <> "Table":
bPass = False
Messages.append("Voyage table parameter is not a table")
#Check Voyage Matching Option
if voyageMethod not in ["Most Frequent","First"]:
bPass = False
Messages.append("Voyage Matching Option parameter is not a valid option")
#Check Include vessel data option
if sAddVesselData not in ["true","false"]:
bPass = False
Messages.append("Option to include Vessel data must be 'true' or 'false'")
else:
if not bRealAISdb and sAddVesselData == "true":
print " Ignoring Vessel data since input data is not part of a complete AIS database"
sAddVesselData = "false"
elif sAddVesselData == "true":
#check vessel table
if arcpy.Exists(vesselTable) == False:
bPass = False
Messages.append("Vessel table does not exist")
else:
desc = arcpy.Describe(vesselTable)
if desc.datasetType <> "Table":
bPass = False
Messages.append("Vessel table parameter is not a table")
return bPass,Messages, bRealAISdb
def HelpText(Messages = []):
"""Returns the command line syntax and examples of usage, when there was a mistake."""
if len(Messages) > 0:
print "\n\n"
for message in Messages:
print " ERROR: " + message
if len(sys.argv) <> 1:
print "\n\n"
print "Trackbuilder Command Line Syntax:"
print """<path to x64 python.exe> <path to Trackbuilder .py script> <Path to input points> <ID field name> <DateTime field name> <Path to output workspace> <name of output trackline feature class> <Break Track line method option> <Maximum Time in minutes> <Maximum Distance in miles> <Time Interval in hours> <Include Voyage Attributes> <Voyage Table> <Voyage Matching Option> <Include Vessel Attributes> <Vessel Table>"""
print ""
print "Examples:"
print """"C:\Python27\ArcGISx6410.3\python.exe" "C:\AIS\TrackBuilderVersion2.1_x64.py" "C:\AIS\Zone19_2011_07.gdb\Broadcast" "MMSI" "BaseDateTime" "C:\AIS\Zone19_2011_07.gdb" "TrackLine" "Maximum Time and Distance" "60" "6" "0" "true" "C:\AIS\Zone19_2011_07.gdb\Voyage" "Most Frequent" "true" "C:\AIS\Zone19_2011_07.gdb\Vessel" """
print ""
print """"C:\Python27\ArcGISx6410.3\python.exe" "C:\AIS\TrackBuilderVersion2.1_x64.py" "C:\AIS\Zone19_2011_07.gdb\Broadcast" "MMSI" "BaseDateTime" "C:\AIS\Zone19_2011_07.gdb" "TrackLine" "Time Interval" "0" "0" "24" "false" "" "" "false" "" """
##########################################
##########################################
if __name__ == '__main__':
timer = Timer()
bRun = True
Errors = []
global bRealAISdb
if len(sys.argv) == 1:
bRun, Errors, bRealAISdb = CheckParameters()
else:
#only proceed if all parameters are present
if len(sys.argv) <> 15 and len(sys.argv) <> 10: #all or without voyage/vessel options
bRun = False
else:
sInputFile = sys.argv[1]
sID_Field = sys.argv[2]
sDT_Field = sys.argv[3]
sOutWorkspace = sys.argv[4]
sOutName = sys.argv[5]
sLineMethod = sys.argv[6]
sMaxTimeMinutes = sys.argv[7]
sMaxDistMiles = sys.argv[8]
sIntervalHours = sys.argv[9]
if len(sys.argv) == 15:
sAddVoyageData = sys.argv[10]
voyageTable = sys.argv[11]
voyageMethod = sys.argv[12]
sAddVesselData = sys.argv[13]
vesselTable = sys.argv[14]
else:
sAddVoyageData = "false"
voyageTable = ""
voyageMethod = ""
sAddVesselData = "false"
vesselTable = ""
bRun, Errors, bRealAISdb = CheckParameters()
#Run the process if all parameters were validated
if bRun:
#Updated to work with ArcGIS Desktop 10.1 or later
if GetVersion() in ["10.1","10.2","10.2.1","10.2.2","10.3","10.3.1","10.4","10.4.1","10.5"]:
if sys.exec_prefix.find("x64") > 0:
try:
arcpy.env.overwriteOutput = True
CreateTracks()
print "Track Lines complete!"
print " Process complete in " + str(datetime.timedelta(seconds=timer.End()))
del timer
except arcpy.ExecuteError:
for msg in range(0, arcpy.GetMessageCount()):
if arcpy.GetSeverity(msg) == 2:
print arcpy.GetMessage(msg)
except:
tb = sys.exc_info()[2]
for i in range(0,len(traceback.format_tb(tb))):
tbinfo = traceback.format_tb(tb)[i]
msg = " ERROR: \n " + tbinfo + " Error Info:\n " + str(sys.exc_info()[1])
print msg
else:
msg = " ERROR: The x64 version of the TrackBuilder can only be used with a 64bit version of Python"
print msg
else:
msg = " ERROR: TrackBuilder can only be used with ArcGIS 10.1 or later"
print msg
#show syntax and examples if parameters were missing or invalid.
else:
HelpText(Errors)
4.再利用核密度分析工具,选择合适的参数值,生成轨迹热力图
5.打开新生成的图像的属性表,打开符号系统,选择合适的色带
最终效果图如下
转载自:https://blog.csdn.net/qq_912917507/article/details/81099656