Hey I'm new to python and working on a uni assignment and need help. So I have a query of cities and some properties to go with them and i need to print out a list of the bordering cities and the cities more south and west of the city of Guelph. Apparently it can be done in 3 lines but I just want to figure out how to get my code to work for now.
ontario = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "Guelph",
"border town?": "false"
},
"geometry": {
"type": "Point",
"coordinates": [-80.255126953125, 43.545561990655855]
}
}
]
}
citiesInOntario = ontario["features"]
for city in citiesInOntario:
if(city["properties"]["border town?"] == "true" and \
city["geometry"]["coordinates"][0] < -80.255 and \
city["geometry"]["coordinates"] < 43.545):
print(city["properties"]["name"])
As people have pointed out in the comments, your conditions for the coordinates don't match
city["geometry"]["coordinates"][0] < -80.255 and \
city["geometry"]["coordinates"] < 43.545):
In the first one you're getting the first coordinate value from the list city["geometry"]["coordinates"] and comparing it, but in the second condition you're comparing the entire list to a number. You just need to reference the second element of the list with city["geometry"]["coordinates"][1].
city["geometry"]["coordinates"][0] < -80.255 and \
city["geometry"]["coordinates"][1] < 43.545)
Also consider changing your "true" and "false" strings to Python's True and False:
ontario = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "Guelph",
"borders_town": False
},
"geometry": {
"type": "Point",
"coordinates": [-80.255126953125, 43.545561990655855]
}
}
]
}
so that your new if condition is now:
if(city["properties"]["borders_town"] and ...
Related
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]
]
]
}
}
]
}
I have a python dictionary that looks something like this:
[
{
"timestamp": 1621559698154,
"user": {
"uri": "spotify:user:xxxxxxxxxxxxxxxxxxxx",
"name": "Panda",
"imageUrl": "https://i.scdn.co/image/ab67757000003b82b54c68ed19f1047912529ef4"
},
"track": {
"uri": "spotify:track:6SJSOont5dooK2IXQoolNQ",
"name": "Dirty",
"imageUrl": "http://i.scdn.co/image/ab67616d0000b273a36e3d46e406deebdd5eafb0",
"album": {
"uri": "spotify:album:0NMpswZbEcswI3OIe6ml3Y",
"name": "Dirty (Live)"
},
"artist": {
"uri": "spotify:artist:4ZgQDCtRqZlhLswVS6MHN4",
"name": "grandson"
},
"context": {
"uri": "spotify:artist:4ZgQDCtRqZlhLswVS6MHN4",
"name": "grandson",
"index": 0
}
}
},
{
"timestamp": 1621816159299,
"user": {
"uri": "spotify:user:xxxxxxxxxxxxxxxxxxxxxxxx",
"name": "maja",
"imageUrl": "https://i.scdn.co/image/ab67757000003b8286459151d5426f5a9e77cfee"
},
"track": {
"uri": "spotify:track:172rW45GEnGoJUuWfm1drt",
"name": "Your Best American Girl",
"imageUrl": "http://i.scdn.co/image/ab67616d0000b27351630f0f26aff5bbf9e10835",
"album": {
"uri": "spotify:album:16i5KnBjWgUtwOO7sVMnJB",
"name": "Puberty 2"
},
"artist": {
"uri": "spotify:artist:2uYWxilOVlUdk4oV9DvwqK",
"name": "Mitski"
},
"context": {
"uri": "spotify:playlist:0tol7yRYYfiPJ17BuJQKu2",
"name": "I Bet on Losing Dogs",
"index": 0
}
}
}
]
How can I get, for example, the group of values for user.name "Panda" and then get that specific "track" list? I can't parse through the list by index because the list order changes randomly.
If you are only looking for "Panda", then you can just loop over the list, check whether the name is "Panda", and then retrieve the track list accordingly.
Otherwise, that would be inefficient if you want to do that for many different users. I would first make a dict that maps user to its index in the list, and then use that for each user (I am assuming that the list does not get modified while you execute the code, although it can be modified between executions.)
user_to_id = {data[i]['user']['name']: i for i in range(len(data))} # {'Panda': 0, 'maja': 1}
def get_track(user):
return data[user_to_id[user]]['track']
print(get_track('maja'))
print(get_track('Panda'))
where data is the list you provided.
Or, perhaps just make a dictionary of tracks directly:
tracks = {item['user']['name']: item['track'] for item in data}
print(tracks['Panda'])
If you want to get list of tracks for user Panda:
tracks = [entry['track'] for entry in data if entry['user']['name'] == 'Panda']
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
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)
A GeoJson with point features contains two attributes: City and Rating.
City as identifier is never changing, but Rating will be updated on a regular basis.
The new Rating is stored in a dictionary as vales ("dnew").
My for loop is not working well. Please see the code below, where "#here is the problem" marks the problem which I cannot solve.
import json
dnew = {"Budapest": "fair", "New York": "very good", "Rome": "awesome"}
data = {
"type": "FeatureCollection",
"name": "Cities",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "City": "New York", "Rating": "good" }, "geometry": { "type": "Point", "coordinates": [ -73.991836734693834, 40.736734693877537 ] } },
{ "type": "Feature", "properties": { "City": "Rome", "Rating": "fair" }, "geometry": { "type": "Point", "coordinates": [ 12.494557823129199, 41.903401360544223 ] } },
{ "type": "Feature", "properties": { "City": "Budapest", "Rating": "awesome" }, "geometry": { "type": "Point", "coordinates": [ 19.091836734693832, 47.494557823129256 ] } }
]
}
#at this point, keys of two dictionaies are compared. If they are the same, the value of the old dict is updated/replaced by the value of the new dict
for key in data["features"]:
citykey = (key["properties"]["City"])
ratingvalue = (key["properties"]["Rating"])
#print(citykey + "| " + ratingvalue)
for keynew in dnew:
citynew = (keynew)
ratingnew = dnew[keynew]
#print(citynew + " | " + ratingnew)
print(citykey + "==" + citynew)
if citykey == citynew:
#
#here is the problem
#
data["features"]["properties"]["Rating"] = ratingnew
print(True)
else:
print(False)
Error Message:
TypeError: list indices must be integers or slices, not str
Thank you!
It misses a number index after "features" as it's a list not a dictionary.
data["features"][0]["properties"]["Rating"]
You're missing the benefits of dictionaries by looping over all the keys in the dnew dictionary for each element in the data['features'] list.
E.Coms noted the problem you're having but that only checks the first item in the list, (data["features"][0])
Perhaps the following will solve your problem.
for key in data["features"]:
citykey = (key["properties"]["City"])
ratingvalue = (key["properties"]["Rating"])
#print(citykey + "| " + ratingvalue)
if citykey in dnew:
key["properties"]["Rating"] = dnew[citykey]