Change start, end lineString coordinate order in geoJson file - python

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)

Related

Python update geojson properties

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'], {}))

How to position attribute in geojson on top of all properties?

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()

JSON compatibility with Mapbox Studio

I am trying to use a JSON file in Mapbox studio for network analysis but it gives me an error:
Input failed. "type" member required on line 1.
The representative sample of JSON file is:
"version": 0.6,
"generator": "Overpass API 0.7.56.2 b688b00f",
"osm3s": {
"timestamp_osm_base": "2020-03-27T11:58:01Z",
"copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL."
},
"elements": [
{
"type": "node",
"id": 123458059,
"lat": -38.3344495,
"lon": 143.5394486
},
{
"type": "node",
"id": 123458066,
"lat": -38.3394461,
"lon": 143.5923655,
"tags": {
"crossing": "traffic_signals",
"highway": "traffic_signals"
}
},
{
"type": "way",
"id": 769574290,
"nodes": [
7183581936,
681081177,
1561328098,
1539139562,
448021781
],
"tags": {
"highway": "trunk",
"lanes": "2",
"maxspeed": "80",
"name": "Princes Highway",
"ref": "A1",
"source:maxspeed:sign": "mapillary"
}
},
{
"type": "way",
"id": 776227225,
"nodes": [
1017428185,
317738200
],
"tags": {
"alt_name": "Princes Highway",
"highway": "trunk",
"lanes": "2",
"maxspeed": "50",
"name": "Murray Street",
"ref": "A1",
"source:maxspeed:sign": "OpenStreetCam",
"source:name": "services.land.vic.gov.au"
}
}
]
}
Does the error occur because of the specification of the format? Do we need to reformat the features or types?
To upload data to Mapbox you will need to convert your JSON file to GeoJSON, a subset of the JSON format. For example:
{"type": "FeatureCollection",
"features": [
{
"geometry": {
"type": "Point",
"coordinates": [
-76.9750541388,
38.8410857803
]
},
"type": "Feature",
"properties": {
"description": "Southern Ave",
"marker-symbol": "rail-metro",
"title": "Southern Ave",
"url": "http://www.wmata.com/rider_tools/pids/showpid.cfm?station_id=107",
"lines": [
"Green"
],
"address": "1411 Southern Avenue, Temple Hills, MD 20748"
}
},
{
"geometry": {
"type": "Point",
"coordinates": [
-76.935256783,
38.9081784965
]
},
"type": "Feature",
"properties": {
"description": "Deanwood",
"marker-symbol": "rail-metro",
"title": "Deanwood",
"url": "http://www.wmata.com/rider_tools/pids/showpid.cfm?station_id=65",
"lines": [
"Orange"
],
"address": "4720 Minnesota Avenue NE, Washington, DC 20019"
}
}
]}
Mapbox accepts GeoJSON, you'll need to ask Overpass to return data as GeoJSON.

Replace multiple keys and values of JSON file in Python

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

Invalid GeoJson (Data was not JSON serializable)

I am fairly new to the geojson spec...and formatting is causing havok.
All I am trying to do is build a new "features" list (for Points only) which I am adding new 'properties' for. Then I write it into (test.json).
Right now I am returning which has intermittent cases of "type": "FeatureCollection" (I was only expecting to see it once - at the top of the file) and some bad } syntax errors:
{"type": "FeatureCollection",
"features": [
{"geometry": {
"type": "Point",
"coordinates": [-122.3447075, 47.6821492]},
"type": "Feature",
"properties": {
"marker-color": "#808080",
"timestamp": "2013-08-17T22:41:18Z",
"version": 3,
"user": "seattlefyi",
"last_updated": "over a year ago",
"id": 427307160,
"marker-size": "small"
}}, ## what??
]} ## what??
{"type": "FeatureCollection",
"features": [
{"geometry": {
"type": "Point",
"coordinates": [-122.3447075, 47.6821492]},
"type": "Feature",
"properties": {
"marker-color": "#808080",
"timestamp": "2013-08-17T22:41:18Z",
"version": 3,
"user": "seattlefyi",
"last_updated": "over a year ago",
"id": 427307160,
"marker-size": "small"
}}, ## what...no "type": "FeatureCollection" on this one?
{"geometry": {
"type": "Point",
"coordinates": [-122.377932, 47.5641566]},
"type": "Feature",
"properties": {
"marker-color": "#808080",
"timestamp": "2009-07-11T04:04:51Z",
"version": 1,
"user": "Rob Lanphier",
"last_updated": "over a year ago",
"id": 439976119,
"marker-size": "small"
}
}
]
}
However, I'm trying to return
{"type": "FeatureCollection",
"features": [
{"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-122.3447075, 47.6821492]},
"properties": {
"marker-color": "#808080",
"timestamp": "2013-08-17T22:41:18Z",
"version": 3,
"user": "ralph",
"last_updated": "over a year ago",
"id": 427307160,
"marker-size": "small"
}},
{"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-122.377932, 47.5641566]},
"properties": {
"marker-color": "#808080",
"timestamp": "2009-07-11T04:04:51Z",
"version": 1,
"user": "Rob Lanphier",
"last_updated": "over a year ago",
"id": 439976119,
"marker-size": "small"
}
}
]
}
Code is:
def write_to_features(source, class_time, color):
""" write the json into geojson
takes all the items from one node ("lat", "lon", "id", "user", "tags", "timestamp")
writes all the items with new tags "last_updated","marker-color","marker-size"
returns a dict
"""
pt = {
"type": "Feature",
"geometry": {
"type": 'Point',
"coordinates": [float(source['lon']), float(source['lat'])]
},
"properties": {
"user": source['user'],
"id": source['id'],
"version": source['version'],
"timestamp": source['timestamp'],
"last_updated": classified_time,
"marker-color": marker_color,
"marker-size": "small"
}
}
return pt
def __main__():
geojson = { "type": "FeatureCollection", "features": [] }
outfile = r'.\test.json'
with open(outfile, 'w') as geojson_file:
for item in all_data_dict['elements']:
point_dict = write_to_features(item, data_w_update, data_item_color)
geojson['features'].append(point_dict)
json.dump(geojson, geojson_file)
Shouldn't your json.dump(geojson, geojson_file) be outside your loop?you append in the line above it..so I'm questioning why would you dump/write to the file multiple times?I would think you should only be calling json.dump once.

Categories