Accessing nested python Dict of coordinates in GeoJSON - python

I'm making a get request from a REST service and returning a GeoJSON but it recognized as a dict by Python. I'm trying to access the nested list value from the coordinates key and the string value from the Zone_ key. Here is a sample of the data:
data = {
"type": "FeatureCollection",
"crs": {
"type": "name",
"properties": {
"name": "EPSG:4326"
}
},
"features": [{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-80.1135430232235,
33.49892053365546
],
[
-80.1165139377003,
33.499835530094444
],
[
-80.1170369402652,
33.49992051898103
],
[
-80.11707393820328,
33.49992653060032
]
]
]
},
"properties": {
"Zone_": "H"
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-79.62281482439842,
33.289964520159124
],
[
-79.62376378105404,
33.29028749972797
],
[
-79.6247927817771,
33.29068750016911
],
[
-79.62604278223982,
33.29121650014533
]
]
]
},
"properties": {
"Zone_": "I"
}
}
]
}
The problem I am having is I'm stumped with trying to access the coordinates from the geometry key. I'm getting strings back and I don't know how to get to the nested list in coordinates. Here is what I have so far:
for x in data['features']:
for y in x['geometry']:
print(y)
Can someone please help me with this please?

The coordinates are nested inside the geometry of the features. So, you need to access it accordingly.
Try:
for feature in data['features']:
print("Feature coods:")
for cood in feature['geometry']['coordinates']:
print(cood)
This will give you the nested coordinate list:
Feature coods:
[[-80.1135430232235, 33.49892053365546], [-80.1165139377003, 33.499835530094444], [-80.1170369402652, 33.49992051898103], [-80.11707393820328, 33.49992653060032]]
Feature coods:
[[-79.62281482439842, 33.289964520159124], [-79.62376378105404, 33.29028749972797], [-79.6247927817771, 33.29068750016911], [-79.62604278223982, 33.29121650014533]]

features is a list so you have to reference that by integer index [0]
for x in data['features'][0]['geometry']['coordinates']:
print(x)
for x in data['features'][0]['properties']:
print(x)

Related

how to validate properties key in a geojson

i would like to know why the below posted geojson format is invalid. i tried to visualize its data in
http://geojson.io
but nothing gets displayed.
geojson
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[[7.85563468082516,49.90287230375267],[7.855636808249913,49.902782379662085],[7.855776033932631,49.902783753651605],[7.855773906766568,49.902873677746555]]
]
]
},"properties": {"areaOfCoverage":"30"}},
}
Try this:
{
"type": "FeatureCollection",
"features": [
{
"properties":{"areaOfCoverage":"30"},
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
7.85563468082516,
49.90287230375267
],
[
7.855636808249913,
49.902782379662085
],
[
7.855776033932631,
49.902783753651605
],
[
7.85563468082516,
49.90287230375267
]
]
]
}
}
]
}

Extracting coordinates (lat, lon) from MultiLineString GeoJSON in Python

I want to make a ScatterMapbox map based on the information contained in this GeoJSON file: https://cdn.buenosaires.gob.ar/datosabiertos/datasets/metrobus/recorrido-de-metrobus.geojson .
However, I am facing the following problem: the geometry in the GeoJSON is a MultiLineString. I provide a written example of the problem here:
{
"type": "FeatureCollection",
"name": "callejero_badata_WGS84",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "id": 28686, "codigo": 17138, "nomoficial": "PUNTA ARENAS", "alt_izqini": 0, "alt_izqfin": 0, "alt_derini": 0, "alt_derfin": 0, "nomanter": null, "nom_mapa": "TÚNEL PUNTA ARENAS", "tipo_c": "TÚNEL", "long": 334.60341680080836, "sentido": "DOBLE", "cod_sent": 2, "observa": "Viaducto - Túnel inaugurado en Abril de 2009", "bicisenda": "-", "lado_ciclo": null, "recorrid_x": null, "ciclo_obse": null, "tooltip_bi": null, "red_jerarq": "VÍA DISTRIBUIDORA COMPLEMENTARIA", "red_tp": null, "ffcc": "SI", "tipo_ffcc": "Túnel", "COMUNA": 15, "COM_PAR": 15, "COM_IMPAR": 15, "BARRIO": "PATERNAL", "BARRIO_PAR": "PATERNAL", "BARRIO_IMP": "PATERNAL" },
"geometry": { "type": "MultiLineString", "coordinates":
[ [ [ -58.46939502613111, -34.590823162817173 ],
[ -58.469462454508552, -34.59098118466796 ],
[ -58.469669480276444, -34.591727559343369 ],
[ -58.469869735691702, -34.592625455739224 ],
[ -58.470073382303283, -34.593447590597258 ],
[ -58.470121607819273, -34.593775790374316 ] ] ] } }, ...
]
}
As you may see, the coordinates are composed by an array of lists (i.e. [ [ [ -58.46939502613111, -34.590823162817173 ], [ -58.469462454508552, -34.59098118466796 ] ] ]).
In order to plot the map, I need to extract from that file ALL the coordinates separately: all the latitudes must be on one list, and all the longitudes must be on another one. I would need something like this (following the example previously provided):
lats = [ -34.590823162817173,
-34.59098118466796,
-34.591727559343369,
...
]
lons = [ -58.46939502613111,
-58.469462454508552,
-58.469669480276444,
...
]
I tried several things, but got nothing close. Everything I did resulted in various errors and/or obtaining just one value/pair of values (coordinates) instead of the wanted list (above).
Everything I have now is the code where I load the GeoJSON, which I provide below.
metrobus = json.load(open("recorrido-de-metrobus.geojson"))
Is there any way to do something like this?
I really appreciate any advice, workaround or solution you might share with me. Thank you, in advance.
EDIT 2
Finally solved after following solution in this link: https://community.plotly.com/t/possible-bug-plotting-multipolygons-in-scattermapbox/33479 .
Thank you Yuri and fusion for your help.
# read your data:
metrobus = json.load(open("recorrido-de-metrobus.geojson"))
lats = []
lons = []
for i in data['features']:
cord = i['geometry']['coordinates']
for j in cord:
for k in j:
lats.append(k[1])
lons.append(k[0])

Delete polygons that have one or more side parts in common

I am trying to solve a particular case of comparison of polygons to others. I have five polygons distributed as in the figure below. The black polygon is the one with the largest area.
There may be other similar cases, the main rule is to remove the smallest polygons among all those that have one or more side portions in common.
The data for this case are in a GeoJson file as follows:
{"type":"FeatureCollection","features":[
{"type":"Feature","properties":{"id":1},"geometry":{"type":"Polygon","coordinates":[[[3.4545135498046875,45.533288879467456],[3.4960556030273433,45.533288879467456],[3.4960556030273433,45.57055337226086],[3.4545135498046875,45.57055337226086],[3.4545135498046875,45.533288879467456]]]}},
{"type":"Feature","properties":{"id":2},"geometry":{"type":"Polygon","coordinates":[[[3.4545135498046875,45.52917023833511],[3.4960556030273433,45.52917023833511],[3.4960556030273433,45.53891018749409],[3.4545135498046875,45.53891018749409],[3.4545135498046875,45.52917023833511]]]}},
{"type":"Feature","properties":{"id":3},"geometry":{"type":"Polygon","coordinates":[[[3.4845542907714844,45.5298015824607],[3.5159683227539062,45.5298015824607],[3.5159683227539062,45.543388795387294],[3.4845542907714844,45.543388795387294],[3.4845542907714844,45.5298015824607]]]}},
{"type":"Feature","properties":{"id":4},"geometry":{"type":"Polygon","coordinates":[[[3.465328216552734,45.542667432984864],[3.4735679626464844,45.542667432984864],[3.4735679626464844,45.5478369923404],[3.465328216552734,45.5478369923404],[3.465328216552734,45.542667432984864]]]}},
{"type":"Feature","properties":{"id":5},"geometry":{"type":"Polygon","coordinates":[[[3.4545138850808144,45.56799974017372],[3.4588050842285156,45.56799974017372],[3.4588050842285156,45.57055290285386],[3.4545138850808144,45.57055290285386],[3.4545138850808144,45.56799974017372]]]}}]}
Is there a solution to delete only the two blue polygons(id 2 and 5)? In python.
By transforming the Polygons into LineString one could look if a Linestring is a portion of another Linestring ? But I don't see how to do it. Or maybe looking to see if the LineString of the black and blue polygons have more than two points in common ? But we can't convert a LineString into more than two points.
The following approach may work for you using shared_paths which correctly calls out the path overlap between polygons 1, 2 and 5:
import json
import shapely as sh
import shapely.ops as ops
import shapely.geometry as geo
with open('./test.json') as f:
features = json.load(f)['features']
for f1 in features:
for f2 in features:
id1 = f1['properties']['id']
id2 = f2['properties']['id']
if int(id1) > int(id2):
s1 = geo.shape(f1['geometry'])
s2 = geo.shape(f2['geometry'])
coll = ops.shared_paths(s1.boundary, s2.boundary)
if not coll.is_empty:
print(f"{id1} and {id2} have shared path")
# update your feature collection etc
I had to reduce the precision to 5 decimal places in your feature geometry for this to work as initially it only detects the overlap between polygon 1 and 2. The shared corner between polygon 1 and 5 is slightly out in your input FeatureCollection:
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"id": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[3.45451, 45.53328],
[3.49605, 45.53328],
[3.49605, 45.57055],
[3.45451, 45.57055],
[3.45451, 45.53328]
]
]
}
},
{
"type": "Feature",
"properties": {
"id": 2
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[3.45451, 45.52917],
[3.49605, 45.52917],
[3.49605, 45.53891],
[3.45451, 45.53891],
[3.45451, 45.52917]
]
]
}
},
{
"type": "Feature",
"properties": {
"id": 3
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[3.48455, 45.52980],
[3.51596, 45.52980],
[3.51596, 45.54338],
[3.48455, 45.54338],
[3.48455, 45.52980]
]
]
}
},
{
"type": "Feature",
"properties": {
"id": 4
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[3.465328, 45.54266],
[3.473567, 45.54266],
[3.473567, 45.54783],
[3.465328, 45.54783],
[3.465328, 45.54266]
]
]
}
},
{
"type": "Feature",
"properties": {
"id": 5
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[3.454513, 45.56799],
[3.458805, 45.56799],
[3.458805, 45.57055],
[3.454513, 45.57055],
[3.454513, 45.56799]
]
]
}
}
]
}

Serialize GeoJSON in Python with rounded coordinates

I'd like to serialize GeoJSON FeatureCollections in Python with limited precision for their coordinates.
For example, here's a FeatureCollection (represented as dicts and lists in Python):
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "Liberty Island",
"area_sqm": 24950.40123456,
"established": 1875,
"height_ft": 305
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[ -74.04715418815613, 40.690994683044906 ],
[ -74.04499769210815, 40.68873311507798 ],
[ -74.04354929924011, 40.689676800252016 ],
[ -74.04715418815613, 40.690994683044906 ]
]
]
}
}
]
}
I can serialize it using json.dumps:
print(json.dumps(fc))
This prints out the JSON:
{"type": "FeatureCollection", "features": [{"type": "Feature", "properties": {"name": "Liberty Island", "area_sqm": 24950.40123456, "established": 1875, "height_ft": 305}, "geometry": {"type": "Polygon", "coordinates": [[[-74.04715418815613, 40.690994683044906], [-74.04499769210815, 40.68873311507798], [-74.04354929924011, 40.689676800252016], [-74.04715418815613, 40.690994683044906]]]}}]}
Those coordinates are far too precise. According to Wikipedia, 7 digits is ~cm precision, which ought to be good enough. What I'm getting is ~nanometer precision.
I'd like to serialize the GeoJSON FeatureCollection with only seven digits of precision for coordinates. Note that I'd like to use Python's default serialization for everything in properties: since the values there could be anything, I can't make any universal claims about how much precision is enough.
My desired output is something like:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "Liberty Island",
"area_sqm": 24950.40123456,
"established": 1875,
"height_ft": 305
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[ -74.0471541, 40.6909946 ],
[ -74.0449976, 40.6887331 ],
[ -74.0435492, 40.6896768 ],
[ -74.0471541, 40.6909946 ]
]
]
}
}
]
}
I'm not sure what exact serialization you're after but when it comes to FeatureCollection simplification, this should get you started:
import json
from copy import copy
def simplify_geometries(feat_collection):
new_coll = copy(feat_collection)
old_features = new_coll['features']
new_features = []
for feature in old_features:
geometry = feature['geometry']
coords = shorten_arr(geometry['coordinates'])
geometry['coordinates'] = coords
new_features.append(feature)
new_coll['features'] = new_features
return json.dumps(new_coll)
print(simplify_geometries(
json.loads('{"type": "FeatureCollection",...')
))
where shorten_arr is a trivial recursion:
def shorten_arr(arr, dec_places=7):
if not isinstance(arr, list):
return round(arr, dec_places)
to_ret = []
for n in arr:
if isinstance(n, list):
n = shorten_arr(n, dec_places)
to_ret.append(shorten_arr(n, dec_places))
return to_ret

Update values in geojson file in Python

I have geojson file as follows:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[
57.45849609375,
57.36801461845934
],
[
57.10693359375,
56.31044317134597
],
[
59.205322265625,
56.20059291588374
],
[
59.4140625,
57.29091812634045
],
[
57.55737304687501,
57.36801461845934
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[
59.40307617187499,
57.29685437021898
],
[
60.8203125,
57.314657355733274
],
[
60.74340820312499,
56.26776108757582
],
[
59.227294921875,
56.21281407174654
],
[
59.447021484375,
57.29091812634045
]
]
}
}
]
}
I want to replace LineString in "type": "LineString" with Polygon, and also, replace coordinates last point of each linestring by coordinates of first point to make it close if it has more than 3 points.
How can I do it in Python with geopandas or pandas? Thanks.
Here is expected output:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
57.45849609375,
57.36801461845934
],
[
57.10693359375,
56.31044317134597
],
[
59.205322265625,
56.20059291588374
],
[
59.4140625,
57.29091812634045
],
[
57.45849609375,
57.36801461845934
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
59.40307617187499,
57.29685437021898
],
[
60.8203125,
57.314657355733274
],
[
60.74340820312499,
56.26776108757582
],
[
59.227294921875,
56.21281407174654
],
[
59.40307617187499,
57.29685437021898
]
]
}
}
]
}
Script to get type and coordinates of first LineString:
import json
from pprint import pprint
with open('data.geojson') as f:
data = json.load(f)
pprint(data)
data["features"][0]["geometry"]['type']
data["features"][0]["geometry"]['coordinates']
You can achieve that with the json module:
file_line = 'file.json'
file_poly = 'file_poly.json'
import json
with open(file_line, 'r') as f:
data = json.load(f)
for feature in data['features']:
if (feature['geometry']['type'] == 'LineString') & (len(feature['geometry']['coordinates']) >= 3):
feature['geometry']['type'] = 'Polygon'
feature['geometry']['coordinates'].append(feature['geometry']['coordinates'][0])
with open(file_poly, 'w+') as f:
json.dump(data, f, indent=2)

Categories

Resources