Loading JSON into a GeoDataFrame - python

I'm having difficulty loading the following JSON containing GIS data (https://data.cityofnewyork.us/resource/5rqd-h5ci.json) into a GeoDataFrame.
The following code fails when I try to set the geometry.
import requests
import geopandas as gpd
data = requests.get("https://data.cityofnewyork.us/resource/5rqd-h5ci.json")
gdf = gpd.GeoDataFrame(data.json())
gdf = gdf.set_geometry('the_geom')
gdf.head()

For people who are using web mapping libraries...
If the GeoJSON is wrapped in a FeatureCollection, as they often are when exported to a GeoJSON string by web mapping libraries (in my case, Leaflet), then all you need to do is pass the list at features to from_features() like so:
import geopandas as gpd
study_area = json.loads("""
{"type": "FeatureCollection", "features": [{"type": "Feature", "properties": {}, "geometry": {"type": "Polygon", "coordinates": [[[36.394272, -18.626726], [36.394272, -18.558391], [36.489716, -18.558391], [36.489716, -18.626726], [36.394272, -18.626726]]]}}]}
""")
gdf = gpd.GeoDataFrame.from_features(study_area["features"])
print(gdf.head())
Output:
geometry
0 POLYGON ((36.394272 -18.626726, 36.394272 -18....
Easy peasy.

Setting the geometry fails because the geopandas.GeoDataFrame constructor doesn't appear to be built to handle JSON objects as python data structures. It therefore complains about the argument not being a valid geometry object. You have to parse it into something that geopandas.GeoDataFrame can understand, like a shapely.geometry.shape. Here's what ran without error on my side, Python 3.5.4:
#!/usr/bin/env python3
import requests
import geopandas as gpd
from shapely.geometry import shape
r = requests.get("https://data.cityofnewyork.us/resource/5rqd-h5ci.json")
r.raise_for_status()
data = r.json()
for d in data:
d['the_geom'] = shape(d['the_geom'])
gdf = gpd.GeoDataFrame(data).set_geometry('the_geom')
gdf.head()
A disclaimer: I know absolutely nothing about Geo anything. I didn't even know these libraries and this kind of data existed until I installed geopandas to tackle this bounty and read a little bit of online documentation.

Combining the above answers, this worked for me.
import pandas as pd
import geopandas as gpd
from shapely.geometry import shape
nta = pd.read_json( r'https://data.cityofnewyork.us/resource/93vf-i5bz.json' )
nta['the_geom'] = nta['the_geom'].apply(shape)
nta_geo = gpd.GeoDataFrame(nta).set_geometry('geometry')

A more idiomatic way that uses regular dataframe functions inherited from pandas, and the native GeoDataFrame.from_features:
gdf = gpd.GeoDataFrame(data.json())
# features column does not need to be stored, this is just for illustration
gdf['features'] = gdf['the_geom'].apply(lambda x: {'geometry': x, 'properties': {}})
gdf2 = gpd.GeoDataFrame.from_features(gdf['features'])
gdf = gdf.set_geometry(gdf2.geometry)
gdf.head()

Related

Convert .geojson to .wkt | extract 'coordinates'

Goal: Ultimately, to convert .geojson to .wkt. Here, I want to extract all coordinates, each as a list.
In my.geojson, there are n many: {"type":"Polygon","coordinates":...
Update: I've successfully extracted the first coordinates. However, this file has two coordinates.
Every .geojson has at least 1 coordinates, but may have more.
How can I dynamically extract all key-values of many coordinates?
Code:
from pathlib import Path
import os
import geojson
import json
from shapely.geometry import shape
ROOT = Path('path/')
all_files = os.listdir(ROOT)
geojson_files = list(filter(lambda f: f.endswith('.geojson'), all_files))
for gjf in geojson_files:
with open(f'{str(ROOT)}/{gjf}') as f:
gj = geojson.load(f)
o = dict(coordinates = gj['features'][0]['geometry']['coordinates'], type = "Polygon")
geom = shape(o)
wkt = geom.wkt
Desired Output:
1 .wkt for all corrdinates in geojson
To convert a series of geometries in GeoJSON files to WKT, the shape() function can convert the GeoJSON geometry to a shapely object which then can be formatted as WKT and/or projected to a different coordinate reference system.
If want to access the coordinates of polygon once it's in a shapely object, usex,y = geo.exterior.xy.
If just want to convert a series of GeoJSON files into one .wkt file per GeoJSON file then try this:
from pathlib import Path
import json
from shapely.geometry import shape
ROOT = Path('path')
for f in ROOT.glob('*.geojson'):
with open(f) as fin, open(f.with_suffix(".wkt"), "w") as fout:
features = json.load(fin)["features"]
for feature in features:
geo = shape(feature["geometry"])
# format geometry coordinates as WKT
wkt = geo.wkt
print(wkt)
fout.write(wkt + "\n")
This output uses your example my.geojson file.
Output:
POLYGON ((19372 2373, 19322 2423, ...
POLYGON ((28108 25855, 27755 26057, ...
If need to convert the coordinates to EPSG:4327 (WGS-84) (e.g. 23.314208, 37.768469), you can use pyproj.
Full code to convert collection of GeoJSON files to a new GeoJSON file in WGS-84.
from pathlib import Path
import json
import geojson
from shapely.geometry import shape, Point
from shapely.ops import transform
from pyproj import Transformer
ROOT = Path('wkt/')
features = []
# assume we're converting from 3857 to 4327
# and center point is at lon=23, lat=37
c = Point(23.676757000000002, 37.9914205)
local_azimuthal_projection = f"+proj=aeqd +R=6371000 +units=m +lat_0={c.y} +lon_0={c.x}"
aeqd_to_wgs84 = Transformer.from_proj(local_azimuthal_projection,
'+proj=longlat +datum=WGS84 +no_defs')
for f in ROOT.glob('*.geojson'):
with open(f) as fin:
features = json.load(fin)["features"]
for feature in features:
geo = shape(feature["geometry"])
poly_wgs84 = transform(aeqd_to_wgs84.transform, geo)
features.append(geojson.Feature(geometry=poly_wgs84))
# Output new GeoJSON file
with open("out.geojson", "w") as fp:
fc = geojson.FeatureCollection(features)
fp.write(geojson.dumps(fc))
Assuming the conversion is from EPSG:3857 to EPSG:4327 and center point is at lon=23, lat=37, the output GeoJSON file will look like this:
{"features": [{"type": "Polygon", "geometry": {"coordinates": [[[23.897879, 38.012554], ...

Python scale GeoJSON by absolute value in meters

I am trying to generate a python code that takes geojson containing polygon as input an then outputs a polygon that is scaled by exact value in meters. For example lets say that my polygon is a square representing a house and I want to get an output of another square that is exactly 20m bigger on each side. This would be simple with square but it is quite hard with complex shapes.
Heres is what I use now (found here Scale GeoJSON to find latitude and longitude points nearby):
import json
from shapely import affinity
from shapely.geometry import shape, Point, mapping
def handler(event, context):
with open('polygon.geojson', encoding='utf-8') as f:
js = json.load(f)
polygon = shape(js['features'][0]['geometry'])
polygon_nearby = affinity.scale(polygon, xfact=1.1, yfact=1.1)
print(json.dumps({"type": "FeatureCollection", "features": [{"type": "Feature", 'properties': {}, 'geometry': mapping(polygon)}]}))
print(json.dumps({"type": "FeatureCollection", "features": [{"type": "Feature", 'properties': {}, 'geometry': mapping(polygon_nearby)}]}))
if __name__ == '__main__':
handler(None, None)
However as you can see this scales by realtive value (10%) and I would like to change that to absolute value in meters.
Thank you in forward for any suggestions

How to join point with polygon in geopandas

I have the polygon combination of lat-long1,lat2-long2 ..... and point like Lat - Long .
I have used GeoPandas library to get the result if there is any point is exist within polygon.
Sample Data of Polygon saved in csv file:
POLYGON((28.56056 77.36535,28.564635293716776
77.3675137204626,28.56871055311656 77.36967760850214,28.572785778190855 77.3718416641586,28.576860968931193 77.37400588747194,28.580936125329096 77.3761702784821,28.585011247376094 77.37833483722912,28.58908633506372 77.38049956375293,28.593161388383457 77.38266445809356,28.59723640732686 77.38482952029099,28.60131139188541 77.38699475038526,28.605386342050664 77.38916014841635,28.60946125781409 77.39132571442434,28.613536139167238 77.39349144844923,28.61761098610158 77.39565735053108,28.62168579860863 77.39782342070995,28.62576057667991 77.39998965902589,28.62983532030691 77.402156065519,28.633910029481108 77.40432264022931,28.637984704194054 77.40648938319696,28.642059344437207 77.408656294462,28.64068221074683 77.41187044231611,28.63920739580329 77.41502778244606,28.63763670052024 77.41812446187686,28.635972042808007 77.42115670220443,28.634215455216115 77.42412080422613,28.63236908243526 77.42701315247152,28.630435178662026 77.42983021962735,28.628416104829583 77.43256857085188,28.626314325707924 77.43522486797251,28.624132406877322 77.437795873562,28.621873011578572 77.44027845488824,28.619538897444272 77.4426695877325,28.617132913115164 77.44496636007166,28.614657994745563 77.44716597562005,28.612117162402576 77.44926575722634,28.609513516363293 77.45126315012166,28.606850233314923 77.45315572501488,28.604130562462267 77.45494118103147,28.60135782154758 77.45661734849246,28.598535392787774 77.45818219153013,28.595666718733966 77.45963381053753,28.592755298058414 77.46097044444889,28.589804681274302 77.46219047284835,28.586818466393503 77.46329241790465,28.583800294527727 77.46427494612952,28.58075384543836 77.46513686995802,28.57768283304089 77.46587714914885,28.574591000868892 77.4664948920035,28.571482117503592 77.46698935640259,28.568359971974488 77.46735995065883,28.565228369136484 77.46760623418534,28.56209112502966 77.4677279179792,28.558952062226695 77.4677248649196,28.55581500517431 77.46759708988064,28.552683775533943 77.46734475965891,28.552683775533943 77.46734475965891,28.553079397193876 77.4622453846313,28.553474828308865 77.45714597129259,28.55387006887434 77.4520465196603,28.554265118885752 77.44694702975198,28.554659978338513 77.4418475015852,28.555054647228083 77.43674793517746,28.555449125549913 77.43164833054634,28.555843413299442 77.42654868770937,28.55623751047213 77.42144900668411,28.556631417063407 77.41634928748812,28.55702513306874 77.41124953013893,28.55741865848359 77.40614973465412,28.557811993303396 77.40104990105122,28.55820513752363 77.39595002934782,28.558598091139757 77.39085011956145,28.558990854147225 77.38575017170969,28.559383426541523 77.3806501858101,28.559775808318093 77.37555016188024,28.560167999472434 77.37045009993768,28.56056 77.36535))
and second dataset is for LAT and LONG as 28.56282, 77.36824 respectively saved in csv file .
I have used below Python code to join both data set based on condition if point exist within polygon. like below
import pandas as pd
import shapely.geometry
from shapely.geometry import Point
import geopandas as gpd
site_df = pd.read_csv (r'lat_long_file.csv') # load lat and long file
site_df['geometry'] = pd.DataFrame(site_df).apply(lambda x: Point(x.LAT,x.LONG), axis='columns') # convert lat and long to point
gdf = gpd.GeoDataFrame(site_df, geometry = site_df.geometry,crs='EPSG:4326') #creating geo pandas data frame for point
from shapely import wkt
polygon_df = pd.read_csv (r'polygon_csv_file') #reading polygon sample raw string file
polygon_df['geometry'] = pd.DataFrame(polygon_df).apply(lambda row: shapely.wkt.loads(row.polygon), axis='columns') #converting string polygon to geometory
gd_polygon = gpd.GeoDataFrame(polygon_df, geometry = polygon_df.geometry,crs='EPSG:4326') #create geopandas dataframe
import shapely.speedups
shapely.speedups.enable() # this makes some spatial queries run faster
join_data = gpd.sjoin(gdf, gd_polygon, how="inner", op="within") //actual join condition
But that query does not retun anything . But point is exist within polygon. as we can see in below diagram
Green Location marker is point Lat and long which is exist within polygon.
I would check the axis order - WKT usually interpreted as longitude first, latitude second order, while the point you construct uses latitude:longitude order.
You can try removing the CRS identifier to see if it changes the result.
Also see
https://gis.stackexchange.com/questions/376751/shapely-flips-lat-long-coordinate
and
https://pyproj4.github.io/pyproj/stable/gotchas.html#axis-order-changes-in-proj-6
your sample data is unusable as it's an image
have sourced a polygon - a county boundary in UK
constructed a geopandas data frame of a point that is within this county
have used plotly to demonstrate visually the data
have used your code fragment gpd.sjoin(gdf, gd_polygon, how="inner", op="within") to do spatial join and it correctly joins point to polygon
import requests, json
import geopandas as gpd
import plotly.express as px
import shapely.geometry
# fmt: off
# get a polygon and construct a point
res = requests.get("https://opendata.arcgis.com/datasets/69dc11c7386943b4ad8893c45648b1e1_0.geojson")
gd_polygon = gpd.GeoDataFrame.from_features(res.json()).loc[lambda d: d["LAD20NM"].str.contains("Hereford")]
gdf = gpd.GeoDataFrame(geometry=gd_polygon.loc[:,["LONG","LAT"]].apply(shapely.geometry.Point, axis=1)).reset_index(drop=True)
# fmt: on
# plot to show point is within polygon
px.scatter_mapbox(gd_polygon, lon="LONG", lat="LAT").update_traces(
name="gd_polygon"
).add_traces(
px.scatter_mapbox(gdf, lat=gdf2.geometry.y, lon=gdf2.geometry.x)
.update_traces(name="gdf", marker_color="red")
.data
).update_traces(
showlegend=True
).update_layout(
mapbox={
"style": "carto-positron",
"layers": [
{"source": json.loads(gd_polygon.geometry.to_json()), "type": "line"}
],
}
).show()
# spatial join, all good :-)
gpd.sjoin(gdf, gd_polygon, how="inner", op="within")
output
spatial join has worked, point is within polygon
geometry
index_right
OBJECTID
LAD20CD
LAD20NM
LAD20NMW
BNG_E
BNG_N
LONG
LAT
Shape__Area
Shape__Length
0
POINT (-2.73931 52.081539)
18
19
E06000019
Herefordshire, County of
349434
242834
-2.73931
52.0815
2.18054e+09
285427

I am trying visualise railway track from shape shapes in python

I am trying to visualise railway tracks using Plotly lines on a map. I am a beginner who is working on gis data and visualise railway networks. i am not sure about the approach i am going about and i don't want to use arcgis or qgis.
Problem: I am not sure what should be added in the names attribute.
it would great i could get an overview of the code where i am going wrong
zipfile contains the .shp,.dbf.shx,.prj,.cpj
The error i am getting is 'GeoDataFrame' object has no attribute 'name'
this the code which i am working on.
import plotly.express as px
import geopandas as gpd
import shapely.geometry
import numpy as np
import wget
geo_df = gpd.read_file(r'railwaytrack.zip' )
lattiudes = [47.04691]
longitudes = [8.37467]
names=[ ]
for feature, line_geo in zip(geo_df.geometry, geo_df.name):
if isinstance(feature, shapely.geometry.linestring.LineString):
linestrings = [feature]
elif isinstance(feature, shapely.geometry.multilinestring.MultiLineString):
linestrings = feature.geoms
else:
continue
for linestring in linestrings:
x, y = linestring.xy
lattiudes = np.append(lattiudes, y)
longitudes = np.append(longitudes, x)
names = np.append(names, [name]*len(y))
lattiudes = np.append(lattiudes, None)
longitudes = np.append(longitudes, None)
names = np.append(names, None)
fig = px.line_geo(lat=lattiudes, lon=longitudes)
fig.show()

Convert geopandas dataframe to GEE feature collection using python

Given a geopandas dataframe (e.g. df that contains a geometry field), is the following a simplest way to convert it into ee.FeatureCollection?
import ee
features=[]
for index, row in df.iterrows():
g=ee.Geometry.Point([row['geometry'].x,row['geometry'].y])
# Define feature with a geometry and 'name' field from the dataframe
feature = ee.Feature(g,{'name':ee.String(row['name'])})
features.append(feature)
fc = ee.FeatureCollection(features)
If you want convert points geodataframe (GeoPandas) to ee.FeatureCollection, you can use this function:
import geopandas as gpd
import numpy as np
from functools import reduce
from geopandas import GeoDataFrame
from shapely.geometry import Point,Polygon
def make_points(gdf):
g = [i for i in gdf.geometry]
features=[]
for i in range(len(g)):
g = [i for i in gdf.geometry]
x,y = g[i].coords.xy
cords = np.dstack((x,y)).tolist()
double_list = reduce(lambda x,y: x+y, cords)
single_list = reduce(lambda x,y: x+y, double_list)
g=ee.Geometry.Point(single_list)
feature = ee.Feature(g)
features.append(feature)
#print("done")
ee_object = ee.FeatureCollection(features)
return ee_object
points_features_collections = make_points(points_gdf)
to do this function I based on this Reference
You can build a FeatureCollection from a json object. So if your geometry data file type is GeoJson you can do the following:
# import libraries
import ee
import json
# initialize earth engine client
ee.Initialize()
# load your gemotry data (which should be in GeoJson file)
with open("my_geometry_data.geojson") as f:
geojson = json.load(f)
# construct a FeatureCollection object from the json object
fc = ee.FeatureCollection(geojson)
If your geometry data is in different format (shapefile, geopackage), you can first save it to GeoJson then build a FeatureCollection object.
Finally, if you don't want to write any conversion code, and want just to convert your Geopandas.GeoDataFrame instantly to ee.FeatureCollection you can use the python package: geemap
geemap has several function for converting geometry data to FeatureCollection, and vice versa. You can see examples here. In your case, you need to use the geopandas_to_ee function, so your code would look like this:
# import libraries
import ee
import geemap
import geopandas as gpd
# initialize earth engine client
ee.Initialize()
# load your gemotry data using GeoPandas (which can be stored in different formats)
gdf = gpd.read_file("my_geometry_file.geojson")
# convert to FeatureCollection using one line of code
fc = geemap.geopandas_to_ee(gdf)
Note that under the hood, geemap is converting the GeoDataFrame to a json file, and then following the first approach I mentioned above.

Categories

Resources