EDIT: I am trying to manipulate JSON files in Python. In my data some polygons have multiple related information: coordinates (LineString) and area percent and area (Text and Area in Point), I want to combine them to a single JSON object. As an example, the data from files are as follows:
data = {
"type": "FeatureCollection",
"name": "entities",
"features": [{
"type": "Feature",
"properties": {
"Layer": "0",
"SubClasses": "AcDbEntity:AcDbBlockReference",
"EntityHandle": "2F1"
},
"geometry": {
"type": "LineString",
"coordinates": [
[61.971069681118479, 36.504485105673659],
[46.471068755199667, 36.504485105673659],
[46.471068755199667, 35.954489281866685],
[44.371068755199758, 35.954489281866685],
[44.371068755199758, 36.10448936390457],
[43.371069617387093, 36.104489150107824],
[43.371069617387093, 23.904496401184584],
[48.172716774891342, 23.904496401184584],
[48.171892994728751, 17.404489374370311],
[61.17106949647404, 17.404489281863786],
[61.17106949647404, 19.404489281863786],
[61.971069689453991, 19.404489282256687],
[61.971069681118479, 36.504485105673659]
]
}
},
{
"type": "Feature",
"properties": {
"Layer": "0",
"SubClasses": "AcDbEntity:AcDbMText",
"EntityHandle": "2F1",
"Text": "6%"
},
"geometry": {
"type": "Point",
"coordinates": [49.745686139884583, 28.11445704760262, 0.0]
}
},
{
"type": "Feature",
"properties": {
"Layer": "0",
"SubClasses": "AcDbEntity:AcDbMText",
"EntityHandle": "2F1",
"Area": "100"
},
"geometry": {
"type": "Point",
"coordinates": [50.216857362443989, 63.981197759829229, 0.0]
}
},
{
"type": "Feature",
"properties": {
"Layer": "0",
"SubClasses": "AcDbEntity:AcDbBlockReference",
"EntityHandle": "2F7"
},
"geometry": {
"type": "LineString",
"coordinates": [
[62.37106968111857, 36.504489398648715],
[62.371069689452725, 19.404489281863786],
[63.171069496474047, 19.404489281863786],
[63.171069496474047, 17.404489281863786],
[77.921070051947027, 17.404489281863786],
[77.921070051947027, 19.504489281855054],
[78.671070051947027, 19.504489281855054],
[78.671070051897914, 36.504485105717322],
[62.37106968111857, 36.504489398648715]
]
}
},
{
"type": "Feature",
"properties": {
"Layer": "0",
"SubClasses": "AcDbEntity:AcDbMText",
"EntityHandle": "2F7",
"Text": "5.8%"
},
"geometry": {
"type": "Point",
"coordinates": [67.27548061311245, 28.11445704760262, 0.0]
}
}
]
}
I want to combine Point's Text and Area key and values to LineString based on EntityHandle's values, and also delete Point lines. The expected output is:
{
"type": "FeatureCollection",
"name": "entities",
"features": [{
"type": "Feature",
"properties": {
"Layer": "0",
"SubClasses": "AcDbEntity:AcDbBlockReference",
"EntityHandle": "2F1",
"Text": "6%",
"Area": "100"
},
"geometry": {
"type": "LineString",
"coordinates": [
[61.971069681118479, 36.504485105673659],
[46.471068755199667, 36.504485105673659],
[46.471068755199667, 35.954489281866685],
[44.371068755199758, 35.954489281866685],
[44.371068755199758, 36.10448936390457],
[43.371069617387093, 36.104489150107824],
[43.371069617387093, 23.904496401184584],
[48.172716774891342, 23.904496401184584],
[48.171892994728751, 17.404489374370311],
[61.17106949647404, 17.404489281863786],
[61.17106949647404, 19.404489281863786],
[61.971069689453991, 19.404489282256687],
[61.971069681118479, 36.504485105673659]
]
}
},
{
"type": "Feature",
"properties": {
"Layer": "0",
"SubClasses": "AcDbEntity:AcDbBlockReference",
"EntityHandle": "2F7",
"Text": "5.8%"
},
"geometry": {
"type": "LineString",
"coordinates": [
[62.37106968111857, 36.504489398648715],
[62.371069689452725, 19.404489281863786],
[63.171069496474047, 19.404489281863786],
[63.171069496474047, 17.404489281863786],
[77.921070051947027, 17.404489281863786],
[77.921070051947027, 19.504489281855054],
[78.671070051947027, 19.504489281855054],
[78.671070051897914, 36.504485105717322],
[62.37106968111857, 36.504489398648715]
]
}
}
]
}
Is it possible to get result above in Python? Thanks.
Updated solution, thanks to #dodopy:
import json
features = data["features"]
point_handle_text = {
i["properties"]["EntityHandle"]: i["properties"]["Text"]
for i in features
if i["geometry"]["type"] == "Point"
}
point_handle_area = {
i["properties"]["EntityHandle"]: i["properties"]["Area"]
for i in features
if i["geometry"]["type"] == "Point"
}
combine_features = []
for i in features:
if i["geometry"]["type"] == "LineString":
i["properties"]["Text"] = point_handle_text.get(i["properties"]["EntityHandle"])
combine_features.append(i)
data["features"] = combine_features
combine_features = []
for i in features:
if i["geometry"]["type"] == "LineString":
i["properties"]["Area"] = point_handle_area.get(i["properties"]["EntityHandle"])
combine_features.append(i)
data["features"] = combine_features
with open('test.geojson', 'w+') as f:
json.dump(data, f, indent=2)
But I get an error:
Traceback (most recent call last):
File "<ipython-input-131-d132c8854a9c>", line 6, in <module>
for i in features
File "<ipython-input-131-d132c8854a9c>", line 7, in <dictcomp>
if i["geometry"]["type"] == "Point"
KeyError: 'Text'
example like this:
import json
data = json.loads(json_data)
features = data["features"]
point_handle_text = {
i["properties"]["EntityHandle"]: i["properties"]["Text"]
for i in features
if i["geometry"]["type"] == "Point"
}
combine_features = []
for i in features:
if i["geometry"]["type"] == "LineString":
i["properties"]["Text"] = point_handle_text.get(i["properties"]["EntityHandle"])
combine_features.append(i)
data["features"] = combine_features
json_data = json.dumps(data)
Yes, it is possible to get your result in python. It just requires storing the json data into a data structure we can work with in python and then writing an algorithm to combine features with the same entity type. I wrote up a script to do just that, along with comments. The program extracts the text property from the Point feature and places it into the properties of the LineString feature. Then, we essentially discard Point.
BTW, your 'before' json data has a trailing comma that shouldn't be there.
Using Python 3.7.0:
import json
import collections
def main():
with open('before_data.json') as f:
before_data = json.load(f) # makes a python dict from the json file and stores in before
features = before_data['features'] # list of features
# loop through features, construct dictionary of entity handle mapped to point texts
point_entities = collections.defaultdict() # to avoid 'if key not in' pattern
for feature in features:
entity_handle = feature['properties']['EntityHandle']
# only append points
if feature['geometry']['type'] == 'Point':
point_entities[entity_handle] = feature['properties']['Text']
merged_features = []
for feature in features:
if feature['geometry']['type'] == 'LineString':
entity_handle = feature['properties']['EntityHandle']
text_percent = point_entities[entity_handle]
feature['properties']['Text'] = text_percent
merged_features.append(feature)
# print(json.dumps(before_data, indent=4))
result = before_data
result['features'] = merged_features
# compare with your expected output
with open('after_data.json') as f:
after_data = json.load(f)
print(result == after_data) # returns True
# finally, write your result to a file
with open('result.json', 'w') as output_file:
json.dump(result, output_file)
if __name__ == '__main__':
main()
Related
I have a .geojson file with many lineStrings with the location of transects used to monitor shoreline change, so each transect runs across a land/water boundary. When visualized as is, the transects originate offshore and end onshore. For an analysis tool I am using, I need the locations to be swapped: the first coordinate needs to start on land and end offshore. I will have many thousands of these transects to change and want to make sure I'm doing it correctly but can't seem to figure out this very simple task (sorry, I am new here). I am working in python and earth engine.
# original
{
"type": "FeatureCollection",
"name": "EastChukci_small_testArea",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::3857" } },
"features": [
{ "type": "Feature", "properties": { "name": 2722 }, "geometry": { "type": "LineString", "coordinates": [ [ -17592698.71288351342082, 11344741.029055444523692 ], [ -17592054.347651835530996, 11343198.733621645718813 ] ] } },
{ "type": "Feature", "properties": { "name": 2723 }, "geometry": { "type": "LineString", "coordinates": [ [ -17592838.831736516207457, 11344682.393273767083883 ], [ -17592194.440066188573837, 11343140.124529516324401 ] ] } },
{ "type": "Feature", "properties": { "name": 2724 }, "geometry": { "type": "LineString", "coordinates": [ [ -17592978.948162343353033, 11344623.755085829645395 ], [ -17592334.530055023729801, 11343081.513031836599112 ] ] } },
]
}
# desired
{
"type": "FeatureCollection",
"name": "EastChukci_small_testArea",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::3857" } },
"features": [
{ "type": "Feature", "properties": { "name": 2722 }, "geometry": { "type": "LineString", "coordinates": [[ -17592054.347651835530996, 11343198.733621645718813 ], [ -17592698.71288351342082, 11344741.029055444523692 ] ] } },
{ "type": "Feature", "properties": { "name": 2723 }, "geometry": { "type": "LineString", "coordinates": [ [ -17592194.440066188573837, 11343140.124529516324401 ], [ -17592838.831736516207457, 11344682.393273767083883 ] ] } },
{ "type": "Feature", "properties": { "name": 2724 }, "geometry": { "type": "LineString", "coordinates": [ [ -17592334.530055023729801, 11343081.513031836599112 ] ], [ -17592978.948162343353033, 11344623.755085829645395 ] ] } },
]
}
Thanks in advance.
To read and write a json file you can use json module. The code below should solve your problem, but the downside of it is that it loads the whole file to the memory at once.
import json
with open('data.json', 'r') as json_file:
data = json.load(json_file)
for feature_data in data['features']:
feature_data['geometry']['coordinates'].reverse()
with open('data.json', 'w') as json_file:
json.dump(data, json_file)
I have a geojson feature collection dataset with a lot of features. I want to add/update the properties of each feature with properties of a json file. The unique identifier of both datasets is the "uuid" value.
This is the geojson format:
mtx = {
"type": "FeatureCollection",
"crs": {
"type": "name",
"properties": {
"name": "EPSG:4326"
}
},
"features": [
{
"type": "Feature",
"id": 1,
"geometry": {
"type": "Point",
"coordinates": [
5.36516933279853,
51.5510854507331
]
},
"properties": {
"OBJECTID": 1,
"PK_UID": 1,
"uuid": "1efa8916-c854-465b-80f5-1f02fd25fb31",
"road": "A2",
"lane": 1,
"km": 134.96,
"bearing": 148.02261,
"locid": "A2134.96"
}
},
{
"type": "Feature",
"id": 2,
"geometry": {
"type": "Point",
"coordinates": [
5.05380200345974,
52.3264095459638
]
},
"properties": {
"OBJECTID": 2,
"PK_UID": 2,
"uuid": "73bf3758-6754-433f-9896-d03c0673ae55",
"road": "A1",
"lane": 3,
"km": 11.593,
"bearing": 113.404253,
"locid": "A111.593"
}
}
]
}
And this is the json format:
msi= [
{
"uuid": "1efa8916-c854-465b-80f5-1f02fd25fb31",
"road": "A2",
"carriageway": "R",
"lane": "1",
"km": "134.960",
"display": "blank",
"display_attrs": "{'flashing': 'false'}",
"speedlimit": "null"
},
{
"uuid": "73bf3758-6754-433f-9896-d03c0673ae55",
"road": "A1",
"carriageway": "R",
"lane": "3",
"km": "11.593",
"display": "blank",
"display_attrs": "{'flashing': 'false'}",
"speedlimit": "null"
}
]
So how can I make a python script that loop through the geojson features and update each feature properties with the matching properties from the json based on the "uuid" value?
I tried something like this but this didn't give me the expected result:
#Loop over GeoJSON features and update the new properties from msi json
for feat in mtx['features']:
for i in range(len(msi)):
if mtx['features'][i]['properties']['uuid'] == msi[i]['uuid']:
feat ['properties'].update(msi[i])
Thanks for helping me out.
I'd do something like this (completely untested, so beware of mistakes...):
# Turn msi into a dictionary for easy access
msi_dict = {m['uuid']: m for m in msi}
for f in mtx['features']:
f['properties'].update(msi_dict.get(f['properties']['uuid'], {}))
I am trying to remove feature_id property from properties array and move upwards.
with open('test.geojson', 'r+') as gjson:
data = json.load(gjson)
for l in range(0, len(data['features'])):
data['features'][l]['id'] = data['features'][l]['properties']['feature_id']
del data['features'][l]['properties']['feature_id']
gjson.seek(0)
json.dump(data, gjson, indent=4)
gjson.truncate()
This is the input.
{
"type": "FeatureCollection",
"name": "name",
"features": [
{
"type": "Feature",
"properties": {
"feature_id": "1e181120-2047-4f97-a359-942ef5940da1",
"type": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[...]
]
}
}
]
}
It does the job but adds the property at the bottom
{
"type": "FeatureCollection",
"name": "name",
"features": [
{
"type": "Feature",
"properties": {
"type": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[..]
]
},
"id": "1e181120-2047-4f97-a359-942ef5940da1"
}
]
}
As you can see id is added at last but it should be on top before properties.
You can use OrderedDict for that.
with open('test.geojson', 'r+') as gjson:
data = json.load(gjson, object_pairs_hook=OrderedDict)
for l in range(0, len(data['features'])):
d = data['features'][l]
d['id'] = data['features'][l]['properties']['feature_id']
d.move_to_end('id', last=False)
del d['properties']['feature_id']
gjson.seek(0)
json.dump(data, gjson, indent=4)
gjson.truncate()
I've a very large JSON file(>60MB), which i was unable to open and edit in any editors. I see that we can do it using python.
Here is my sample data.
[
{
"type": "Feature",
"geometry": {
"type": "MultiPolygon"
},
"id": "94601"
},
{
"type": "Feature",
"geometry": {
"type": "MultiPolygon"
},
"id": "94801"
}
]
and my expected output is
{
"type": "FeatureCollection",
"features":[
{
"type": "Feature",
"geometry": {
"type": "MultiPolygon"
},
"id": "94601"
},
{
"type": "Feature",
"geometry": {
"type": "MultiPolygon"
},
"id": "94801"
}
]
}
I'm not sure of how can I do it in python. Here is the code that I'm using to read the file. But not sure of how to proceed further.
import json
f = open('/Users/Downloads/out_ca_california_zip_codes_geo.json', 'r')
out_file = open("/Users/Downloads/outNew_ca_california_zip_codes_geo.json", "w")
out_file.write(json.dumps(myFinalResult))
Thanks
Decode your json which then become a Python dictionary then you can simply add your key to that, then re-encode and rewrite the file.
Try this:
import json
with open(json_file) as json_file:
json_decoded = json.load(json_file)
json_decoded['ADDED_KEY'] = 'ADDED_VALUE'
with open(json_file, 'w') as json_file:
json.dump(json_decoded, json_file)
For more details check here: https://docs.python.org/3.4/library/json.html
For geojson type file named data as follows:
{
"type": "FeatureCollection",
"name": "entities",
"features": [{
"type": "Feature",
"properties": {
"Layer": "0",
"SubClasses": "AcDbEntity:AcDbPolyline",
"EntityHandle": "1A0"
},
"geometry": {
"type": "LineString",
"coordinates": [
[3220.136443006845184, 3001.530372177397112],
[3847.34171007254281, 3000.86074447018018],
[3847.34171007254281, 2785.240077064262096],
[3260.34191304818205, 2785.240077064262096],
[3260.34191304818205, 2795.954148466309107]
]
}
},
{
"type": "Feature",
"properties": {
"Layer": "0",
"SubClasses": "AcDbEntity:AcDbPolyline",
"EntityHandle": "1A4"
},
"geometry": {
"type": "LineString",
"coordinates": [
[3611.469650131302842, 2846.845982610575902],
[3695.231030111376185, 2846.845982610575902],
[3695.231030111376185, 2785.240077064262096],
[3611.469650131302842, 2785.240077064262096],
[3611.469650131302842, 2846.845982610575902]
]
}
}
]
}
I hope to realize the following manipulation to data:
replace key EntityHandle with Name;
replace EntityHandle's value from hex number with 'sf_001', 'sf_002', 'sf_003', etc;
replace type's value LineString with Polygon;
replace coordinates's two square brackets with three square brackets.
This is expected output:
{
"type": "FeatureCollection",
"name": "entities",
"features": [{
"type": "Feature",
"properties": {
"Layer": "0",
"SubClasses": "AcDbEntity:AcDbPolyline",
"Name": "sf_001"
},
"geometry": {
"type": "Polygon",
"coordinates": [ [
[3220.136443006845184, 3001.530372177397112],
[3847.34171007254281, 3000.86074447018018],
[3847.34171007254281, 2785.240077064262096],
[3260.34191304818205, 2785.240077064262096],
[3260.34191304818205, 2795.954148466309107]
] ]
}
},
{
"type": "Feature",
"properties": {
"Layer": "0",
"SubClasses": "AcDbEntity:AcDbPolyline",
"Name": "sf_002"
},
"geometry": {
"type": "Polygon",
"coordinates": [ [
[3611.469650131302842, 2846.845982610575902],
[3695.231030111376185, 2846.845982610575902],
[3695.231030111376185, 2785.240077064262096],
[3611.469650131302842, 2785.240077064262096],
[3611.469650131302842, 2846.845982610575902]
] ]
}
}
]
}
I'm new in JSON file manipulation using Python. Please help me, thanks at advance.
import json
from pprint import pprint
with open('data.geojson') as f:
data = json.load(f)
pprint(data)
for feature in data['features']:
#print(feature)
print(feature['properties']['EntityHandle'])
for feature in data['features']:
feature['properties']['EntityHandle'] = feature['properties']['Name'] #Rename `EntityHandle` to `Name`
del feature['properties']['EntityHandle']
Result:
Traceback (most recent call last):
File "<ipython-input-79-8b30b71fedf9>", line 2, in <module>
feature['properties']['EntityHandle'] = feature['properties']['Name']
KeyError: 'Name'
Give this a shot:
You can rename a key by using:
dict['NewKey'] = dict.pop('OldKey')
And to replace a value, it's just as simple as setting the new vale equal to the specific key:
dict['Key'] = new_value
Full Code:
data = {
"type": "FeatureCollection",
"name": "entities",
"features": [{
"type": "Feature",
"properties": {
"Layer": "0",
"SubClasses": "AcDbEntity:AcDbPolyline",
"EntityHandle": "1A0"
},
"geometry": {
"type": "LineString",
"coordinates": [
[3220.136443006845184, 3001.530372177397112],
[3847.34171007254281, 3000.86074447018018],
[3847.34171007254281, 2785.240077064262096],
[3260.34191304818205, 2785.240077064262096],
[3260.34191304818205, 2795.954148466309107]
]
}
},
{
"type": "Feature",
"properties": {
"Layer": "0",
"SubClasses": "AcDbEntity:AcDbPolyline",
"EntityHandle": "1A4"
},
"geometry": {
"type": "LineString",
"coordinates": [
[3611.469650131302842, 2846.845982610575902],
[3695.231030111376185, 2846.845982610575902],
[3695.231030111376185, 2785.240077064262096],
[3611.469650131302842, 2785.240077064262096],
[3611.469650131302842, 2846.845982610575902]
]
}
}
]
}
sf_count = 0
for feature in data['features']:
feature['properties']['Name'] = feature['properties'].pop('EntityHandle') #Rename `EntityHandle` to `Name`
sf_count += 1
feature['properties']['Name'] = 'sf_%.3d' %sf_count # Replace hex with incrimental sf_xxx
feature['geometry']['type'] = 'Polygon' # Replace `LineString` to `Polygon`
feature['geometry']['coordinates'] = [[ each for each in feature['geometry']['coordinates'] ]]