How to create Isopolygon using python? - python

I have a set of points of a location. I am trying to create an isoline using those points. In order to generate isolines I used convex hull and alphashape which is creating kind of box shaped or straight cut line kind of polygon structure like below. How do I get a proper isoline shape? What is way of perfect way to generate an isochrone using python?
print(df)
id latitude longitude geometry
8758520180 53.334261 -2.569419 POINT (-2.56942 53.33426)
9339285446 53.346211 -2.575348 POINT (-2.57535 53.34621)
616761660 53.340828 -2.566912 POINT (-2.56691 53.34083)
9454070930 53.338889 -2.574538 POINT (-2.57454 53.33889)
9454071045 53.339388 -2.574591 POINT (-2.57459 53.33939)
and so on.
import alphashape
polygon= alphashape.alphashape(df['geometry'], 0.20)
GeoDataFrame(polygon, crs="EPSG:4326", geometry=p_df['geometry'])
final output by alphashape:-
Excepted output (sketch):-
points :-

Related

Finding corner coordinates of a polygon (latitudes and longitudes) represented in a 2D array

I am trying to find the corner coorindates of a list of latitudes and longitudes I have (shared below) so that I can draw a polygon in Python (plotly choropleth). The challenge here is the latitudes and longitudes are scattered all around the map (say within a city in US).
I find a similar post that uses bfs, however, it works only for integer coordinates, and the points have to be in adjacent grids. Other similar posts mostly use open-cv, and doesn't work for lat and lon, unless tweaked appropriately.
Edits:
Seems we can use othagonal complex hull algorithm. However, I could not find any implementations in Python.
# Sample list of latitudes and longitudes
[
[-86.496774, 32.344437],
[-86.717897, 32.402814],
[-86.814912, 32.340803],
[-86.890581, 32.502974],
[-86.917595, 32.664169],
[-86.71339, 32.661732],
[-86.714219, 32.705694],
[-86.413116, 32.707386],
[-86.411172, 32.409937],
[-86.496774, 32.344437],
[-86.577799, 33.765316],
[-86.759144, 33.840617],
[-86.953664, 33.815297],
[-86.954305, 33.844862],
[-86.96296, 33.844865],
[-86.963358, 33.858221],
[-86.924387, 33.909222],
[-86.793914, 33.952059],
[-86.685365, 34.05914],
[-86.692061, 34.092654],
[-86.599632, 34.119914],
[-86.514881, 34.25437],
[-86.45302, 34.259317],
[-86.303516, 34.099073],
[-86.332723, 33.986109],
[-86.370152, 33.93977],
[-86.325622, 33.940147],
[-86.377532, 33.861706],
[-86.577528, 33.801977],
[-86.577799, 33.765316]
]
Appreciate your help and inputs!

Using pyproj to transform shapely data is giving me strange results

I've got a polygon which looks something like this in WKT:
POLYGON ((-2.5079473598836624 51.34385834919997, -2.5081726654409133 51.34353499032948, -2.507909808957454 51.343441165566986, -2.507679138982173 51.34359530614682, -2.5079473598836624 51.34385834919997))
I'm trying to transform this from EPSG:3857 (web mercator) to EPSG:32630 (UTM 30N) to do some distance calculations on it but the results look weird:
wgs_proj = pyproj.CRS("EPSG:3857")
utm_proj = pyproj.CRS("EPSG:32630")
transform = pyproj.Transformer.from_crs(wgs_proj, utm_proj, always_xy=True).transform
shape = shapely.wkt.loads("POLYGON ((-2.5079473598836624 51.34385834919997, -2.5081726654409133 51.34353499032948, -2.507909808957454 51.343441165566986, -2.507679138982173 51.34359530614682, -2.5079473598836624 51.34385834919997))")
boundary = shapely.ops.transform(transform, shape)
print(str(boundary))
This outputs:
POLYGON ((833976.0465009063 51.05017626883035, 833976.04627538 51.04985475944731, 833976.0465384943 51.049761471464706, 833976.0467693905 51.0499147304722, 833976.0465009063 51.05017626883035))
This looks to me like it's got the longitude conversion roughly right but the latitude conversion completely wrong. The units are supposed to be in metres, I think. So unless the shape happens to be at a latitude about 51m North of the origin of UTM30N, something has gone wrong. Can anyone point me to what?
This is because the data is in long-lats, not in EPSG:3857. Almost everything online says that EPSG:3857 is what Google maps uses, but this is only true internally. EPSG:3857 is WGS84 projected into metres. Externally, Google still uses WGS84 long-lats, ie EPSG:4326. Changing the origin coordinate system in the code shown in the question produces the right result.

Reproject coordinates with rasterio creates a irregular grid from an regular grid

I have geotiff files load into xarray with a crs = EPSG:31467. I want to transform/reproject (don't know if there is a difference) these files into EPSG:4326. To do that, I use rasterio.warp.transform function which needs 1D arrays for x,y. To generate these i use numpy.meshgrid and flatten functions. Here is a small example with my data:
import numpy
#Longitude and Latitude in EPSG:31467
lon = [3280914, 3281914, 3282914]
lat = [6103001, 6102001, 6101001]
#create 2d meshgrid
xv, yv = np.meshgrid(lon, lat)
xv, yv
(array([[3280914, 3281914, 3282914],
[3280914, 3281914, 3282914],
[3280914, 3281914, 3282914]]),
array([[6103001, 6103001, 6103001],
[6102001, 6102001, 6102001],
[6101001, 6101001, 6101001]]))
Now I have a sequence of different longitude [3280914, 3281914, 3282914] for the same latitude [6103001, 6103001, 6103001]
When i now use rasterio.transform(src_crs, dst_crs, x, y) these sequences disappear and i dont unterstand why?!
from rasterio.warp import transform
# Compute the lon/lat coordinates with rasterio.warp.transform
lon, lat = transform('EPSG:31467','EPSG:4326',
xv.flatten(), yv.flatten())
np.asarray(lon).reshape(3,3), np.asarray(lat).reshape(3,3)
> (array([[5.57397386, 5.58957607, 5.6051787 ],
> [5.57473921, 5.59033795, 5.60593711],
> [5.57550412, 5.5910994 , 5.60669509]]), array([[55.00756605, 55.00800488, 55.00844171],
> [54.9985994 , 54.99903809, 54.99947477],
> [54.98963274, 54.99007128, 54.99050782]]))
np.unique(xv).shape, np.unique(yv).shape
> ((3,), (3,))
np.unique(lon).shape, np.unique(lat).shape
> ((9,), (9,))
To change the reporjected coordinates back to xarray I have to get the same shape in sense of equality. Which process I don't understand, is it the function of transform or the concept of projections?
I can't understand what exactly you are trying to do after np.asarray(lon).reshape(3,3)
Which process I don't understand, is it the function of transform or the concept of projections?
It seems like you don't understand both.
EPSG:31467 and EPSG:4326 are fundamentally different types of data. EPSG:31467 is actually a planar rectangular coordinate system in zonal projection. EPSG:4326 is not a projection at all, it is a pure geodetic coordinates in WGS-84 terrestrial coordinate system with WGS-84 ellypsoid. What is exactly emportant here is that same coordinates in EPSG:31467 don't have to be same in EPSG:4326. Because in 4326 your coordinate is an angle and in 31467 your coordinate is a distance from equator or false meridien. Axes in these systems are not collinear and related with convergence of meridians parameter. So, if you change Norting or Easting in 31467, both latitude and logitute can change.
Here you can notice an angle between blue lines (one cell is 31467 analogue) and black lines (whole grid is 4326 analogue)
https://ru.wikipedia.org/wiki/%D0%A4%D0%B0%D0%B9%D0%BB:Soviet_topographic_map_kilometer_grid.svg
It's pretty easy to check, that transformation works correctly - just do it backwards.
lon, lat = transform('EPSG:31467','EPSG:4326',
xv.flatten(), yv.flatten())
x_check, y_check = transform('EPSG:4326', 'EPSG:31467', lon, lat)
#we'll have some troubles because of computational errors, so let's round
x_check = [int(round(i, 0)) for i in x_check]
print(lon)
print(x_check)
print(xv.flatten())
>[5.574033001416839, 5.5896346633743175, 5.605236748547687, 5.574797816145165, 5.5903960110246524, 5.605994628800234, 5.5755622060626155, 5.591156935778857, 5.6067520880717225]
>[3280914, 3281914, 3282914, 3280914, 3281914, 3282914, 3280914, 3281914, 3282914]
>[3280914 3281914 3282914 3280914 3281914 3282914 3280914 3281914 3282914]
Output examples that transform() returns you exactly what it expected to return.
Next code also works as it is expected (you can match output with above one):
print(np.asarray(lon).reshape(3,3))
print(xv)
>[[5.574033 5.58963466 5.60523675]
> [5.57479782 5.59039601 5.60599463]
> [5.57556221 5.59115694 5.60675209]]
>[[3280914 3281914 3282914]
> [3280914 3281914 3282914]
> [3280914 3281914 3282914]]
I have never worked with rasterio, so I can't provide you working solution.
Some notes:
I have no idea why do you need grid for raster transformation
Rasterio docs are clear and have solution for you: https://rasterio.readthedocs.io/en/latest/topics/reproject.html#reprojecting-a-geotiff-dataset
You can transform raster between crs directly. If not in rasterio, try osgeo.gdal (gdal.Warp(dst_file, src_file, srcSRS='EPSG:31467', dstSRS='EPSG:4326')
Note the difference between reprojection and defining projection for raster. First changes image, second changes metadata. For correct work of direct transform, your GeoTIFF must have valid projection defenition in metadata (that matches actual projection of your raster)
If you're not developing standalone app and just need to reproject 2-3 rasters, use QGIS and do it without coding. It's also helpfull to try understanding geodetic concepts on 2-3 examples in QGIS before coding. Just use it as a playground
If you're not developing standalone app, you can solve your automatisation task in QGIS python API. You can test workflow with UI and then call some QGIS/GDAL tools from python script as batch. What is more - rasterio and all other packages will be avaluable for installation on QGIS' python. Of course, it's a bad idea for deployment unless you are creating a QGIS plugin
In EPSG:31467 the coordinate value of 0.001 is 1 mm. So more precise is useless. In EPSG:4326 1 degree is 111.1 km approx (or 111.3*cos(lat)). So, you can calculate useful precise. Everything more than 4-5 digit after . may also be useless

in GeoPandas, select (line string) data within a latitude longitude box defined by user

I have a geopandas dataframe consisting of a combination of LineStrings and MultiLineStrings. I would like to select those LineStrings and MultiLineStrings containing a point within a box (defined by me) of latitude longitude, for which I don't have a geometry. In other words, I have some mapped USGS fault traces and I would like to pick a square inset of those fault lines within a certain distance from some lat/lons. So far I've had some success unwrapping just coordinates from the entire data frame and only saving points that fall within a box of lat/lon, but then I no longer keep the original geometry or information saved in the data frame. (i.e. like this:)
xvals=[]
yvals=[]
for flt in qfaults['geometry']:
for coord in flt.coords:
if coord[1] >= centroid[1]-1 and coord[1] <= centroid[1]+1 and coord[0]<=centroid[0]+1 and coord[0]>=centroid[0]-1:
xvals.append(coord[0])
yvals.append(coord[1])
Is there any intuition as to how to do this using the GeoPandas data frame? Thanks in advance.
GeoPandas has .cx indexer which works exactly like this. See https://geopandas.readthedocs.io/en/latest/docs/user_guide/indexing.html
Syntax is gdf.cx[xmin:xmax, ymin:ymax]
world = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))
southern_world = world.cx[:, :0]
western_world = world.cx[:0, :]
western_europe = world.cx[1:10, 40:60]

how to determine if a point is inside a polygon using geojson and shapely

I am hoping to create a region on a map and be able to automatically determine if points (coordinates) are inside that region. I'm using a geojson file of the entire US and coordinates for New York City for this example.
Geojson: https://github.com/johan/world.geo.json
I have read the shapely documentation and just can't figure out why my results are returning False. Any help would be much appreciated.
import json
from shapely.geometry import shape, GeometryCollection, Point
with open('USA.geo.json', 'r') as f:
js = json.load(f)
point = Point(40.712776, -74.005974)
for feature in js['features']:
polygon = shape(feature['geometry'])
if polygon.contains(point):
print ('Found containing polygon:', feature)
I'm hoping to print the contained coordinates, but nothing is printed.
You need to swap the values of the Point() around:
point = Point(-74.005974, 40.712776)
The dataset you're using has the longitude first and the latitude second in their coordinates.

Categories