Intersect two shapely polygons on the Earth projection - python

as i know, shapely use only cartesian coordinate system. I have two point on the earth with lat and lon coordinates. I need create buffer with 1km radius around this two points and find polygon, where this buffers intersect.
But construstion
buffer = Point(54.4353,65.87343).buffer(0.001) create simple circle, but in projection on the Earth it becomes ellipse, but i need two real circle with 1 km radius.
I think, i need convert my buffers into new projection and then intersect it, but dont now how correct do it.

You need to do what you say. For that, you will need to use a library that handles projections (pyproj is the choice here). There is a similar question in Geodesic buffering in python
import pyproj
from shapely.geometry import MultiPolygon, Polygon, Point
from shapely.ops import transform as sh_transform
from functools import partial
wgs84_globe = pyproj.Proj(proj='latlong', ellps='WGS84')
def point_buff_on_globe(lat, lon, radius):
#First, you build the Azimuthal Equidistant Projection centered in the
# point given by WGS84 lat, lon coordinates
aeqd = pyproj.Proj(proj='aeqd', ellps='WGS84', datum='WGS84',
lat_0=lat, lon_0=lon)
#You then transform the coordinates of that point in that projection
project_coords = pyproj.transform(wgs84_globe, aeqd, lon, lat)
# Build a shapely point with that coordinates and buffer it in the aeqd projection
aeqd_buffer = Point(project_coords).buffer(radius)
# Transform back to WGS84 each coordinate of the aeqd buffer.
# Notice the clever use of sh_transform with partial functor, this is
# something that I learned here in SO. A plain iteration in the coordinates
# will do the job too.
projected_pol = sh_transform(partial(pyproj.transform, aeqd, wgs84_globe),
aeqd_buffer)
return projected_pol
The function point_buff_on_globe will give you a polygon in lat lon that is the result of buffering the given point in the Azimuthal Equidistant Projection centered in that point (the best you can do with your requirements. Two observations:
I don't remember the units of the radius argument. I think is in meters, so if you need a 10 km buffer, you will need to pass it 10e3. But please, check it!
Beware of using this with radius to wide or points that are to far away from each other. Projections work well when the points are near to the point you are centering the projection.

Related

Calculate the centroid of a rectangle geometry in python

I have the following Polygon geometry. When I calculate the centroid, I get an invalid / inaccurate POINT. I am using geopandas centroid to calculate the point.
https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoSeries.centroid.html
import shapely
from shapely import wkt
from shapely.geometry import box
import geopandas as gpd
g = "POLYGON ((-96.8115234375 32.87109375, -96.8115234375 -96.767578125, 32.8271484375 -96.767578125, 32.8271484375 32.87109375, -96.8115234375 32.87109375))"
print(wkt.loads(g).centroid)
POINT (-31.9921875 -31.9482421875)
How do I calculate the centroid POINT lat, long coordinates of the box? The shape of the Polygon is rectangle.
Your code is fine, your polygon is not and does not represents a rectangle area in Dallas, TX. Let's split it into vertices to make it apparent:
g = "POLYGON ((-96.8115234375 32.87109375,
-96.8115234375 -96.767578125,
32.8271484375 -96.767578125,
32.8271484375 32.87109375,
-96.8115234375 32.87109375))"
The first point is good and is indeed in Dallas. The second point has wrong latitude, -96.7... (it is south of South pole :) - however Shapely does not care and accepts it). Third point has wrong both latitude and longitude, etc. Apparently, latitudes and longitudes got mixed up here.

Calculate distance between LineString and Point in meters

I have LineString and Point, and I want to calculate distance between them in meters:
line = LINESTRING (7.301606 52.5602366, 7.300065099999999 52.5587741)
point = POINT (8.02 52.303333)
I use: line.distance(point). I got a distance, but I don't know how to convert to meters or km. I am using geopandas, and I read that unit is CRS. Any help will be appreciated.
as per comments you need to specify the CRS I am using geopandas, and I read that unit is CRS. I assume by this statement you mean WSG84 CRS, which is EPSG:4326
given limitation of geopandas to have only one geometry column per GeoDataFrame have put LINESTRING and POINT into separate data frames
project from WSG84 onto UTM CRS https://en.wikipedia.org/wiki/Universal_Transverse_Mercator_coordinate_system. This then means co-ordinates are expressed in meters. This will not work if LINESTRING and POINT are in different UTM zones
then it's exceptionally simple to use https://geopandas.readthedocs.io/en/latest/docs/reference/api/geopandas.GeoSeries.distance.html
import shapely.wkt
import geopandas as gpd
gdfl = gpd.GeoDataFrame(geometry=[shapely.wkt.loads("LINESTRING (7.301606 52.5602366, 7.300065099999999 52.5587741)")], crs="EPSG:4326")
gdfp = gpd.GeoDataFrame(geometry=[shapely.wkt.loads("POINT (8.02 52.303333)")], crs="EPSG:4326")
utm = gdfl.estimate_utm_crs()
gdfl.to_crs(utm).distance(gdfp.to_crs(utm))
output
56592.878178

How to convert Earth Centered Inertial (ECI) coordinates to Earth Centered Earth Fixed (ECEF) AstroPy? Other?

I have position (x,y,z) and velocity (Vx,Vy,Vz) vectors in Earth Centered Inertial Coordinates (ECI) for a satellite orbit, and ultimately want to end up with geodetic coordinates (Latitude, Longitude, & Altitude).
According to this other Stack Overflow question it seems that I need to convert to Earth Centered Earth Fixed (ECEF) coordinates as an intermediate step (so ECI --> ECEF --> Lat/Lon/Alt).
I know ECI and ECEF share the same origin point (the center of mass of Earth) and the same z-axis that points to the North Pole. However, I am not sure what actual equations or adjustments I need to do to convert ECI to ECEF.
Otherwise, if anyone knows of any canned conversions on Astropy or something similar that would be even better. (I haven't seen ECI as an option on Astro Py or Space Py).
Here is the code I am using to generate my orbit and get the position and velocity vectors.
from scipy.constants import kilo
import orbital
from orbital import earth, KeplerianElements, Maneuver, plot, utilities
from orbital.utilities import Position, Velocity
import matplotlib.pyplot as plt
import numpy as np
orbitPineapple = KeplerianElements.with_period(5760, body=earth,
e=0.05, i=(np.deg2rad(0)), arg_pe=(np.deg2rad(30)))
plot(orbitPineapple)
plt.show()
print(orbitPineapple.r)
print(orbitPineapple.v)
Out:
Position(x=5713846.540659178, y=3298890.8383577876, z=0.0)
Velocity(x=-3982.305479346745, y=6897.555421488496, z=0.0)
There are a number of different Earth Centered Inertial frames, and the answer depends on which one you have your coordinates in.
The most common is so-called J2000; which is defined w.r.t to the orientation of the Earth on Jan 1st 2000. Another common one is GCRF, which is almost the same (to within 80 milli arcseconds).
If it’s either of those two, you should be able to create an astropy EarthLocation object and access the lat, lon and height attributes like so
from astropy import coordinates as coord
from astropy import units as u
from astropy.time import Time
now = Time('2017-09-27 12:22:00')
# position of satellite in GCRS or J20000 ECI:
cartrep = coord.CartesianRepresentation(x=5713846.540659178,
y=3298890.8383577876,
z=0., unit=u.m)
gcrs = coord.GCRS(cartrep, obstime=now)
itrs = gcrs.transform_to(coord.ITRS(obstime=now))
loc = coord.EarthLocation(*itrs.cartesian.cartrep )
print(loc.lat, loc.lon, loc.height)

Find the intersection between two geographical data points

I have two pairs of lat/lon (expressed in decimal degrees) along with their radius (expressed in meters). What I am trying to achieve is to find if an intersect between these two points exits (of course, it is obvious that this doesn't hold here but the plan is to try this algorithm in many other data points). In order to check this I am using Shapely's intersects() function. My question however is how should I deal with the different units? Should I make some sort of transformation \ projection first (same units for both lat\lon and radius)?
48.180759,11.518950,19.0
47.180759,10.518950,10.0
EDIT:
I found this library here (https://pypi.python.org/pypi/utm) which seems helpfull. However, I am not 100% sure if I apply it correctly. Any ideas?
X = utm.from_latlon(38.636782, 21.414384)
A = geometry.Point(X[0], X[1]).buffer(30.777)
Y = utm.from_latlon(38.636800, 21.414488)
B = geometry.Point(Y[0], Y[1]).buffer(23.417)
A.intersects(B)
SOLUTION:
So, I finally managed to solve my problem. Here are two different implementations that both solve the same problem:
X = from_latlon(48.180759, 11.518950)
Y = from_latlon(47.180759, 10.518950)
print(latlonbuffer(48.180759, 11.518950, 19.0).intersects(latlonbuffer(47.180759, 10.518950, 19.0)))
print(latlonbuffer(48.180759, 11.518950, 100000.0).intersects(latlonbuffer(47.180759, 10.518950, 100000.0)))
X = from_latlon(48.180759, 11.518950)
Y = from_latlon(47.180759, 10.518950)
print(geometry.Point(X[0], X[1]).buffer(19.0).intersects(geometry.Point(Y[0], Y[1]).buffer(19.0)))
print(geometry.Point(X[0], X[1]).buffer(100000.0).intersects(geometry.Point(Y[0], Y[1]).buffer(100000.0)))
Shapely only uses the Cartesian coordinate system, so in order to make sense of metric distances, you would need to either:
project the coordinates into a local projection system that uses distance units in metres, such as a UTM zone.
buffer a point from (0,0), and use a dynamic azimuthal equidistant projection centered on the lat/lon point to project to geographic coords.
Here's how to do #2, using shapely.ops.transform and pyproj
import pyproj
from shapely.geometry import Point
from shapely.ops import transform
from functools import partial
WGS84 = pyproj.Proj(init='epsg:4326')
def latlonbuffer(lat, lon, radius_m):
proj4str = '+proj=aeqd +lat_0=%s +lon_0=%s +x_0=0 +y_0=0' % (lat, lon)
AEQD = pyproj.Proj(proj4str)
project = partial(pyproj.transform, AEQD, WGS84)
return transform(project, Point(0, 0).buffer(radius_m))
A = latlonbuffer(48.180759, 11.518950, 19.0)
B = latlonbuffer(47.180759, 10.518950, 10.0)
print(A.intersects(B)) # False
Your two buffered points don't intersect. But these do:
A = latlonbuffer(48.180759, 11.518950, 100000.0)
B = latlonbuffer(47.180759, 10.518950, 100000.0)
print(A.intersects(B)) # True
As shown by plotting the lon/lat coords (which distorts the circles):

Ordering polygon coordinates for plotting

I have a model grid composed of many cells for which I would like to plot a shaded polygon on a matplotlib basemap.
Using pyproj, I first projected the points, before creating a polygon using shapely.geometry's Polygon class to extract the grid's exterior coordinates from. I then revert them back to WGS84 for passing to my plotting function:
grid_x_mesh, grid_y_mesh = pyproj.transform(wgs84, nplaea, grid_lons, grid_lats)
grid_x = grid_x_mesh.ravel()
grid_y = grid_y_mesh.ravel()
grid_poly = Polygon(zip(grid_x, grid_y))
grid_x, grid_y = grid_poly.exterior.coords.xy
grid_plons, grid_plats = pyproj.transform(nplaea, wgs84, grid_x, grid_y)
Then, using the matplotlib.basemap method, I projected the WSG84 coordinates to the map projection (nplaea in this case) and
grid_poly_x, grid_poly_y = m(grid_plons, grid_plats)
grid_poly_xy = zip(grid_poly_x, grid_poly_y)
grid_poly = Polygon(grid_poly_xy, facecolor='red', alpha=0.4)
plt.gca().add_patch(grid_poly)
When attempting to do so, I am getting a criss-cross pattern, which I assume has to do the ordering of the coordinates that I supplied to the polygon function.
I would think this has to do with either how I extracted the exterior coordinates, or just the ordering of the coordinate lists when I created the final polygon to plot.
Is there a clever way of ordering these properly if that is the problem?
Plotted polygon
Close-up
I agree there is some misarrangement of the grid coordinates. How was grid_lons created? Possibly a cleaner way to use Pyproj with Shapely geometries is to use a relatively new function shapely.ops.transform. For example:
import pyproj
from shapely.geometry import Polygon, Point
from shapely.ops import transform
from functools import partial
project = partial(
pyproj.transform,
pyproj.Proj(init='epsg:4326'), # WGS84 geographic
pyproj.Proj(init='epsg:3575')) # North Pole LAEA Europe
# Example grid cell, in long/lat
poly_g = Polygon(((5, 52), (5, 60), (15, 60), (15, 52), (5, 52)))
# Transform to projected system
poly_p = transform(project, poly_g)
The sanity of the coordinates should be preserved through the transformation (assuming that they were sane to begin with).
Soooo... apparently the shapely.geometry.Polygon method was drawing the polygon with all interior grid coordinates, which I realized due to the grid_plons and grid_plats having the same length as the np.ravel()'ed mesh coordinate array.
I ended up just doing a manual extraction of the external coordinates from the mesh coordinate arrays before passing them to the Polygon method (see below). Though, I imagine there may be a prettier and more general way of doing this.
Manual Extraction method:
grid_x_mesh, grid_y_mesh = pyproj.transform(wgs84, nplaea, grid_lons, grid_lats)
# The coordinates must be ordered in the order they are to be drawn
[grid_x.append(i) for i in grid_x_mesh[0,:]]
[grid_x.append(i) for i in grid_x_mesh[1:-1,-1]]
# Note that these two sides of the polygon are appended in reverse
[grid_x.append(i) for i in (grid_x_mesh[-1,:])[::-1]]
[grid_x.append(i) for i in (grid_x_mesh[1:-1,0])[::-1]]
[grid_y.append(i) for i in grid_y_mesh[0,:]]
[grid_y.append(i) for i in grid_y_mesh[1:-1,-1]]
[grid_y.append(i) for i in (grid_y_mesh[-1,:])[::-1]]
[grid_y.append(i) for i in (grid_y_mesh[1:-1,0])[::-1]]
grid_poly = Polygon(zip(grid_x, grid_y))

Categories

Resources