def _manGetShapepointsTimeDist(startLoc, endLoc, speedMPS, expDurationSec, verticalFirst=True): # if verticalFirst is true, it means go north/south first then go east/west if verticalFirst: path = [startLoc, [endLoc[0], startLoc[1]], endLoc] dist = [ 0, geoDistance2D(startLoc, [endLoc[0], startLoc[1]]), geoDistance2D([endLoc[0], startLoc[1]], endLoc) ] else: path = [startLoc, [startLoc[0], endLoc[1]], endLoc] dist = [ 0, geoDistance2D(startLoc, [startLoc[0], endLoc[1]]), geoDistance2D([startLoc[0], endLoc[1]], endLoc) ] if (expDurationSec != None): time = [ 0, expDurationSec * dist[1] / (dist[1] + dist[2]), expDurationSec * dist[2] / (dist[1] + dist[2]) ] else: time = [0, dist[1] / speedMPS, dist[2] / speedMPS] return [path, time, dist]
def _getTimeDistManhattan(fromLocs, toLocs, speedMPS): """ Generate two dictionaries, one for time, another for distance, using Manhattan Parameters ---------- fromLocs: list, Required The start node coordinates in format of [[lat, lon], [lat, lon], ... ] toLocs: list, Required The End node coordinates in format of [[lat, lon], [lat, lon], ... ] speedMPS: float, Required A constant speed for calculation returns ------- timeSecs: dictionary A dictionary for time from nodes to nodes, unit is in [seconds] distMeters: dictionary A dictionary for distance from nodes to nodes, unit is in [meters] """ distMeters = {} timeSecs = {} for i in range(len(fromLocs)): for j in range(len(toLocs)): fromLocsDict = locs2Dict(fromLocs) toLocsDict = locs2Dict(toLocs) manDist = geoDistance2D(fromLocs[i], ( fromLocsDict[i]['lat'], toLocsDict[j]['lon'])) + geoDistance2D( (fromLocsDict[i]['lat'], toLocsDict[j]['lon']), toLocs[j]) distMeters[i, j] = manDist timeSecs[i, j] = manDist / speedMPS return [timeSecs, distMeters]
def areaOfTriangle(loc1, loc2, loc3): """ Calculates the area of triangle defined by three locations Parameters ---------- loc1: list First location loc2: list Second location loc3: list Third location Return ------ float Area of triangle """ # Using Heron's Formula a = geoDistance2D(loc1, loc2) b = geoDistance2D(loc2, loc3) c = geoDistance2D(loc1, loc3) s = (a + b + c) / 2 area = math.sqrt(s * (s - a) * (s - b) * (s - c)) return area
def _genNodesRoadBased(numNodes=None, boundingRegion=None, distToRoad=None, dataProvider=None, dataProviderArgs=None): """ Generate randomized node using Uniform distribution within a bounding area and close to roads Note ---- This function is an approximation, the error is getting larger when the location is closer to poles Parameters ---------- numNodes: int, Required Number of nodes to be generated boudingArea: list, Required A defined polygon, nodes are generated within this area distToRoad: float, Required The maximun distance to road for generated nodes. dataProvider: string, Conditional, default as None Specifies the data source to be used for generating nodes on a road network. See :ref:`Data Providers` for options and requirements. dataProviderArgs: dictionary, Conditional, default as None For some data providers, additional parameters are required (e.g., API keys or database names). See :ref:`Data Providers` for the additional arguments required for each supported data provider. Returns ------- list of lists A list of coordinates, within a given distance to its nearest street """ # Initialize locs = [] holes = [] goodLocs = [] # Generate nodes, if it is not close enough, discard and regenerate while (len(locs) < numNodes): newLocs = _genNodesUniformBounded(numNodes - len(locs), boundingRegion) goodLocs = [] for i in range(len(newLocs)): goodFlag = True for j in range(len(holes)): if (geoDistance2D(newLocs[i], holes[j][0]) < holes[j][1]): goodFlag = False break if (goodFlag): goodLocs.append(newLocs[i]) if (len(goodLocs) > 0): snapLocs = privGetSnapLocBatch(goodLocs, dataProvider, dataProviderArgs) for i in range(len(snapLocs)): dist = geoDistance2D(goodLocs[i], snapLocs[i]) if (dist > distToRoad): holes.append([goodLocs[i], dist - distToRoad]) else: locs.append(goodLocs[i]) return locs
def _getTimeDistEuclidean2D(fromLocs, toLocs, speedMPS): """ Generate two dictionaries, one for time, another for distance, using euclidean (in 2D) Parameters ---------- fromLocs: list, Required The start node coordinates in format of [[lat, lon], [lat, lon], ... ] toLocs: list, Required The End node coordinates in format of [[lat, lon], [lat, lon], ... ] speedMPS: float, Required A constant speed for calculation returns ------- timeSecs: dictionary A dictionary for time from nodes to nodes, unit is in [seconds] distMeters: dictionary A dictionary for distance from nodes to nodes, unit is in [meters] """ distMeters = {} timeSecs = {} for i in range(len(fromLocs)): for j in range(len(toLocs)): eucDist = geoDistance2D(fromLocs[i], toLocs[j]) distMeters[i, j] = eucDist timeSecs[i, j] = eucDist / speedMPS return [timeSecs, distMeters]
def distributeTimeDist(path, totalTime): """ This function gives time stamps for a series of coordinates, given a start time and total time, used in shapepoint calculating. The distribution is made based on the euclidean distance between neigboring coordinates - they are very close to each other Parameters ---------- path: list of lists A path that consist a list of locations as shapepoints/waypoints, in the form of [[lat, lon], [lat, lon], ..., [lat, lon]], it will be considered as open polyline. totalTime: float Required, total time to be distributed Returns ------- timeSecs: list A list of time stamps, those time stamps are not accumulative in seconds. distMeters: list Distance between neighboring coordinates in meters. """ timeSecs = [] distMeters = [] totalDistMeters = 0 distMeters.append(0) timeSecs.append(0) for i in range(1, len(path)): d = geoDistance2D(path[i - 1], path[i]) distMeters.append(d) totalDistMeters += d for i in range(1, len(path)): timeSecs.append(totalTime * float(distMeters[i]) / float(totalDistMeters)) return [timeSecs, distMeters]
def _eucGetShapepointsTimeDist(startLoc, endLoc, speedMPS, expDurationSec): path = [startLoc, endLoc] dist = [0, geoDistance2D(startLoc, endLoc)] if (expDurationSec != None): time = [0, expDurationSec] else: time = [0, dist[1] / speedMPS] return [path, time, dist]
def _genNodesRoadBased(numNodes=None, boundingRegion=None, distToRoad=None, dataProvider=None, APIkey=None, databaseName=None): """ Generate randomized node using Uniform distribution within a bounding area and close to roads Note ---- This function is an approximation, the error is getting larger when the location is closer to poles Parameters ---------- numNodes: int, Required Number of nodes to be generated boudingArea: list, Required A defined polygon, nodes are generated within this area distToRoad: float, Required The maximun distance to road for generated nodes. dataProvider: string, Conditional, See :ref:`Dataprovider` Specifies the data source to be used for generating nodes on a road network. APIkey: string, Conditional, See :ref:`Dataprovider` Some data providers require an API key (which you'll need to register for). databaseName: string, Conditional, See :ref:`Dataprovider` If you are hosting a data provider on your local machine (e.g., pgRouting), you'll need to specify the name of the local database. Returns ------- list of lists A list of coordinates, within a given distance to its nearest street """ # Initialize locs = [] # Generate nodes, if it is not close enough, discard and regenerate while (len(locs) < numNodes): newLocs = _genNodesUniformBounded(numNodes - len(locs), boundingRegion) snapLocs = privGetSnapLocBatch(newLocs, dataProvider, APIkey, databaseName) for i in range(len(snapLocs)): if (geoDistance2D(newLocs[i], snapLocs[i]) <= distToRoad): locs.append(newLocs[i]) return locs
def pgrGetTimeDist(fromLocs, toLocs, databaseName): """ This function generated time and distance matrix using pgRouting Parameters ---------- fromLoc: list, Conditional Used in 'one2many' mode. To state the coordinate of the starting node locs: list of lists Used in 'all2all', 'one2many', 'many2one' modes. A list of coordinates, in the format of [[lat1, lon1], [lat2, lon2], ...] toLoc: list, Conditional Used in 'many2one' mode. To state the coordinate of the ending node databaseName: string If you are hosting a data provider on your local machine (e.g., pgRouting), you'll need to specify the name of the local database. Returns ------- timeSecs: dictionary The key of each item in this dictionary is in (coordID1, coordID2) format, the travelling time from first entry to second entry, the units are seconds distMeters: dictionary The key of each item in this dictionary is in (coordID1, coordID2) format, the travelling distance from first entry to second entry, the units are meters """ conn = psycopg2.connect("dbname='%s' user='******' host='%s' password='******'" % ( databaseName, VRV_SETTING_PGROUTING_USERNAME, VRV_SETTING_PGROUTING_HOST, VRV_SETTING_PGROUTING_PASSWORD)) conn.autocommit = True cur = conn.cursor() dummyClassID = 821 # Hard-coded number, no specific meaning # FIXME! For database security reason, we need to testify if class_id = 821 is not used in the original database sqlCommand = " select max(id) from ways_vertices_pgr;" cur.execute(sqlCommand) row = cur.fetchone() newlyInsertVidNum = int(row[0]) + 1 locs = fromLocs.copy() for i in range(len(toLocs)): try: locs.index(toLocs[i]) except ValueError: locs.append(toLocs[i]) startVidList = [] endVidList = [] for i in range(len(fromLocs)): startVidList.append(newlyInsertVidNum + locs.index(fromLocs[i])) for i in range(len(toLocs)): endVidList.append(newlyInsertVidNum + locs.index(toLocs[i])) for i in range(len(locs)): # Add dummy vertices street = pgrGetNearestStreet(locs[i], databaseName) snapLoc = pgrGetSnapToRoadLatLon(street['gid'], locs[i], databaseName) dicSnapLoc = loc2Dict(snapLoc) sqlCommand = " insert into ways_vertices_pgr (id, lon, lat) values (%s, %s, %s);" % ( newlyInsertVidNum + locs.index(locs[i]), dicSnapLoc['lon'], dicSnapLoc['lat']) cur.execute(sqlCommand) # Add four two road segments distSource2Snapped = geoDistance2D(street['sourceLoc'], snapLoc) distSnapped2Target = geoDistance2D(snapLoc, street['targetLoc']) ratio = distSource2Snapped / (distSource2Snapped + distSnapped2Target) dicSourceLoc = loc2Dict(street['sourceLoc']) dicTargetLoc = loc2Dict(street['targetLoc']) sqlCommand = " insert into ways (class_id, source, target, length_m, x1, y1, x2, y2, cost_s, reverse_cost_s) values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s);" % ( dummyClassID, street['source'], newlyInsertVidNum + locs.index(locs[i]), distSource2Snapped, dicSourceLoc['lon'], dicSourceLoc['lat'], dicSnapLoc['lon'], dicSnapLoc['lat'], street['cost_s'] * ratio, street['reverse_cost_s'] * ratio) cur.execute(sqlCommand) sqlCommand = " insert into ways (class_id, source, target, length_m, x1, y1, x2, y2, cost_s, reverse_cost_s) values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s);" % ( dummyClassID, newlyInsertVidNum + locs.index(locs[i]), street['target'], distSnapped2Target, dicSnapLoc['lon'], dicSnapLoc['lat'], dicTargetLoc['lon'], dicTargetLoc['lat'], street['cost_s'] * (1 - ratio), street['reverse_cost_s'] * (1 - ratio)) cur.execute(sqlCommand) sqlCommand = " select " sqlCommand += " start_vid as start_node, " sqlCommand += " end_vid as end_node, " sqlCommand += " sum(cost) as time, " sqlCommand += " sum(length_m) as distance " sqlCommand += " from (" sqlCommand += " select " sqlCommand += " a.*, " sqlCommand += " b.length_m" sqlCommand += " from pgr_dijkstra(" sqlCommand += " 'select gid as id, source, target, cost_s as cost, reverse_cost_s as reverse_cost from ways', " sqlCommand += " ARRAY%s, " % (startVidList) sqlCommand += " ARRAY%s, " % (endVidList) sqlCommand += " directed := true) a " sqlCommand += " left join " sqlCommand += " ways b " sqlCommand += " on " sqlCommand += " a.edge = b.gid " sqlCommand += " order by " sqlCommand += " a.path_seq" sqlCommand += " ) x " sqlCommand += " group by " sqlCommand += " start_vid, " sqlCommand += " end_vid;" cur.execute(sqlCommand) row = cur.fetchall() for i in range(len(startVidList)): sqlCommand = " delete from ways_vertices_pgr where id = %s;" % (startVidList[i]) cur.execute(sqlCommand) for i in range(len(endVidList)): sqlCommand = " delete from ways_vertices_pgr where id = %s;" % (endVidList[i]) cur.execute(sqlCommand) sqlCommand = " delete from ways where class_id = %s;" % (dummyClassID) cur.execute(sqlCommand) conn.close() rawDist = {} rawTime = {} for i in range(len(row)): rawTime[row[i][0], row[i][1]] = row[i][2] rawDist[row[i][0], row[i][1]] = row[i][3] distMeters = {} timeSecs = {} for i in range(len(fromLocs)): for j in range(len(toLocs)): try: distMeters[i, j] = rawDist[startVidList[i], endVidList[j]] except: distMeters[i, j] = 0 try: timeSecs[i, j] = rawTime[startVidList[i], endVidList[j]] except: timeSecs[i, j] = 0 return [timeSecs, distMeters]
def pgrGetShapepointsTimeDist(startLoc, endLoc, databaseName): """ A function to get a list of shapepoints from start coordinate to end coordinate. Parameters ---------- startLoc: list Start location, the format is [lat, lon] (altitude, above sea level, set to be 0) or [lat, lon, alt] endLat: float Required, latitude of end coordinate endLon: float Required, longitude of end coordinate databaseName: string, Require If you are hosting a data provider on your local machine (e.g., pgRouting), you'll need to specify the name of the local database. Returns ------- path: list of lists A list of coordinates in sequence that shape the route from startLoc to endLoc timeSecs: list time between current shapepoint and previous shapepoint, the first element should be 0 distMeters: list distance between current shapepoint and previous shapepoint, the first element should be 0 """ conn = psycopg2.connect("dbname='%s' user='******' host='%s' password='******'" % ( databaseName, VRV_SETTING_PGROUTING_USERNAME, VRV_SETTING_PGROUTING_HOST, VRV_SETTING_PGROUTING_PASSWORD)) conn.autocommit = True cur = conn.cursor() # Calculate the distance between snapped location and source/target of closest street for the START coordinate startStreet = pgrGetNearestStreet(startLoc, databaseName) snapStartLoc = pgrGetSnapToRoadLatLon(startStreet['gid'], startLoc, databaseName) dicSnapStartLoc = loc2Dict(snapStartLoc) distSnapStart2Source = geoDistance2D(snapStartLoc, startStreet['sourceLoc']) distSnapStart2Target = geoDistance2D(snapStartLoc, startStreet['targetLoc']) # Calculate the distance between snapped location and source/target of closest street for the END coordinate endStreet = pgrGetNearestStreet(endLoc, databaseName) snapEndLoc = pgrGetSnapToRoadLatLon(endStreet['gid'], endLoc, databaseName) dicSnapEndLoc = loc2Dict(snapEndLoc) distSnapEnd2Source = geoDistance2D(snapEndLoc, endStreet['sourceLoc']) distSnapEnd2Target = geoDistance2D(snapEndLoc, endStreet['targetLoc']) # Find the number of vertices in the pgRouting database sqlCommand = " select count(*) from ways_vertices_pgr;" cur.execute(sqlCommand) row = cur.fetchone() newlyInsertVidNum = int(row[0]) + 1 # Testify and find a dummyClassID to put temp vertices and segments dummyClassID = 821 # Hard-coded number, no specific meaning # FIXME! For database security reason, we need to testify if class_id = 821 is not used in the original database # insert the snapped location for START coordinate, and two segments from the coordinate to source/target of the closest street sqlCommand = " insert into ways_vertices_pgr (id, lon, lat) values (%s, %s, %s);" % ( newlyInsertVidNum, dicSnapStartLoc['lon'], dicSnapStartLoc['lat']) sqlCommand += " insert into ways (class_id, source, target, length_m, x1, y1, x2, y2, cost_s, reverse_cost_s) values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s);" % ( dummyClassID, newlyInsertVidNum, startStreet['target'], distSnapStart2Target, dicSnapStartLoc['lon'], dicSnapStartLoc['lat'], startStreet['targetLoc'][1], startStreet['targetLoc'][0], startStreet['cost_s'] * distSnapStart2Target / (distSnapStart2Target + distSnapStart2Source), startStreet['reverse_cost_s'] * distSnapStart2Target / (distSnapStart2Target + distSnapStart2Source)) sqlCommand += " insert into ways (class_id, source, target, length_m, x1, y1, x2, y2, cost_s, reverse_cost_s) values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s);" % ( dummyClassID, startStreet['source'], newlyInsertVidNum, distSnapStart2Source, startStreet['sourceLoc'][1], startStreet['sourceLoc'][0], dicSnapStartLoc['lon'], dicSnapStartLoc['lat'], startStreet['cost_s'] * distSnapStart2Source / (distSnapStart2Target + distSnapStart2Source), startStreet['reverse_cost_s'] * distSnapStart2Source / (distSnapStart2Target + distSnapStart2Source)) # insert the snapped location for END coordinate, and two segments from the coordinate to source/target of the closest street sqlCommand += " insert into ways_vertices_pgr (id, lon, lat) values (%s, %s, %s);" % ( newlyInsertVidNum + 1, dicSnapEndLoc['lon'], dicSnapEndLoc['lat']) sqlCommand += " insert into ways (class_id, source, target, length_m, x1, y1, x2, y2, cost_s, reverse_cost_s) values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s);" % ( dummyClassID, newlyInsertVidNum + 1, endStreet['target'], distSnapEnd2Target, dicSnapEndLoc['lon'], dicSnapEndLoc['lat'], endStreet['targetLoc'][1], endStreet['targetLoc'][0], endStreet['cost_s'] * distSnapEnd2Target / (distSnapEnd2Target + distSnapEnd2Source), endStreet['reverse_cost_s'] * distSnapEnd2Target / (distSnapEnd2Target + distSnapEnd2Source)) sqlCommand += " insert into ways (class_id, source, target, length_m, x1, y1, x2, y2, cost_s, reverse_cost_s) values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s);" % ( dummyClassID, endStreet['source'], newlyInsertVidNum + 1, distSnapEnd2Source, endStreet['sourceLoc'][1], endStreet['sourceLoc'][0], dicSnapEndLoc['lon'], dicSnapEndLoc['lat'], endStreet['cost_s'] * distSnapEnd2Source / (distSnapEnd2Target + distSnapEnd2Source), endStreet['reverse_cost_s'] * distSnapEnd2Source / (distSnapEnd2Target + distSnapEnd2Source)) # Do dijstra algorithm to find shortest path sqlCommand += " select b.gid as gid, b.y1 as lats1, b.x1 as lons1, b.y2 as lats2, b.x2 as lons2, a.cost as secs, b.length_m as dist " sqlCommand += " from " sqlCommand += " pgr_dijkstra(" sqlCommand += " 'select gid as id, source, target, cost_s as cost, reverse_cost_s as reverse_cost from ways'," sqlCommand += " %s," % (newlyInsertVidNum) sqlCommand += " %s," % (newlyInsertVidNum + 1) sqlCommand += " directed := true" sqlCommand += " ) a" sqlCommand += " left join" sqlCommand += " ways b" sqlCommand += " on a.edge = b.gid" sqlCommand += " order by a.path_seq" # Return the shapepoint result from dijstra algorithm cur.execute(sqlCommand) row = cur.fetchall() summary = pd.DataFrame(row, columns=['gid', 'lats1', 'lons1', 'lats2', 'lons2', 'secs', 'dist']) # Delete the temp data sqlCommand = " delete from ways_vertices_pgr where id = (%s);" % (newlyInsertVidNum) sqlCommand += " delete from ways_vertices_pgr where id = (%s);" % (newlyInsertVidNum + 1) sqlCommand += " delete from ways where class_id = %s;" % (dummyClassID) cur.execute(sqlCommand) # The last row is junk info, drop it summary.drop(summary.index[len(summary) - 1], inplace = True) # Sorting the coordinates so that they can be linked to each other lats1 = summary['lats1'].tolist() lons1 = summary['lons1'].tolist() lats2 = summary['lats2'].tolist() lons2 = summary['lons2'].tolist() path = [] path.append(startLoc) for i in range(1, len(lats1)): if (lats1[i] != lats1[i - 1] and lats1[i] != lats2[i - 1]): path.append([lats1[i], lons1[i]]) else: path.append([lats2[i], lons2[i]]) timeSecs = summary['secs'].tolist() distMeters = summary['dist'].tolist() conn.close() return [path, timeSecs, distMeters]
def privGetShapepoints2D( odID=1, objectID=None, modelFile=None, startLoc=None, endLoc=None, startTimeSec=0.0, expDurationSec=None, routeType='euclidean2D', speedMPS=None, leafletColor=config['VRV_DEFAULT_LEAFLETARCCOLOR'], leafletWeight=config['VRV_DEFAULT_LEAFLETARCWEIGHT'], leafletStyle=config['VRV_DEFAULT_LEAFLETARCSTYLE'], leafletOpacity=config['VRV_DEFAULT_LEAFLETARCOPACITY'], leafletCurveType=config['VRV_DEFAULT_ARCCURVETYPE'], leafletCurvature=config['VRV_DEFAULT_ARCCURVATURE'], useArrows=True, modelScale=config['VRV_DEFAULT_CESIUMMODELSCALE'], modelMinPxSize=config['VRV_DEFAULT_CESIUMMODELMINPXSIZE'], cesiumColor=config['VRV_DEFAULT_CESIUMPATHCOLOR'], cesiumWeight=config['VRV_DEFAULT_CESIUMPATHWEIGHT'], cesiumStyle=config['VRV_DEFAULT_CESIUMPATHSTYLE'], cesiumOpacity=config['VRV_DEFAULT_CESIUMPATHOPACITY'], ganttColor=config['VRV_DEFAULT_GANTTCOLOR'], popupText=None, dataProvider=None, dataProviderArgs=None): # Replace backslash modelFile = replaceBackslashToSlash(modelFile) # Ensure leading slash modelFile = addHeadSlash(modelFile) try: dataProvider = dataProvider.lower() except: pass try: routeType = routeType.lower() except: pass if (startLoc != endLoc): extras = {} if (routeType == 'euclidean2d'): [path, time, dist] = _eucGetShapepointsTimeDist(startLoc, endLoc, speedMPS, expDurationSec) elif (routeType == 'manhattan'): [path, time, dist] = _manGetShapepointsTimeDist(startLoc, endLoc, speedMPS, expDurationSec) elif (routeType == 'fastest' and dataProviderDictionary[dataProvider] == 'pgrouting'): databaseName = dataProviderArgs['databaseName'] [path, time, dist] = pgrGetShapepointsTimeDist(startLoc, endLoc, databaseName) elif (routeType == 'fastest' and dataProviderDictionary[dataProvider] == 'osrm-online'): [path, time, dist] = osrmGetShapepointsTimeDist(startLoc, endLoc) elif (routeType in ['fastest', 'shortest', 'pedestrian'] and dataProviderDictionary[dataProvider] == 'mapquest'): APIkey = dataProviderArgs['APIkey'] [path, time, dist] = mqGetShapepointsTimeDist(startLoc, endLoc, routeType, APIkey) elif (routeType in ['fastest', 'pedestrian', 'cycling', 'truck', 'wheelchair'] and dataProviderDictionary[dataProvider] == 'ors-online'): APIkey = dataProviderArgs['APIkey'] if ('requestExtras' in dataProviderArgs.keys()): requestExtras = dataProviderArgs['requestExtras'] else: requestExtras = True [path, extras, time, dist] = orsGetShapepointsTimeDist(startLoc, endLoc, routeType, APIkey, requestExtras) elif (routeType in ['fastest', 'pedestrian', 'cycling', 'truck'] and dataProviderDictionary[dataProvider] == 'ors-local'): port = dataProviderArgs['port'] if ('requestExtras' in dataProviderArgs.keys()): requestExtras = dataProviderArgs['requestExtras'] else: requestExtras = True [path, extras, time, dist] = orsLocalGetShapepointsTimeDist(startLoc, endLoc, routeType, port, requestExtras) else: return # Check if the original point is too far away from the actual start point of the shapepoints from query distOri = geoDistance2D(startLoc, path[0]) if (distOri >= config['VRV_DEFAULT_DISTANCE_ERROR_TOLERANCE'] ): # Go back to 10m after testing print( "Message: The origin point (lat: %s, lon: %s) is %.1f meters away from the road. You might find a gap between the origin point and the route." % (startLoc[0], startLoc[1], distOri)) # Check if the actual end point is too far away from destination point distDes = geoDistance2D(path[-1], endLoc) if (distDes >= config['VRV_DEFAULT_DISTANCE_ERROR_TOLERANCE'] ): # Go back to 10m after testing print( "Message: The destination point (lat: %s, lon: %s) is %.1f meters away from the road. You might find a gap between destination point and the route." % (endLoc[0], endLoc[1], distDes)) # convert distance to accumulated distance accDist = [] accDist.append(0) for i in range(1, len(dist)): accDist.append(accDist[i - 1] + dist[i]) # If `expDurationSec` is provided, override `speedMPS` and datasource, otherwise, if `speedMPS` is provided, override datasource if (expDurationSec != None): [newTime, newDist] = distributeTimeDist(path, expDurationSec) time = newTime dist = newDist elif (speedMPS != None and expDurationSec == None): newExpDurationSec = accDist[len(accDist) - 1] / speedMPS [newTime, newDist] = distributeTimeDist(path, newExpDurationSec) time = newTime dist = newDist # convert time to accumulated time accTime = [] accTime.append(startTimeSec) for i in range(1, len(dist)): accTime.append(accTime[i - 1] + time[i]) # For maintainability, convert locs into dictionary dicPath = locs2Dict(path) # shapepoint dataframe assignments = privInitDataframe('Assignments') # generate assignments for i in range(1, len(path)): if (i - 1) in extras: startElev = extras[i - 1]['elev'] if 'elev' in extras[i - 1] else None wayname = extras[i]['wayname'] if 'wayname' in extras[ i] else None waycategory = extras[i][ 'waycategory'] if 'waycategory' in extras[i] else None surface = extras[i]['surface'] if 'surface' in extras[ i] else None waytype = extras[i]['waytype'] if 'waytype' in extras[ i] else None steepness = extras[i]['steepness'] if 'steepness' in extras[ i] else None tollway = extras[i]['tollway'] if 'tollway' in extras[ i] else None else: startElev = None wayname = None waycategory = None surface = None waytype = None steepness = None tollway = None if i in extras: endElev = extras[i]['elev'] if 'elev' in extras[i] else None else: endElev = None assignments = assignments.append( { 'odID': odID, 'objectID': objectID, 'modelFile': modelFile, 'startTimeSec': accTime[i - 1], 'startLat': dicPath[i - 1]['lat'], 'startLon': dicPath[i - 1]['lon'], 'startAltMeters': dicPath[i - 1]['alt'], 'endTimeSec': accTime[i], 'endLat': dicPath[i]['lat'], 'endLon': dicPath[i]['lon'], 'endAltMeters': dicPath[i]['alt'], 'leafletColor': leafletColor, 'leafletWeight': leafletWeight, 'leafletStyle': leafletStyle, 'leafletCurveType': leafletCurveType, 'leafletCurvature': leafletCurvature, 'useArrows': useArrows, 'leafletOpacity': leafletOpacity, 'modelScale': modelScale, 'modelMinPxSize': modelMinPxSize, 'cesiumColor': stripCesiumColor(cesiumColor), 'cesiumWeight': cesiumWeight, 'cesiumStyle': cesiumStyle, 'cesiumOpacity': cesiumOpacity, 'ganttColor': ganttColor, 'popupText': popupText, 'startElevMeters': startElev, 'endElevMeters': endElev, 'wayname': wayname, 'waycategory': waycategory, 'surface': surface, 'waytype': waytype, 'steepness': steepness, 'tollway': tollway }, ignore_index=True) else: # For maintainability, convert locs into dictionary dicStartLoc = loc2Dict(startLoc) if (dataProviderDictionary[dataProvider] == 'ors-online'): [[lat, lon, elev]] = orsGetElevation([startLoc], dataProviderArgs['APIkey']) else: elev = None assignments = privInitDataframe('Assignments') assignments = assignments.append( { 'odID': odID, 'objectID': objectID, 'modelFile': modelFile, 'startTimeSec': startTimeSec, 'startLat': dicStartLoc['lat'], 'startLon': dicStartLoc['lon'], 'startAltMeters': dicStartLoc['alt'], 'endTimeSec': expDurationSec + startTimeSec if (expDurationSec is not None) else startTimeSec, 'endLat': dicStartLoc['lat'], 'endLon': dicStartLoc['lon'], 'endAltMeters': dicStartLoc['alt'], 'leafletColor': leafletColor, 'leafletWeight': leafletWeight, 'leafletStyle': leafletStyle, 'leafletCurveType': leafletCurveType, 'leafletCurvature': leafletCurvature, 'useArrows': useArrows, 'leafletOpacity': leafletOpacity, 'modelScale': modelScale, 'modelMinPxSize': modelMinPxSize, 'cesiumColor': stripCesiumColor(cesiumColor), 'cesiumWeight': cesiumWeight, 'cesiumStyle': cesiumStyle, 'cesiumOpacity': cesiumOpacity, 'ganttColor': ganttColor, 'popupText': popupText, 'startElevMeters': elev, 'endElevMeters': elev, 'wayname': None, 'waycategory': None, 'surface': None, 'waytype': None, 'steepness': None, 'tollway': None }, ignore_index=True) return assignments
def privGetShapepoints2D(odID=1, objectID=None, modelFile=None, startLoc=None, endLoc=None, startTimeSec=0.0, expDurationSec=None, routeType='euclidean2D', speedMPS=None, leafletColor=VRV_DEFAULT_LEAFLETARCCOLOR, leafletWeight=VRV_DEFAULT_LEAFLETARCWEIGHT, leafletStyle=VRV_DEFAULT_LEAFLETARCSTYLE, leafletOpacity=VRV_DEFAULT_LEAFLETARCOPACITY, useArrows=True, modelScale=VRV_DEFAULT_CESIUMMODELSCALE, modelMinPxSize=VRV_DEFAULT_CESIUMMODELMINPXSIZE, cesiumColor=VRV_DEFAULT_CESIUMPATHCOLOR, cesiumWeight=VRV_DEFAULT_CESIUMPATHWEIGHT, cesiumStyle=VRV_DEFAULT_CESIUMPATHSTYLE, cesiumOpacity=VRV_DEFAULT_CESIUMPATHOPACITY, dataProvider=None, dataProviderArgs=None): # Replace backslash modelFile = replaceBackslashToSlash(modelFile) try: dataProvider = dataProvider.lower() except: pass try: routeType = routeType.lower() except: pass if (startLoc != endLoc): if (routeType == 'euclidean2d'): [path, time, dist] = _eucGetShapepointsTimeDist(startLoc, endLoc, speedMPS, expDurationSec) elif (routeType == 'manhattan'): [path, time, dist] = _manGetShapepointsTimeDist(startLoc, endLoc, speedMPS, expDurationSec) elif (routeType == 'fastest' and dataProviderDictionary[dataProvider] == 'pgrouting'): databaseName = dataProviderArgs['databaseName'] [path, time, dist] = pgrGetShapepointsTimeDist(startLoc, endLoc, databaseName) elif (routeType == 'fastest' and dataProviderDictionary[dataProvider] == 'osrm-online'): [path, time, dist] = osrmGetShapepointsTimeDist(startLoc, endLoc) elif (routeType in ['fastest', 'shortest', 'pedestrian'] and dataProviderDictionary[dataProvider] == 'mapquest'): APIkey = dataProviderArgs['APIkey'] [path, time, dist] = mqGetShapepointsTimeDist(startLoc, endLoc, routeType, APIkey) elif (routeType in ['fastest', 'pedestrian', 'cycling', 'truck'] and dataProviderDictionary[dataProvider] == 'ors-online'): APIkey = dataProviderArgs['APIkey'] [path, time, dist] = orsGetShapepointsTimeDist(startLoc, endLoc, routeType, APIkey) else: return # Check if the original point is too far away from the actual start point of the shapepoints from query distOri = geoDistance2D(startLoc, path[0]) if (distOri >= VRV_DEFAULT_DISTANCE_ERROR_TOLERANCE ): # Go back to 10m after testing print( "Message: The origin point (lat: %s, lon: %s) is %.1f meters away from the road. You might find a gap between the origin point and the route." % (startLoc[0], startLoc[1], distOri)) # Check if the actual end point is too far away from destination point distDes = geoDistance2D(path[-1], endLoc) if (distDes >= VRV_DEFAULT_DISTANCE_ERROR_TOLERANCE ): # Go back to 10m after testing print( "Message: The destination point (lat: %s, lon: %s) is %.1f meters away from the road. You might find a gap between destination point and the route." % (endLoc[0], endLoc[1], distDes)) # convert distance to accumulated distance accDist = [] accDist.append(0) for i in range(1, len(dist)): accDist.append(accDist[i - 1] + dist[i]) # If `expDurationSec` is provided, override `speedMPS` and datasource, otherwise, if `speedMPS` is provided, override datasource if (expDurationSec != None): [newTime, newDist] = distributeTimeDist(path, expDurationSec) time = newTime dist = newDist elif (speedMPS != None and expDurationSec == None): newExpDurationSec = accDist[len(accDist) - 1] / speedMPS [newTime, newDist] = distributeTimeDist(path, newExpDurationSec) time = newTime dist = newDist # convert time to accumulated time accTime = [] accTime.append(startTimeSec) for i in range(1, len(dist)): accTime.append(accTime[i - 1] + time[i]) # For maintainability, convert locs into dictionary dicPath = locs2Dict(path) # shapepoint dataframe assignments = initDataframe('Assignments') # generate assignments for i in range(1, len(path)): assignments = assignments.append( { 'odID': odID, 'objectID': objectID, 'modelFile': modelFile, 'startTimeSec': accTime[i - 1], 'startLat': dicPath[i - 1]['lat'], 'startLon': dicPath[i - 1]['lon'], 'startAltMeters': dicPath[i - 1]['alt'], 'endTimeSec': accTime[i], 'endLat': dicPath[i]['lat'], 'endLon': dicPath[i]['lon'], 'endAltMeters': dicPath[i]['alt'], 'leafletColor': leafletColor, 'leafletWeight': leafletWeight, 'leafletStyle': leafletStyle, 'useArrows': useArrows, 'leafletOpacity': leafletOpacity, 'modelScale': modelScale, 'modelMinPxSize': modelMinPxSize, 'cesiumColor': cesiumColor, 'cesiumWeight': cesiumWeight, 'cesiumStyle': cesiumStyle, 'cesiumOpacity': cesiumOpacity }, ignore_index=True) else: # For maintainability, convert locs into dictionary dicStartLoc = loc2Dict(startLoc) assignments = initDataframe('Assignments') assignments = assignments.append( { 'odID': odID, 'objectID': objectID, 'modelFile': modelFile, 'startTimeSec': startTimeSec, 'startLat': dicStartLoc['lat'], 'startLon': dicStartLoc['lon'], 'startAltMeters': dicStartLoc['alt'], 'endTimeSec': expDurationSec + startTimeSec if (expDurationSec is not None) else startTimeSec, 'endLat': dicStartLoc['lat'], 'endLon': dicStartLoc['lon'], 'endAltMeters': dicStartLoc['alt'], 'leafletColor': leafletColor, 'leafletWeight': leafletWeight, 'leafletStyle': leafletStyle, 'useArrows': useArrows, 'leafletOpacity': leafletOpacity, 'modelScale': modelScale, 'modelMinPxSize': modelMinPxSize, 'cesiumColor': cesiumColor, 'cesiumWeight': cesiumWeight, 'cesiumStyle': cesiumStyle, 'cesiumOpacity': cesiumOpacity }, ignore_index=True) return assignments
def _buildFlightPath(path, speedMPS): """ Since _buildFlightProfile is not very flexible, this function gives a more customized method to generate profile, by listing lists of lats, lons and alts. Parameters ---------- path: list of lists A list of locations along with the flight path, in the format of [lat, lon] (altitude, above sea level, set to be 0) or [lat, lon, alt]. speedMPS: float A constant speed, during this path the vehicle will cruise in this speed. Return ------ flight dataframe A dataframe to be interpreted into assignments dataframe. """ # Flight Profile dataframe flight = pd.DataFrame(columns=[ 'lat', 'lon', 'altAGL', 'accuGroundDistance', 'description', 'loiterTime', 'groundDistance', 'flightDistance', 'accuFlightDistance', 'timeFromPreviousPosition', 'pathStartTimeSec', 'pathEndTimeSec' ]) # Check and guarantee that each point in path has 3 dimension dicPath = locs2Dict(path) # Add flight path one coordinate at a time accuGroundDistance = 0 accuFlightDistance = 0 accuPathTime = 0 for i in range(len(path)): # Calculate fields for flight dataframe if i > 0: groundDistance = geoDistance2D(path[i - 1], path[i]) deltaAGL = dicPath[i]['alt'] - dicPath[i - 1]['alt'] flightDistance = math.sqrt(groundDistance * groundDistance + deltaAGL * deltaAGL) else: groundDistance = 0 flightDistance = 0 accuGroundDistance += groundDistance accuFlightDistance += flightDistance timeFromPreviousPosition = accuFlightDistance / speedMPS accuPathTime += timeFromPreviousPosition # And one way point to the flight path flight = flight.append( { 'lat': dicPath[i]['lat'], 'lon': dicPath[i]['lon'], 'altAGL': dicPath[i]['alt'], 'accuGroundDistance': accuGroundDistance, 'description': "Waypoint", 'loiterTime': 0.0, 'groundDistance': groundDistance, 'flightDistance': flightDistance, 'accuFlightDistance': accuFlightDistance, 'timeFromPreviousPosition': accuFlightDistance / speedMPS, 'pathStartTimeSec': accuPathTime, 'pathEndTimeSec': accuPathTime }, ignore_index=True) return flight
def _buildFlightProfile(startLoc, cruiseAltMetersAGL, endLoc, takeoffSpeedMPS, rateOfClimbMPS, cruiseSpeedMPS, landSpeedMPS, rateOfDescentMPS): """ Build a flight profile, by profile, it means it has take_off/cruise/loiter(mission)/landing phase, if we want to construct a customized profile, use _buildFlightPath Parameters ---------- startLoc: list Start location, the format is [lat, lon] (altitude, above sea level, set to be 0) or [lat, lon, alt]. cruiseAltMetersAGL: float Cruise altitude, meters above sea level. endLoc: list End location, the format is [lat, lon] (altitude, above sea level, set to be 0) or [lat, lon, alt]. rateOfClimbMPS: float Rate of climb of the aircraft, it is a vertical speed. climbGradientInDegree: float Climb gradient, the unit is degree, horizontal as zero, minimal value as 0, maximum value as 90 (vertical up). cruiseSpeedMPS: float Speed of cruise, in the unit of meters/second. rateOfDescentMPS: float Rate of descent, vertical speed. descentGradientInDegree: float Descent gradient, the unit is degree, horizontal as zero, minimal value as 0, maximum value as 90 (vertical down). Return ------ flight dataframe A dataframe to be interpreted into assignments dataframe """ # Interpret locations into readable dictionary dicStartLoc = loc2Dict(startLoc) dicEndLoc = loc2Dict(endLoc) # Calculate gradients of climbing and landing climbGradientInDegree = math.degrees( math.asin(rateOfClimbMPS / takeoffSpeedMPS)) descentGradientInDegree = math.degrees( math.asin(rateOfDescentMPS / landSpeedMPS)) # calculate the ideal takeoff/landing time/ground distance idealTakeoffTimeSec = (cruiseAltMetersAGL - dicStartLoc['alt']) / rateOfClimbMPS idealTakeoffGroundDistance = (cruiseAltMetersAGL - dicStartLoc['alt']) / math.tan( math.radians(climbGradientInDegree)) idealLandingTimeSec = (cruiseAltMetersAGL - dicEndLoc['alt']) / rateOfDescentMPS idealLandingGroundDistance = (cruiseAltMetersAGL - dicEndLoc['alt']) / math.tan( math.radians(descentGradientInDegree)) # including start and end, takeoffAt and arrivalAt are not included in here markPath = [startLoc, endLoc] # Total ground distance totalGroundDistance = geoDistancePath2D(markPath) # Flight Profile dataframe flight = pd.DataFrame(columns=[ 'lat', 'lon', 'altAGL', 'accuGroundDistance', 'description', 'loiterTime' ]) # For the first location, add one row flight = flight.append( { 'lat': dicStartLoc['lat'], 'lon': dicStartLoc['lon'], 'altAGL': dicStartLoc['alt'], 'accuGroundDistance': 0.0, 'description': "beforeTakeoff", 'loiterTime': 0.0 }, ignore_index=True) # Check if distance is enough for taking off and landing if (totalGroundDistance > idealTakeoffGroundDistance + idealLandingGroundDistance): # if can reach cruise altitude, everything is ideal takeoffMileage = geoMileageInPath2D(markPath, idealTakeoffGroundDistance) landingMileage = geoMileageInPath2D( markPath, totalGroundDistance - idealLandingGroundDistance) # if can cruise, it means we need two locations flight = flight.append( { 'lat': takeoffMileage['loc'][0], 'lon': takeoffMileage['loc'][1], 'altAGL': cruiseAltMetersAGL, 'accuGroundDistance': idealTakeoffGroundDistance, 'description': "takeoffAtAlt", 'loiterTime': 0.0 }, ignore_index=True) flight = flight.append( { 'lat': landingMileage['loc'][0], 'lon': landingMileage['loc'][1], 'altAGL': cruiseAltMetersAGL, 'accuGroundDistance': totalGroundDistance - idealLandingGroundDistance, 'description': "arrivalAtAlt", 'loiterTime': 0.0 }, ignore_index=True) else: # if can not reach cruise altitude, the profile is "triangle", i.e. the takeoffAt position are the same as arrivalAt position deltaAGLTakeoffLanding = dicStartLoc['alt'] - dicEndLoc['alt'] deltaAGLCruiseTakeoff = ( (totalGroundDistance - deltaAGLTakeoffLanding / math.tan(math.radians(descentGradientInDegree))) * (math.tan(math.radians(climbGradientInDegree)) + math.tan(math.radians(descentGradientInDegree)))) deltaAGLCruiseLanding = deltaAGLCruiseTakeoff + deltaAGLTakeoffLanding takeoffGroundDistance = deltaAGLCruiseTakeoff / math.tan( math.radians(climbGradientInDegree)) landingGroundDistance = deltaAGLCruiseLanding / math.tan( math.radians(descentGradientInDegree)) takeoffMileage = geoMileageInPath2D(markPath, takeoffGroundDistance) flight = flight.append( { 'lat': takeoffMileage['loc'][0], 'lon': takeoffMileage['loc'][1], 'altAGL': deltaAGLCruiseTakeoff + dicStartLoc['alt'], 'accuGroundDistance': takeoffGroundDistance, 'description': "takeoffAtAlt and arrivalAtAlt", 'loiterTime': 0.0 }, ignore_index=True) # For the last location, add one row flight = flight.append( { 'lat': dicEndLoc['lat'], 'lon': dicEndLoc['lon'], 'altAGL': dicEndLoc['alt'], 'accuGroundDistance': totalGroundDistance, 'description': "afterArrival", 'loiterTime': 0.0 }, ignore_index=True) # Reorder flight in order flight = flight.sort_values('accuGroundDistance', ascending=True) flight = flight.reset_index(drop=True) # Add the 'groundDistance' column to flight dataframe groundDistance = [0.0] for i in range(1, len(flight)): groundDistance.append( geoDistance2D( (flight.iloc[i]['lat'], flight.iloc[i]['lon']), (flight.iloc[i - 1]['lat'], flight.iloc[i - 1]['lon']))) flight['groundDistance'] = groundDistance # Add the 'flightDistance' column to flight dataframe flightDistance = [0.0] for i in range(1, len(flight)): deltaHeight = flight.iloc[i]['altAGL'] - flight.iloc[i - 1]['altAGL'] groundDistance = flight.iloc[i]['groundDistance'] flightDistance.append( math.sqrt(deltaHeight * deltaHeight + groundDistance * groundDistance)) flight['flightDistance'] = flightDistance # Add the 'accuFlightDistance' column to flight dataframe accuFlightDistance = [0.0] for i in range(1, len(flight)): accuFlightDistance.append(accuFlightDistance[i - 1] + flight.iloc[i]['flightDistance']) flight['accuFlightDistance'] = accuFlightDistance # Add the 'time' column to flight dataframe duration = [0.0] for i in range(1, len(flight)): if (flight.iloc[i]['description'] == "takeoffAtAlt" or flight.iloc[i]['description'] == "takeoffAtAlt and arrivalAtAlt"): speed = takeoffSpeedMPS duration.append(flight.iloc[i]['flightDistance'] / speed) elif (flight.iloc[i]['description'] == "arrivalAtAlt"): speed = cruiseSpeedMPS duration.append(flight.iloc[i]['flightDistance'] / speed) elif (flight.iloc[i]['description'] == "afterArrival"): speed = landSpeedMPS duration.append(flight.iloc[i]['flightDistance'] / speed) flight['timeFromPreviousPosition'] = duration # Add the 'accuTime' column to flight dataframe startTimeSec = [] endTimeSec = [] startTimeSec.append(0.0) endTimeSec.append(flight.iloc[0]['loiterTime']) for i in range(1, len(flight)): startTimeSec.append(endTimeSec[i - 1] + flight.iloc[i]['timeFromPreviousPosition']) endTimeSec.append(endTimeSec[i - 1] + flight.iloc[i]['timeFromPreviousPosition'] + flight.iloc[i]['loiterTime']) flight['pathStartTimeSec'] = startTimeSec flight['pathEndTimeSec'] = endTimeSec return flight