2

I would like to reverse the order of the coordinates in this JSON response from (lat,lon) to (lon,lat):

url='https://www.sciencebase.gov/catalogMaps/mapping/ows/5342c5fce4b0aa151574a8ed?\
service=wfs&version=1.1.0&request=GetFeature&typeNames=sb:Conservation_Zone_WGS84&outputFormat=application/json'
response = requests.get(url).json()   
print response

{u'crs': {u'properties': {u'code': u'4326'}, u'type': u'EPSG'},
 u'features': [{u'geometry': {u'coordinates': [[[[39.81487959537135,
        -74.09688169446223],
       [39.81488113835475, -74.09587338924456],
       [39.8143317590967, -74.09614209870023],
       [39.8137616151959, -74.09633047532941],
       [39.812950626580545, -74.09670529470912],
       [39.8120075697193, -74.09698124228382],
       [39.814255381955064, -74.0973277412355],
       [39.81487959537135, -74.09688169446223]]]],
    u'type': u'MultiPolygon'},
   u'geometry_name': u'the_geom',
   u'id': u'Conservation_Zone_WGS84.1',
   u'properties': {u'ID': 1,
    u'NAME': u'Sedge Island Marine Conservation Zone',
    u'OBJECTID': 1,
    u'SHAPE_AREA': 70259289.0821,
    u'SHAPE_LEN': 40592.8006466,
    u'WEB_LINK': u'http://www.state.nj.us/dep/fgw/sedge.htm'},
   u'type': u'Feature'}],
 u'type': u'FeatureCollection'}

I could pull this apart, brute force it, and stick it back together, but I'm wondering: what would be a good pythonic way to change the order while leaving the structure intact?

Rich Signell
  • 12,639
  • 3
  • 40
  • 67

4 Answers4

3

Solution using numpy that should work for any geojson. It will flip all 'coordinates'.

import json
import requests
import numpy as np


def flip_geojson_coordinates(geo):
    if isinstance(geo, dict):
        for k, v in geo.iteritems():
            if k == "coordinates":
                z = np.asarray(geo[k])
                f = z.flatten()
                geo[k] = np.dstack((f[1::2], f[::2])).reshape(z.shape).tolist()
            else:
                flip_geojson_coordinates(v)
    elif isinstance(geo, list):
        for k in geo:
            flip_geojson_coordinates(k)

url = "https://www.sciencebase.gov/catalogMaps/mapping/ows/5342c5fce4b0aa151574a8ed?\
service=wfs&version=1.1.0&request=GetFeature&typeNames=sb:Conservation_Zone_WGS84&outputFormat=application/json"
resp = requests.get(url)
gj = json.loads(resp.text)

print gj
flip_geojson_coordinates(gj)
print gj
kwilcox
  • 46
  • 1
  • I selected this one not because it's the shortest or most elegant, but it does get seem to work with all the geojson responses I've seen so far. Other solutions are cool though -- I learned something from both. – Rich Signell Apr 11 '14 at 22:24
2

Use a list comprehension and re-assign the result to the structure.

There are several lists involved here, so a few loops are needed:

for feature in response['features']:
    feature['geometry']['coordinates'] = [[
        [[long, lat] for lat, long in coords] for coords in poly]
        for poly in feature['geometry']['coordinates']]

This does presume that the structure of 'coordinates' is stable; I see that there is a 'type' key too, you may have to vary how you alter the structure if types other than u'MultiPolygon are used.

This moves the data from:

>>> pprint.pprint(response)
{u'crs': {u'properties': {u'code': u'4326'}, u'type': u'EPSG'},
 u'features': [{u'geometry': {u'coordinates': [[[[39.81487959537135,
                                                  -74.09688169446223],
                                                 [39.81488113835475,
                                                  -74.09587338924456],
                                                 [39.8143317590967,
                                                  -74.09614209870023],
                                                 [39.8137616151959,
                                                  -74.09633047532941],
                                                 ....
                                                 [39.814255381955064,
                                                  -74.0973277412355],
                                                 [39.81487959537135,
                                                  -74.09688169446223]]]],
                              u'type': u'MultiPolygon'},
                u'geometry_name': u'the_geom',
                u'id': u'Conservation_Zone_WGS84.1',
                u'properties': {u'ID': 1,
                                u'NAME': u'Sedge Island Marine Conservation Zone',
                                u'OBJECTID': 1,
                                u'SHAPE_AREA': 70259289.0821,
                                u'SHAPE_LEN': 40592.8006466,
                                u'WEB_LINK': u'http://www.state.nj.us/dep/fgw/sedge.htm'},
                u'type': u'Feature'}],
 u'type': u'FeatureCollection'}

to:

>>> pprint.pprint(response)
{u'crs': {u'properties': {u'code': u'4326'}, u'type': u'EPSG'},
 u'features': [{u'geometry': {u'coordinates': [[[[-74.09688169446223,
                                                  39.81487959537135],
                                                 [-74.09587338924456,
                                                  39.81488113835475],
                                                 [-74.09614209870023,
                                                  39.8143317590967],
                                                 [-74.09633047532941,
                                                  39.8137616151959],
                                                 ....
                                                 [-74.0973277412355,
                                                  39.814255381955064],
                                                 [-74.09688169446223,
                                                  39.81487959537135]]]],
                              u'type': u'MultiPolygon'},
                u'geometry_name': u'the_geom',
                u'id': u'Conservation_Zone_WGS84.1',
                u'properties': {u'ID': 1,
                                u'NAME': u'Sedge Island Marine Conservation Zone',
                                u'OBJECTID': 1,
                                u'SHAPE_AREA': 70259289.0821,
                                u'SHAPE_LEN': 40592.8006466,
                                u'WEB_LINK': u'http://www.state.nj.us/dep/fgw/sedge.htm'},
                u'type': u'Feature'}],
 u'type': u'FeatureCollection'}
Martijn Pieters
  • 889,049
  • 245
  • 3,507
  • 2,997
  • Hmmm.. Right. Ideally I'd like to have this handle the general case, reversing any coordinates that are returned, regardless of the structure of the JSON response. – Rich Signell Apr 11 '14 at 18:16
  • You'd have to survey what values `type` can take, and what the `coordinates` structure looks like in that case. – Martijn Pieters Apr 11 '14 at 18:21
2

Well ... digging down to your coordinates list takes this:

d['features'][0]['geometry']['coordinates'][0][0]

So to reverse those you'd have to do this:

d['features'][0]['geometry']['coordinates'][0][0] = [i[::-1] for i in d['features'][0]['geometry']['coordinates'][0][0]]

Or, a bit cleaner IMO:

for l in d['features'][0]['geometry']['coordinates'][0][0]:
    l.reverse()
g.d.d.c
  • 41,737
  • 8
  • 91
  • 106
  • That is pretty slick. It would be great if any coordinates returned in the JSON could be reversed, as Martijn Pieters points out in his answer. – Rich Signell Apr 11 '14 at 18:19
2

Your "pythonic" code is unreadable and doesn't work. Keep it simple:

def swapCoords(x):
    out = []
    for iter in x:
        if isinstance(iter, list):
            out.append(swapCoords(iter))
        else:
            return [x[1], x[0]]
    return out



for feature in response['features']:
    feature['geometry']['coordinates'] = swapCoords(feature['geometry']['coordinates'])
Travis Webb
  • 13,507
  • 6
  • 51
  • 101