Transform SRS and get Vertices of the Geometry¶
This example shows how to transform the spatial reference system of a polygon and extract its vertices using the coordinates of the new spatial reference system. However, this can lead to problems when the geometry intersects the antimeridian (±180° longitude line) in the original SRS. How to resolve this issue is demonstrated later.
Transform Spatial Reference System and Extract Vertices¶
First, create a polygon, then extract its vertices.
import geokit.core.srs
import geokit.core.geom
import matplotlib.pyplot as plt
aachen_pt = geokit.core.geom.point((6.083, 52.775), srs=geokit.core.srs.EPSG4326)
print(type(aachen_pt))
# Get the buffer around this point
aachen_buffered_area = aachen_pt.Buffer(1)
# Get a boundary
boundary_polygon = aachen_buffered_area.Boundary()
geokit.core.geom.extractVerticies(boundary_polygon)[:5, :]
<class 'osgeo.ogr.Geometry'>
array([[ 7.083 , 52.775 ],
[ 7.08162953, 52.72266404],
[ 7.0775219 , 52.67047154],
[ 7.07068834, 52.61856553],
[ 7.0611476 , 52.56708831]])
Transform Polygon and Extract Vertices¶
The polygon has been converted from SRS EPSG4326 to EPSG3857, and its vertices have been extracted.
# Get Points
boundary_polygon_3857 = geokit.core.geom.transform(boundary_polygon, toSRS=geokit.core.srs.EPSG3857)
geokit.core.geom.extractVerticies(boundary_polygon_3857)[:5, :]
array([[ 788475.95328876, 6941486.97274921],
[ 788323.39379549, 6931862.13450442],
[ 787866.13347064, 6922275.17314893],
[ 787105.42563299, 6912752.18910776],
[ 786043.35532983, 6903319.01626946]])
Compare the transformed Geometry¶
Lastly, the original geometry and the transformed geometry are compared. As expected, the original shape is distorted due to the spatial transformation.
fig, axs = plt.subplots(ncols=2, nrows=1, figsize=(12, 6))
ax_handle = geokit.core.geom.drawGeoms(boundary_polygon, figsize=(6, 6), ax=axs[0])
ax_handle = geokit.core.geom.drawGeoms(boundary_polygon_3857, figsize=(6, 6), ax=axs[1])
Deal with antimeridian issues¶
The transform() method shown hereabove can lead to problems when the geometry in its original SRS would intersect with the antimeridian (+/-180° longitude line).
Geokit contains several functions to deal with such issues.
- Apply the transform() function with revert360degProj flag to preserve geometry shape at the antimeridian
- Force the resulting geometry into the -/+180° range window
- Split given multipolygons which have been adapted to the -/+180° range but would actually be connected across the antimeridian
import pathlib
import geokit.core.geom
import geokit.core.vector
import geokit.core.srs
# Apply to a geometry close to the antimeridian
# NOTE: The inbuilt PROJ function would distort or even break the geometry since it shifts points "behind the antimeridian" by 360° - when reconnecting those with the non-shifted points, the geometry is broken.
# create a polygon near the antimeridian
polygon_4326 = geokit.core.geom.box((-179.9, -1, -175.9, 1), srs=geokit.core.srs.EPSG4326)
# now transform it to a centered LAEA projection
_laea = geokit.core.srs.centeredLAEA(geom=polygon_4326)
polygon_laea = geokit.core.geom.transform(polygon_4326, toSRS=_laea)
# buffer the polygon and plot
buffered_polygon_laea = polygon_laea.Buffer(100000)
geokit.core.geom.drawGeoms(buffered_polygon_laea, srs=_laea, figsize=(8, 4))
# now try to reproject to EPSG:4326 the "standard" way
buffered_polygon_4326_broken = geokit.core.geom.transform(buffered_polygon_laea, toSRS=4326)
print(buffered_polygon_4326_broken.GetEnvelope())
geokit.core.geom.drawGeoms(buffered_polygon_4326_broken, srs=4326, figsize=(8, 4))
# NOTE: Self-intersection and a polygon that ranges nearly across the whole world! See plot but also envelope above
# now use the revert360degProj = True flag to fix the shifted points
buffered_polygon_4326_fixed = geokit.core.geom.transform(buffered_polygon_laea, toSRS=4326, revert360degProj=True)
print(buffered_polygon_4326_fixed.GetEnvelope())
geokit.core.geom.drawGeoms(buffered_polygon_4326_fixed, srs=4326, figsize=(8, 4))
# NOTE how the polygon shape is correct now, with an extent over the antimeridian though!
(-175.80491336969473, 179.958807055332, -1.8992465451983134, 1.899246545198326) (179.2013754989235, 184.9986245010765, -1.9043005657821173, 1.9043005657821308)
Warning 1: Self-intersection at or near point -175.80491336969473 1.899246545198326
AxHands(ax=<Axes: >, handles=[<matplotlib.patches.PathPatch object at 0x71654dae6210>], cbar=None)
# what if we need the polygon to be in the -180°/+180° longitude range convention?
# use the fixOutOfBoundsGeom() method to split and shift or clip the geometry
# first option: clip off whatever is beyond the -180°/180° range
buffered_polygon_4326_fixed_clipped = geokit.core.geom.fixOutOfBoundsGeoms(buffered_polygon_4326_fixed, how="clip")
geokit.core.geom.drawGeoms(buffered_polygon_4326_fixed_clipped, figsize=(3, 3))
# NOTE: Only the part in the -180°/180° range is kept (here the part on the left of the antimeridian)
# second option: shift clipped parts beyond the -180°/180° range back into it
buffered_polygon_4326_fixed_shifted = geokit.core.geom.fixOutOfBoundsGeoms(buffered_polygon_4326_fixed, how="shift")
geokit.core.geom.drawGeoms(buffered_polygon_4326_fixed_shifted)
# NOTE: The polygon is now in the -180°/180° range, but it is split into two parts
Warning 1: Non closed ring detected. Warning 1: Non closed ring detected.
AxHands(ax=<Axes: >, handles=[[<matplotlib.patches.PathPatch object at 0x71654ddb9a90>, <matplotlib.patches.PathPatch object at 0x71654ddb9bd0>]], cbar=None)
# Now we can also have multipolygons which were provided in a split version to observe the -/+180° latitude range convention
# Think of Fidji, a country spanning the antimeridian
# load the shape file of Fidji, a country spanning the antimeridian
import pathlib
from geokit.core.get_test_data import get_test_data
FJI_geom = geokit.core.vector.extractFeatures(
get_test_data(
file_name="FJI.shp",
)
).geom.iloc[0]
# FJI is split into two parts, one in the eastern hemisphere and one in the western hemisphere separated by the antimeridian
# in our example like mostly, the Western part has been shifted eastwards by 360 degrees
# that creates a problem when plotting the data or loading anything via the extent due to its huge extent of 360° longitude
# it also prevents distance measuring between parts east and west of the antimeridian
# the test plot demonstrates the problem
geokit.core.geom.drawGeoms(FJI_geom)
# NOTE how far it spreads, with some islands on the right and some on the left of the plot
# we can therefore extract each of the two parts separately
# demonstrate here for the part "right" of the antimeridian (i.e. East of antimeridian but West on the map)
FJI_right_geom = geokit.core.geom.divideMultipolygonIntoEasternAndWesternPart(geom=FJI_geom, side="right")
geokit.core.geom.drawGeoms(FJI_right_geom, figsize=(4, 4))
# (side='left' would return the part West of the antimeridian, i.e. East on the map)
# (side='both' would return both parts as a tuple of 2 geometries)
# side='main' returns the side with the largest total polygon area
FJI_main_geom = geokit.core.geom.divideMultipolygonIntoEasternAndWesternPart(geom=FJI_geom, side="main")
geokit.core.geom.drawGeoms(FJI_main_geom, figsize=(4, 4))
# NOTE: we can see that in our case, it is the part "left" of the antimeridian that has the largest area
AxHands(ax=<Axes: >, handles=[[<matplotlib.patches.PathPatch object at 0x71654dcb5310>, <matplotlib.patches.PathPatch object at 0x71654dcb5450>, <matplotlib.patches.PathPatch object at 0x71654dcb5590>, <matplotlib.patches.PathPatch object at 0x71654dcb56d0>, <matplotlib.patches.PathPatch object at 0x71654dcb5810>, <matplotlib.patches.PathPatch object at 0x71654dcb5950>, <matplotlib.patches.PathPatch object at 0x71654dcb5a90>, <matplotlib.patches.PathPatch object at 0x71654dcb5bd0>, <matplotlib.patches.PathPatch object at 0x71654dcb5d10>, <matplotlib.patches.PathPatch object at 0x71654dcb5e50>, <matplotlib.patches.PathPatch object at 0x71654dcb5f90>, <matplotlib.patches.PathPatch object at 0x71654dcb60d0>, <matplotlib.patches.PathPatch object at 0x71654dcb6210>, <matplotlib.patches.PathPatch object at 0x71654dcb6350>, <matplotlib.patches.PathPatch object at 0x71654dcb6490>, <matplotlib.patches.PathPatch object at 0x71654dcb65d0>, <matplotlib.patches.PathPatch object at 0x71654dcb6710>, <matplotlib.patches.PathPatch object at 0x71654dcb6850>, <matplotlib.patches.PathPatch object at 0x71654dcb6990>, <matplotlib.patches.PathPatch object at 0x71654dcb6ad0>, <matplotlib.patches.PathPatch object at 0x71654dcb6c10>, <matplotlib.patches.PathPatch object at 0x71654dcb6d50>, <matplotlib.patches.PathPatch object at 0x71654dcb6e90>, <matplotlib.patches.PathPatch object at 0x71654dcb6fd0>, <matplotlib.patches.PathPatch object at 0x71654dcb7110>, <matplotlib.patches.PathPatch object at 0x71654dcb7250>, <matplotlib.patches.PathPatch object at 0x71654dcb7390>, <matplotlib.patches.PathPatch object at 0x71654dcb74d0>, <matplotlib.patches.PathPatch object at 0x71654dcb7610>, <matplotlib.patches.PathPatch object at 0x71654dcb7750>, <matplotlib.patches.PathPatch object at 0x71654dcb7890>, <matplotlib.patches.PathPatch object at 0x71654dcb79d0>, <matplotlib.patches.PathPatch object at 0x71654dcb7b10>, <matplotlib.patches.PathPatch object at 0x71654dcb7ed0>, <matplotlib.patches.PathPatch object at 0x716547f3c050>, <matplotlib.patches.PathPatch object at 0x716547f3c190>, <matplotlib.patches.PathPatch object at 0x716547f3c2d0>, <matplotlib.patches.PathPatch object at 0x716547f3c410>, <matplotlib.patches.PathPatch object at 0x716547f3c550>, <matplotlib.patches.PathPatch object at 0x716547f3c690>, <matplotlib.patches.PathPatch object at 0x716547f3c7d0>, <matplotlib.patches.PathPatch object at 0x71654dcb7d90>, <matplotlib.patches.PathPatch object at 0x71654dcb7c50>, <matplotlib.patches.PathPatch object at 0x716547f3c910>, <matplotlib.patches.PathPatch object at 0x716547f3ca50>, <matplotlib.patches.PathPatch object at 0x716547f3cb90>, <matplotlib.patches.PathPatch object at 0x716547f3ccd0>, <matplotlib.patches.PathPatch object at 0x716547f3ce10>, <matplotlib.patches.PathPatch object at 0x716547f3cf50>, <matplotlib.patches.PathPatch object at 0x716547f3d090>, <matplotlib.patches.PathPatch object at 0x716547f3d1d0>, <matplotlib.patches.PathPatch object at 0x716547f3d310>, <matplotlib.patches.PathPatch object at 0x716547f3d450>, <matplotlib.patches.PathPatch object at 0x716547f3d590>, <matplotlib.patches.PathPatch object at 0x716547f3d6d0>, <matplotlib.patches.PathPatch object at 0x716547f3d810>, <matplotlib.patches.PathPatch object at 0x716547f3d950>, <matplotlib.patches.PathPatch object at 0x716547f3da90>, <matplotlib.patches.PathPatch object at 0x716547f3dbd0>, <matplotlib.patches.PathPatch object at 0x716547f3dd10>, <matplotlib.patches.PathPatch object at 0x716547f3de50>, <matplotlib.patches.PathPatch object at 0x716547f3df90>, <matplotlib.patches.PathPatch object at 0x716547f3e0d0>, <matplotlib.patches.PathPatch object at 0x716547f3e210>, <matplotlib.patches.PathPatch object at 0x716547f3e350>, <matplotlib.patches.PathPatch object at 0x716547f3e490>, <matplotlib.patches.PathPatch object at 0x716547f3e5d0>, <matplotlib.patches.PathPatch object at 0x716547f3e710>, <matplotlib.patches.PathPatch object at 0x716547f3e850>, <matplotlib.patches.PathPatch object at 0x716547f3e990>, <matplotlib.patches.PathPatch object at 0x716547f3ead0>, <matplotlib.patches.PathPatch object at 0x716547f3ec10>, <matplotlib.patches.PathPatch object at 0x716547f3ed50>, <matplotlib.patches.PathPatch object at 0x716547f3ee90>, <matplotlib.patches.PathPatch object at 0x716547f3efd0>, <matplotlib.patches.PathPatch object at 0x716547f3f110>, <matplotlib.patches.PathPatch object at 0x716547f3f250>, <matplotlib.patches.PathPatch object at 0x716547f3f390>, <matplotlib.patches.PathPatch object at 0x716547f3f4d0>, <matplotlib.patches.PathPatch object at 0x716547f3f610>, <matplotlib.patches.PathPatch object at 0x716547f3f750>, <matplotlib.patches.PathPatch object at 0x716547f3f890>, <matplotlib.patches.PathPatch object at 0x716547f3f9d0>, <matplotlib.patches.PathPatch object at 0x716547f3fb10>, <matplotlib.patches.PathPatch object at 0x716547f3fc50>, <matplotlib.patches.PathPatch object at 0x716547f3fd90>, <matplotlib.patches.PathPatch object at 0x716547f3fed0>, <matplotlib.patches.PathPatch object at 0x716547fb0050>, <matplotlib.patches.PathPatch object at 0x716547fb0190>, <matplotlib.patches.PathPatch object at 0x716547fb02d0>, <matplotlib.patches.PathPatch object at 0x716547fb0410>, <matplotlib.patches.PathPatch object at 0x716547fb0550>, <matplotlib.patches.PathPatch object at 0x716547fb0690>, <matplotlib.patches.PathPatch object at 0x716547fb07d0>, <matplotlib.patches.PathPatch object at 0x716547fb0910>, <matplotlib.patches.PathPatch object at 0x716547fb0a50>, <matplotlib.patches.PathPatch object at 0x716547fb0b90>, <matplotlib.patches.PathPatch object at 0x716547fb0cd0>, <matplotlib.patches.PathPatch object at 0x716547fb0e10>, <matplotlib.patches.PathPatch object at 0x716547fb0f50>, <matplotlib.patches.PathPatch object at 0x716547fb1090>, <matplotlib.patches.PathPatch object at 0x716547fb11d0>, <matplotlib.patches.PathPatch object at 0x716547fb1310>, <matplotlib.patches.PathPatch object at 0x716547fb1450>, <matplotlib.patches.PathPatch object at 0x716547fb1590>, <matplotlib.patches.PathPatch object at 0x716547fb16d0>, <matplotlib.patches.PathPatch object at 0x716547fb1810>, <matplotlib.patches.PathPatch object at 0x716547fb1950>, <matplotlib.patches.PathPatch object at 0x716547fb1a90>, <matplotlib.patches.PathPatch object at 0x716547fb1bd0>, <matplotlib.patches.PathPatch object at 0x716547fb1d10>, <matplotlib.patches.PathPatch object at 0x716547fb1e50>, <matplotlib.patches.PathPatch object at 0x716547fb1f90>, <matplotlib.patches.PathPatch object at 0x716547fb20d0>, <matplotlib.patches.PathPatch object at 0x716547fb2210>, <matplotlib.patches.PathPatch object at 0x716547fb2350>, <matplotlib.patches.PathPatch object at 0x716547fb2490>, <matplotlib.patches.PathPatch object at 0x716547fb25d0>, <matplotlib.patches.PathPatch object at 0x716547fb2710>, <matplotlib.patches.PathPatch object at 0x716547fb2850>, <matplotlib.patches.PathPatch object at 0x716547fb2990>, <matplotlib.patches.PathPatch object at 0x716547fb2ad0>, <matplotlib.patches.PathPatch object at 0x716547fb2c10>, <matplotlib.patches.PathPatch object at 0x716547fb2d50>, <matplotlib.patches.PathPatch object at 0x716547fb2e90>, <matplotlib.patches.PathPatch object at 0x716547fb2fd0>, <matplotlib.patches.PathPatch object at 0x716547fb3110>, <matplotlib.patches.PathPatch object at 0x716547fb3250>, <matplotlib.patches.PathPatch object at 0x716547fb3390>, <matplotlib.patches.PathPatch object at 0x716547fb34d0>, <matplotlib.patches.PathPatch object at 0x716547fb3610>, <matplotlib.patches.PathPatch object at 0x716547fb3750>, <matplotlib.patches.PathPatch object at 0x716547fb3890>, <matplotlib.patches.PathPatch object at 0x716547fb39d0>, <matplotlib.patches.PathPatch object at 0x716547fb3b10>, <matplotlib.patches.PathPatch object at 0x716547fb3c50>, <matplotlib.patches.PathPatch object at 0x716547fb3d90>, <matplotlib.patches.PathPatch object at 0x716547fb3ed0>, <matplotlib.patches.PathPatch object at 0x716547e14050>, <matplotlib.patches.PathPatch object at 0x716547e14190>, <matplotlib.patches.PathPatch object at 0x716547e142d0>, <matplotlib.patches.PathPatch object at 0x716547e14410>, <matplotlib.patches.PathPatch object at 0x716547e14550>, <matplotlib.patches.PathPatch object at 0x716547e14690>, <matplotlib.patches.PathPatch object at 0x716547e147d0>, <matplotlib.patches.PathPatch object at 0x716547e14910>, <matplotlib.patches.PathPatch object at 0x716547e14a50>, <matplotlib.patches.PathPatch object at 0x716547e14b90>, <matplotlib.patches.PathPatch object at 0x716547e14cd0>, <matplotlib.patches.PathPatch object at 0x716547e14e10>, <matplotlib.patches.PathPatch object at 0x716547e14f50>, <matplotlib.patches.PathPatch object at 0x716547e15090>, <matplotlib.patches.PathPatch object at 0x716547e151d0>, <matplotlib.patches.PathPatch object at 0x716547e15310>, <matplotlib.patches.PathPatch object at 0x716547e15450>, <matplotlib.patches.PathPatch object at 0x716547e15590>, <matplotlib.patches.PathPatch object at 0x716547e156d0>, <matplotlib.patches.PathPatch object at 0x716547e15810>, <matplotlib.patches.PathPatch object at 0x716547e15950>, <matplotlib.patches.PathPatch object at 0x716547e15a90>, <matplotlib.patches.PathPatch object at 0x716547e15bd0>, <matplotlib.patches.PathPatch object at 0x716547e15d10>, <matplotlib.patches.PathPatch object at 0x716547e15e50>, <matplotlib.patches.PathPatch object at 0x716547e15f90>, <matplotlib.patches.PathPatch object at 0x716547e160d0>, <matplotlib.patches.PathPatch object at 0x716547e16210>, <matplotlib.patches.PathPatch object at 0x716547e16350>, <matplotlib.patches.PathPatch object at 0x716547e16490>, <matplotlib.patches.PathPatch object at 0x716547e165d0>, <matplotlib.patches.PathPatch object at 0x716547e16710>, <matplotlib.patches.PathPatch object at 0x716547e16850>, <matplotlib.patches.PathPatch object at 0x716547e16990>, <matplotlib.patches.PathPatch object at 0x716547e16ad0>, <matplotlib.patches.PathPatch object at 0x716547e16c10>, <matplotlib.patches.PathPatch object at 0x716547e16d50>, <matplotlib.patches.PathPatch object at 0x716547e16e90>, <matplotlib.patches.PathPatch object at 0x716547e16fd0>, <matplotlib.patches.PathPatch object at 0x716547e17110>, <matplotlib.patches.PathPatch object at 0x716547e17250>, <matplotlib.patches.PathPatch object at 0x716547e17390>, <matplotlib.patches.PathPatch object at 0x716547e174d0>, <matplotlib.patches.PathPatch object at 0x716547e17610>, <matplotlib.patches.PathPatch object at 0x716547e17750>, <matplotlib.patches.PathPatch object at 0x716547e17890>, <matplotlib.patches.PathPatch object at 0x716547e179d0>, <matplotlib.patches.PathPatch object at 0x716547e17b10>, <matplotlib.patches.PathPatch object at 0x716547e17c50>, <matplotlib.patches.PathPatch object at 0x716547e17d90>, <matplotlib.patches.PathPatch object at 0x716547e17ed0>, <matplotlib.patches.PathPatch object at 0x716547e70050>, <matplotlib.patches.PathPatch object at 0x716547e70190>, <matplotlib.patches.PathPatch object at 0x716547e702d0>, <matplotlib.patches.PathPatch object at 0x716547e70410>, <matplotlib.patches.PathPatch object at 0x716547e70550>, <matplotlib.patches.PathPatch object at 0x716547e70690>, <matplotlib.patches.PathPatch object at 0x716547e707d0>, <matplotlib.patches.PathPatch object at 0x716547e70a50>, <matplotlib.patches.PathPatch object at 0x716547e70910>, <matplotlib.patches.PathPatch object at 0x716547e70b90>, <matplotlib.patches.PathPatch object at 0x716547e70cd0>, <matplotlib.patches.PathPatch object at 0x716547e70e10>, <matplotlib.patches.PathPatch object at 0x716547e70f50>, <matplotlib.patches.PathPatch object at 0x716547e71090>, <matplotlib.patches.PathPatch object at 0x716547e711d0>, <matplotlib.patches.PathPatch object at 0x716547e71310>, <matplotlib.patches.PathPatch object at 0x716547e71450>, <matplotlib.patches.PathPatch object at 0x716547e71590>, <matplotlib.patches.PathPatch object at 0x716547e716d0>, <matplotlib.patches.PathPatch object at 0x716547e71810>, <matplotlib.patches.PathPatch object at 0x716547e71950>, <matplotlib.patches.PathPatch object at 0x716547e71a90>, <matplotlib.patches.PathPatch object at 0x716547e71bd0>, <matplotlib.patches.PathPatch object at 0x716547e71d10>, <matplotlib.patches.PathPatch object at 0x716547e71e50>, <matplotlib.patches.PathPatch object at 0x716547e71f90>, <matplotlib.patches.PathPatch object at 0x716547e720d0>, <matplotlib.patches.PathPatch object at 0x716547e72210>, <matplotlib.patches.PathPatch object at 0x716547e72350>, <matplotlib.patches.PathPatch object at 0x716547e72990>, <matplotlib.patches.PathPatch object at 0x716547e72490>]], cbar=None)