ralfwessels
ralfwessels

Reputation: 51

read and analyze JSON from osm overpass API - get polylines

with Overpass API I get this (polyline) data from OSM as JSON file:

{
  "version": 0.6,
  "generator": "Overpass API",
  "elements": [
{
  "type": "node",
  "id": 308240514,
  "lat": 52.7074546,
  "lon": 7.1369361
},
{
  "type": "node",
  "id": 308729130,
  "lat": 52.6934662,
  "lon": 7.1353250
},
......
.......
.......
{
  "type": "way",
  "id": 99421713,
  "nodes": [
    1149813380,
    2103522316,
    2103522207,
    2103522202,
    2103522201,
    .....
    ....
    ],
      "tags": {
    "admin_level": "2",
    ......
  }
},
{
  "type": "way",
  "id": 99421718,
  "nodes": [
    647317213,
    2103495916,
    2103495906,
    2103495902,
    2103495901,
    ....
    ....
    ....
    ]

For printing the polylines (ways) in a mapping application like Google Maps API I need to get the coordinates (lat,lon in JSON) assigned to the ways (type:way) by the numbers in the nodes array - these numbers are the id's of the coordinates. As result I need something like this:

"coords":{
"way1" : [(37.772323, -122.214897), (21.291982, -157.821856),(-18.142599, 178.431),(-27.46758, 153.027892)],
"way2" : [(37.772323, -122.214897),...........] 

I used jquery to get the JSON file and then loop through the data, so I could get the coordinates, but not assigned to the ways and also not in the right order like the nodes array.

$.getJSON(url, function(data) {
   $.each(data.elements, function(i,data){
      var coords = (data.lat,data.lon);
      .........

Anyone has an idea how to solve my problem? Is jquery solutions for this or it is better to use native javascript?

... 2 days later:

After a few hours of testing and trying at least I found a solution for my problem. Here is the javascript-code:

$.getJSON('test.js', function(data) {

var ways = [];
var way_nodes = [];
var inhalt = [];

for (var x in data.elements) {
    if (data.elements[x].type == "way") {
        var way_tmp = data.elements[x].nodes;
        ways.push(way_tmp);
    }
    if (data.elements[x].type == "node") {
        inhalt = data.elements;
    }
}

for (var h in ways) {
    var mypath = [];
    way_nodes = ways[h];
    for (var k in way_nodes) {
        for (var x in inhalt) {
            if (way_nodes[k] == inhalt[x].id) {
                var coords = new google.maps.LatLng(inhalt[x].lat,inhalt[x].lon);  
                mypath.push(coords);
            }
        }
    }

    var polyline = new google.maps.Polyline({
        path: mypath,
        strokeColor: "#FF0000",
        strokeOpacity: 0.6,
        strokeWeight: 5
    });

    var poly_points = polyline.getPath();
    for (var i = 0; i < poly_points.length; i++) {
        bounds.extend(poly_points.getAt(i));
    }   
    polyline.setMap(map);
}
    map.fitBounds(bounds);
});

And here is a link to a working example displaying with the Google Maps API: http://www.ralf-wessels.de/test/apiv3/json/04map_osm_viele_polylines_structured.html# I don't know if this is the smartest way to solve the problem, specially if I work with big data. If someone knows a better way, I'm interested in this.

Upvotes: 4

Views: 2569

Answers (1)

Juho
Juho

Reputation: 427

For your data manipulation needs, I'd suggest looking into functional libraries like Lodash. Or better yet, Ramda. Lodash is more popular, Ramda is more convenient with emphasis on currying and function composition. Both share the upside of breaking things down to small, easily manageable parts.

There's a bit of a learning curve, but after learning such a tool, you'll find just how much of a pain data manipulation with for-loops was.

For example with Ramda, the same functionality could be achieved like this:

var parseWaysFromResponse = (function () {
    // function [{id:1, key1:val1 ...}, {id:2, key2:val2}]
    //    -> {1:{key1:val1 ...}, 2:{key2:val2 ...}}
    var generateIdToNodeMap = R.compose(
        R.mapObj(R.head),
        R.groupBy(R.prop('id'))
    );

    // Filter array of objects based on key "type"
    var elementTypeIs = function(typeVal) {
      return R.propEq('type', typeVal);
    }

    // Create {id:{values}} from the apiResponse elements
    var getNodes = R.compose(
      generateIdToNodeMap,
      R.filter(elementTypeIs('node'))
    );

    // Api elements -> [[way1 node-id1, id2, ...], [way 2 node-id1, ...]]
    var getWayNodes = R.compose(
      R.pluck('nodes'),
      R.filter(elementTypeIs('way'))
    );

    // Map generated by getNodes, node id -> [lat, lon] of given node id
    var linkNodeIdToCoords = R.curry(function (nodes, id) {
        return R.props(['lat', 'lon'], nodes[id])
    });

    return function (apiResponse) {
        var nodes = getNodes(apiResponse.elements);
        var getAllWays = R.compose(
            R.map(R.map(linkNodeIdToCoords(nodes))),
            getWayNodes
        );
        return getAllWays(apiResponse.elements)
    }
})();

Upvotes: 2

Related Questions