Extracting coordinates (lat, lon) from MultiLineString GeoJSON in Python - 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])

Related

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]
]
]
}
}
]
}

timestamp geojson with grouping feature layer control

I want to visualize geo-data with timestamp information on it. I use plugins.TimestampedGeoJson in my python code to do that. the code is working actually but i need to display more than 1 feature in the map and each of these features can be shown/hidden from LayerControl.
is there any idea how to do that ?
#example with one feature
features_3 = []
for row in DF_3.itertuples():
long = row.longitude
lat = row.latitude
data= row.data
features_3 .append (
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [long,lat],
},
"properties": {
"time": str(row.xdate),
"popup": "Record : " + str(data) ,
"icon": "circle",
"iconstyle": {
"fillColor": color_scale(data),
"fillOpacity": 0.6,
"stroke": "false",
"radius": 8,
},
"style": {"weight": 0},
},
}
)
plugins.TimestampedGeoJson(
{"type": "FeatureCollection", "features": features_3 },
period="P1D",
add_last_point=True,
auto_play=True,
loop=False,
max_speed=1,
loop_button=True,
time_slider_drag_update=True,
duration="P1D",
).add_to(map)
LayerControl().add_to(map)
TimestampedGeoJson duration parameter causes polygons to disappear
The code in this link might help you. You can see how it's adding multiple polygon layers in the same map.

Difference between osrm nearest and osrm match services

I want to match gps coordinates to osm nodes. The osrm document clearly states that match service is for that purpose. But I see most of them using nearest service for that purpose. Why so? Also any examples of Match service and what is the main difference between the two service for the matching operation.Which one is better?
As per the OSRM API Documentation,
the neareast service a query endpoint where the nearest number of waypoint objects to the snapped coordinates (single coordinate allowed) are returned.
GET http://{server}/nearest/v1/{profile}/{coordinates}.json?number={number}
On the other hand, as the name suggests match service does something quite different and as one can say more complex. It accepts multiple coordinates and matches these points in a plausible way to the route network. As it can be seen from the dummy and a bit manipulated response; matchings array defines the confidence (calculated by OSRM), geometry (provided in arguments), legs (defines the route details annotations and overview arguments provide flexibility) and tracepoints array that lists the waypoints with additional properties.
GET /match/v1/{profile}/{coordinates}?steps={true|false}&geometries={polyline|polyline6|geojson}&overview={simplified|full|false}&annotations={true|false}
coordinates: comma separated GPS points
steps: route steps for each route
geometries: 3 formats are supported for route geometry.
overview: adds the overview geometry according to the given value.
annotations: returns additional metadata for each coordinate.
Dummy Match service response overview as it is not very clearly provided:
{
"code": "Ok",
"matchings": [
{
"confidence": 0.980739,
"geometry": {
"coordinates": [
# coordinates here
],
"type": "LineString"
},
"legs": [
{
"annotation": {
# optional
},
"steps": [
{
"intersections": [
{
"out": 0,
"entry": [
true
],
"bearings": [
12
],
"location": [
28.98672,
41.041189
]
}
],
"driving_side": "right",
"geometry": {
"coordinates": [
# coordinates here
],
"type": "LineString"
},
"mode": "driving",
"duration": 95.6,
"maneuver": {
},
"weight": 95.6,
"distance": 796.3,
"name": "Cumhuriyet Caddesi"
},
],
"distance": 1250.9,
"duration": 150.2,
"summary": "Cumhuriyet Caddesi, Halaskargazi Caddesi",
"weight": 150.2
}
],
"distance": 1250.9,
"duration": 150.2,
"weight_name": "routability",
"weight": 150.2
}
],
"tracepoints": [
{
# waypoint object with additional fields
}
]
}

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

Accessing nested python Dict of coordinates in GeoJSON

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)

Categories

Resources