How to add Point to the LineString? - python

I am working on vessel tracking. I have setup route path, and vessel current position. Now my aim is to add that current position point to the path and update it. and calculate the length of linestring and time duration of whole line(start to end) and remain line(from current position to end).
Now my main question is how can add the point to the LineString in order to update the route?
Below, I have provided the my input linestring and current point data. also output of line and point.
Data:
p = {'route': [{'path': [[51.51309, 0.4836599999999862],
[51.512222970577746, 0.4838196951935174],
[51.50817683327391, 0.4845649394298732],
.
.
[26.160997649670072, 56.29158290493038],
[25.234004681463745, 55.29187512793891],
[25.035363331133816, 55.07765203286928],
[25.00255, 55.10811000000001]],
'type': 'SEA'}]}
l_path=p['route'][0]['path']
path = LineString(l_path)
I want to connect this point to the linestring:
position = Point(13.752724664396988, 56.42578125)
Line and point output:
Thanks in advance.

I have update the path my connecting with the point but still not getting the desire output. Because, using below code vessel taking long path which is causing me a issue in with wrong length and time.
from shapely.geometry import LineString
from shapely.ops import transform
from functools import partial
import pyproj
from shapely.ops import split
from itertools import chain
total_path=p['route'][0]['path'][1:-1] ##### Total path
total_line = LineString(total_path)
project = partial(
pyproj.transform,
pyproj.Proj('EPSG:4326'),
pyproj.Proj('EPSG:32633'))
total_ls = transform(project, total_line)
total_ls
total_distance = total_ls.length/1852 ######Tortal distance in NM
print(total_distance, "Distance")
current_pin = (13.752724664396988, 56.42578125)
position = Point(current_pin)
all_points_coords = chain(total_line.coords,position.coords)
all_points = map(Point, all_points_coords)
new_line = LineString(sorted(all_points, key=total_line.project))
new_ls = transform(project, new_line)
new_distance = new_ls.length/1852
print(new_distance,"New distance")
new_ls
## remain distance from current position
remain = LineString(new_line.coords[new_line.coords[:].index(current_pin):])
remain = transform(project, remain)
r_distance = remain.length/1852
print(r_distance,"remain distance")
remain

Related

Find coordinate of point on a straight line between two points?

How can I code a script in python for the following problem?
I have starting and ending coordinates of a straight line.
I want to find coordinate of a point with a given distance from the starting point.
May be this might help you. I had converted the coordinates into polar coordinates,
import pandas as pd
import re
df = pd.DataFrame({"Start_Northing": ["41°10'23"],"Start_Easting":["61°26'24"],"End_Northing":["41°10'42"],
"End_Easting": ["61°27'00"]})
def dms2dd(s):
degrees, minutes, seconds = re.split('[°\'"]+', s)
dd = float(degrees) + float(minutes)/60 + float(seconds)/(60*60)
return dd
for column in df.columns:
df['New_'+ str(column)] = df[column].apply(dms2dd)
df['point Easting'] = df[["New_End_Northing", "New_End_Easting"]].apply(tuple, axis=1)
df["point Northing'"] = df[["New_Start_Northing", "New_Start_Easting"]].apply(tuple, axis=1)
df = df.drop(['New_Start_Northing','New_Start_Easting','New_End_Northing','New_End_Easting'],axis=1)
print(df.to_string())
The result is,
Start_Northing Start_Easting End_Northing End_Easting point Easting point Northing'
0 41°10'23 61°26'24 41°10'42 61°27'00 (41.17833333333333, 61.45) (41.17305555555555, 61.44)

How to find which Lines and Arcs are connected when imported from DXF?

I am using ezdxf to import a .dxf file into Python. It works smoothly. I get a list of lines and arcs.
How do I figure out which lines and arcs are connected to each other? Are they somehow indexed or do I need to search start and end points of lines and arcs and match them afterwards?
What I need to find are the closed lines in the .dxf file.
You have to match line and arc end points manually.
Get the end points of arcs with default extrusion (0, 0, 1):
from ezdxf.math import Vector
start_point = Vector.from_deg_angle(arc.dxf.start_angle, arc.dxf.radius)
end_point = Vector.from_deg_angle(arc.dxf.end_angle, arc.dxf.radius)
Add to arc center:
s = start_point + arc.dxf.center
e = end_point + arc.dxf.center
If the Object Coordinate System (OCS) defined by arc.dxf.extrusion is different from (0, 0, 1), a transformation to the World Coordinate System (WCS) has to be done:
ocs = arc.ocs()
s = ocs.to_wcs(s)
e = ocs.to_wcs(e)
Next ezdxf version v0.11 will have Arc.start_point and Arc.end_point properties, which will return the WCS coordinates.
Important: Don't compare with the equal operator (==), use math.isclose() or better Vector.isclose() like:
s.isclose(line.dxf.start, abs_tol=1e-6)
e.isclose(line.dxf.start, abs_tol=1e-6)
s.isclose(line.dxf.end, abs_tol=1e-6)
e.isclose(line.dxf.end, abs_tol=1e-6)
Set absolute tolerance according to your needs.

Getting (some) points of a polygon

I am trying to grab the vertices from a polygon and do some stuff to them to recreate the polygon in a new location/rotation (essentially this: https://community.esri.com/thread/46497). The example code below is not exactly what I am doing, but showcases the issue. The code would work except that after it grabs the last vertex of the polygon, it throws an error message which breaks the script and stops everything else from running to draw the new polygon. Otherwise, if I go through my code line-by-line I can continue on and create the new polygon feature:
AttributeError: 'NoneType' object has no attribute 'X'
Is there a way that I can use the loop to run through all except the "last" vertex, which either has an issue or doesn't exist?
import arcpy
import os
import random
import math
pa = 'protected_areas' # protected areas
sr = arcpy.Describe(pa).spatialReference # spatial ref
sa = 'study_area' # study area
x = [] # placeholder
with arcpy.da.SearchCursor(pa,'SHAPE#',spatial_reference=sr) as cursor: # for each polygon
for row in cursor:
centroid = row[0].centroid # calculate centroid
poly = row[0]
for part in poly: # for each polygon part
for pnt in part: # for each vertex
x.append(pnt.X)
You can iterate over an index and skip the las element, changing
for pnt in part: # for each vertex
x.append(pnt.X)
To
for k in range(len(pnt)-1): # for each vertex
x.append(pnt[k].X)
Hope it helps

Fast way to find the closest polygon to a point

I have a list of Shapely polygons and a point like so:
from shapely.geometry import Point, Polygon
polygons = [Polygon(...), Polygon(...), ...]
point = Point(2.5, 5.7)
and I want to find the closest polygon in the list to that point. I'm already aware of the object.distance(other) function which returns the minimum distance between two geometric shapes, and I thought about computing all the distances in a loop to find the closest polygon:
polygons = [Polygon(...), Polygon(...), ...]
point = Point(2.5, 5.7)
min_dist = 10000
closest_polygon = None
for polygon in polygons:
dist = polygon.distance(point)
if dist < min_dist:
min_dist = dist
closest_polygon = polygon
My question is: Is there a more efficient way to do it?
There is a shorter way, e.g.
from shapely.geometry import Point, Polygon
import random
from operator import itemgetter
def random_coords(n):
return [(random.randint(0, 100), random.randint(0, 100)) for _ in range(n)]
polys = [Polygon(random_coords(3)) for _ in range(4)]
point = Point(random_coords(1))
min_distance, min_poly = min(((poly.distance(point), poly) for poly in polys), key=itemgetter(0))
as Georgy mentioned (++awesome!) even more concise:
min_poly = min(polys, key=point.distance)
but distance computation is in general computationally intensive
I have a solution that works if you have at least 2 polygons with a distance different from 0. Let's call these 2 polygons "basePolygon0" and "basePolygon1". The idea is to build a KD tree with the distance of each polygon to each of the "basis" polygons.
Once the KD tree has been built, we can query it by computing the distance to each of the basis polygons.
Here's a working example:
from shapely.geometry import Point, Polygon
import numpy as np
from scipy.spatial import KDTree
# prepare a test with triangles
poly0 = Polygon([(3,-1),(5,-1),(4,2)])
poly1 = Polygon([(-2,1),(-4,2),(-3,4)])
poly2 = Polygon([(-3,-3),(-4,-6),(-2,-6)])
poly3 = Polygon([(-1,-4),(1,-4),(0,-1)])
polys = [poly0,poly1,poly2,poly3]
p0 = Point(4,-3)
p1 = Point(-4,1)
p2 = Point(-4,-2)
p3 = Point(0,-2.5)
testPoints = [p0,p1,p2,p3]
# select basis polygons
# it works with any pair of polygons that have non zero distance
basePolygon0 = polys[0]
basePolygon1 = polys[1]
# compute tree query
def buildQuery(point):
distToBasePolygon0 = basePolygon0.distance(point)
distToBasePolygon1 = basePolygon1.distance(point)
return np.array([distToBasePolygon0,distToBasePolygon1])
distances = np.array([buildQuery(poly) for poly in polys])
# build the KD tree
tree = KDTree(distances)
# test it
for p in testPoints:
q = buildQuery(p)
output = tree.query(q)
print(output)
This yields as expected:
# (distance, polygon_index_in_KD_tree)
(2.0248456731316584, 0)
(1.904237866994273, 1)
(1.5991500555008626, 2)
(1.5109986459170694, 3)
There is one way that might be faster, but without doing any actual tests, it's hard for me to say for sure.
This might not work for your situation, but the basic idea is each time a Shapely object is added to the array, you adjust the position of different array elements so that it is always "sorted" in this manner. In Python, this can be done with the heapq module. The only issue with that module is that it's hard to choose a function to compare to different objects, so you would have to do something like this answer, where you make a custom Class for objects to put in the heapq that is a tuple.
import heapq
class MyHeap(object):
def __init__(self, initial=None, key=lambda x:x):
self.key = key
if initial:
self._data = [(key(item), item) for item in initial]
heapq.heapify(self._data)
else:
self._data = []
def push(self, item):
heapq.heappush(self._data, (self.key(item), item))
def pop(self):
return heapq.heappop(self._data)[1]
The first element in the tuple is a "key", which in this case would be the distance to the point, and then the second element would be the actual Shapely object, and you could use it like so:
point = Point(2.5, 5.7)
heap = MyHeap(initial=None, key=lambda x:x.distance(point))
heap.push(Polygon(...))
heap.push(Polygon(...))
# etc...
And at the end, the object you're looking for will be at heap.pop().
Ultimately, though, both algorithms seem to be (roughly) O(n), so any speed up would not be a significant one.

How to iterate between two geo locations with a certain speed in Python(3)

I want to simulate a movement on a real world map (spherical) and represent the actual position on (google|openStreet)maps.
I have an initial lat/long pair e.g. (51.506314, -0.088455) and want to move to e.g. (51.509359, -0.087221) on a certain speed by getting interpolated coordinates periodically.
Pseudocode for clarification:
loc_init = (51.509359, -0.087221)
loc_target = (51.509359, -0.087221)
move_path = Something.path(loc_init, loc_target, speed=50)
for loc in move_path.get_current_loc():
map.move_to(loc)
device.notify_new_loc(loc)
...
time.sleep(1)
Retrieving the current interpolated position can happen in different ways e.g. calculating with a fixed refresh time (1 sec) or maybe running in a thread holding and calculating continuously new positions.
Unfortunately I never worked with geo data before and can't find something useful on the internet. Maybe there is already a module or an implementation doing that?
Solved my problem:
Found a C++ library geographiclib which was ported to Python doing exactly what I was looking for.
Example code to calculate a inverse geodesic line and get positions for a specific distance:
from geographiclib.geodesic import Geodesic
import math
# define the WGS84 ellipsoid
geod = Geodesic.WGS84
loc_init = (51.501218, -0.093773)
loc_target = (51.511020, -0.086563)
g = geod.Inverse(loc_init[0], loc_init[1], loc_target[0], loc_target[1])
l = geod.InverseLine(loc_init[0], loc_init[1], loc_target[0], loc_target[1])
print ("The distance is {:.3f} m.".format(g['s12']))
# interval in m for interpolated line between locations
interval = 500
step = int(math.ceil(l.s13 / interval))
for i in range(step + 1):
if i == 0:
print ("distance latitude longitude azimuth")
s = min(interval * i, l.s13)
loc = l.Position(s, Geodesic.STANDARD | Geodesic.LONG_UNROLL)
print ("{:.0f} {:.5f} {:.5f} {:.5f}".format(
loc['s12'], loc['lat2'], loc['lon2'], loc['azi2']))
Gives:
The distance is 1199.958 m.
distance latitude longitude azimuth
0 51.50122 -0.09377 24.65388
500 51.50530 -0.09077 24.65623
1000 51.50939 -0.08776 24.65858
1200 51.51102 -0.08656 24.65953

Categories

Resources