Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to get way node refs in the geojson #106

Open
wants to merge 18 commits into
base: gh-pages
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ Converts OSM data into GeoJSON.
* `data`: the OSM data. Either as a XML DOM or in [OSM JSON](http://overpass-api.de/output_formats.html#json).
* `options`: optional. The following options can be used:
* `flatProperties`: If true, the resulting GeoJSON feature's properties will be a simple key-value list instead of a structured json object (with separate tags and metadata). default: false
* `wayRefs`: If true, the GeoJSON will have a `ndrefs` property when flat, or in `meta` when not flat, which is an array of all the node members of the `way`. default: false
* `uninterestingTags`: Either a [blacklist](https://github.com/tyrasd/osmtogeojson/blob/2.0.0/index.js#L14-L24) of tag keys or a callback function. Will be used to decide if a feature is *interesting* enough for its own GeoJSON feature.
* `polygonFeatures`: Either a [json object](https://github.com/tyrasd/osmtogeojson/blob/2.0.0/polygon_features.json) or callback function that is used to determine if a closed way should be treated as a Polygon or LineString. [read more](https://wiki.openstreetmap.org/wiki/Overpass_turbo/Polygon_Features)
* `mapRelations` either `true` or `false`. If set to true, osmtogeojson returns some additional data, apart from the GeoJSON. The data returned will now be an object containing `geojson`, `featuresInRelation` and `nodes`. `geojson` will be the current geojson format. `featuresInRelation` is an array of feature ids that are members of relations. `nodes` is an array of node objects, including nodes without tags. Additionally, if `mapRelations` is passed, geometry processing of relations is ignored, and relations are omitted from the `geojson` output.

The result is a javascript object of GeoJSON data:

Expand Down
79 changes: 52 additions & 27 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ osmtogeojson = function( data, options, featureCallback ) {
{
verbose: false,
flatProperties: true,
wayRefs: false,
mapRelations: false,
uninterestingTags: {
"source": true,
"source_ref": true,
Expand Down Expand Up @@ -232,6 +234,8 @@ osmtogeojson = function( data, options, featureCallback ) {
var nodes = new Array();
var ways = new Array();
var rels = new Array();
var featuresInRelation = new Set();

// helper function
function copy_attribute( x, o, attr ) {
if (x.hasAttribute(attr))
Expand Down Expand Up @@ -372,8 +376,9 @@ osmtogeojson = function( data, options, featureCallback ) {
copy_attribute( node, nodeObject, 'changeset' );
copy_attribute( node, nodeObject, 'uid' );
copy_attribute( node, nodeObject, 'user' );
if (!_.isEmpty(tags))
nodeObject.tags = tags;
// always set nodeObject.tags to tags, even if tags is an empty object.
// this ensures we get valid properties when returning data for all nodes
nodeObject.tags = tags;
nodes.push(nodeObject);
});
// ways
Expand Down Expand Up @@ -417,45 +422,61 @@ osmtogeojson = function( data, options, featureCallback ) {
_.each( xml.getElementsByTagName('relation'), function( relation, i ) {
var tags = {};
var members = [];
_.each( relation.getElementsByTagName('tag'), function( tag ) {
tags[tag.getAttribute('k')] = tag.getAttribute('v');
});

var has_full_geometry = false;
_.each( relation.getElementsByTagName('member'), function( member, i ) {
members[i] = {};

copy_attribute( member, members[i], 'ref' );
copy_attribute( member, members[i], 'role' );
copy_attribute( member, members[i], 'type' );

if (!has_full_geometry &&
(members[i].type == 'node' && member.getAttribute('lat')) ||
(members[i].type == 'way' && member.getElementsByTagName('nd').length>0) )
has_full_geometry = true;
if (options.mapRelations) {
featuresInRelation.add(`${members[i].type}/${members[i].ref}`);
}
});
var relObject = {
"type": "relation"

if (!options.mapRelations) {
_.each( relation.getElementsByTagName('tag'), function( tag ) {
tags[tag.getAttribute('k')] = tag.getAttribute('v');
});
var relObject = {
"type": "relation"
}
copy_attribute( relation, relObject, 'id' );
copy_attribute( relation, relObject, 'version' );
copy_attribute( relation, relObject, 'timestamp' );
copy_attribute( relation, relObject, 'changeset' );
copy_attribute( relation, relObject, 'uid' );
copy_attribute( relation, relObject, 'user' );
if (members.length > 0)
relObject.members = members;
if (!_.isEmpty(tags))
relObject.tags = tags;
if (centroid = relation.getElementsByTagName('center')[0])
centerGeometry(relObject,centroid);
if (has_full_geometry)
fullGeometryRelation(relObject, relation.getElementsByTagName('member'));
else if (bounds = relation.getElementsByTagName('bounds')[0])
boundsGeometry(relObject,bounds);
rels.push(relObject);
}
copy_attribute( relation, relObject, 'id' );
copy_attribute( relation, relObject, 'version' );
copy_attribute( relation, relObject, 'timestamp' );
copy_attribute( relation, relObject, 'changeset' );
copy_attribute( relation, relObject, 'uid' );
copy_attribute( relation, relObject, 'user' );
if (members.length > 0)
relObject.members = members;
if (!_.isEmpty(tags))
relObject.tags = tags;
if (centroid = relation.getElementsByTagName('center')[0])
centerGeometry(relObject,centroid);
if (has_full_geometry)
fullGeometryRelation(relObject, relation.getElementsByTagName('member'));
else if (bounds = relation.getElementsByTagName('bounds')[0])
boundsGeometry(relObject,bounds);
rels.push(relObject);
});
return _convert2geoJSON(nodes,ways,rels);
if (options.mapRelations) {
return {
geojson: _convert2geoJSON(nodes,ways,rels),
featuresInRelation: Array.from(featuresInRelation),
nodes: nodes
}
} else {
return _convert2geoJSON(nodes,ways,rels);
}
}
function _convert2geoJSON(nodes,ways,rels) {

// helper function that checks if there are any tags other than "created_by", "source", etc. or any tag provided in ignore_tags
function has_interesting_tags(t, ignore_tags) {
if (typeof ignore_tags !== "object")
Expand All @@ -475,7 +496,8 @@ osmtogeojson = function( data, options, featureCallback ) {
"version": object.version,
"changeset": object.changeset,
"user": object.user,
"uid": object.uid
"uid": object.uid,
"ndrefs": object.hasOwnProperty('ndrefs') && options.wayRefs ? object.ndrefs : undefined
};
for (var k in res)
if (res[k] === undefined)
Expand Down Expand Up @@ -510,12 +532,14 @@ osmtogeojson = function( data, options, featureCallback ) {
var waynids = new Object();
for (var i=0;i<ways.length;i++) {
var way = ways[i];
way.ndrefs = [];
if (wayids[way.id]) {
// handle input data duplication
way = options.deduplicator(way, wayids[way.id]);
}
wayids[way.id] = way;
if (_.isArray(way.nodes)) {
way.ndrefs = Object.assign([], way.nodes);
for (var j=0;j<way.nodes.length;j++) {
if (typeof way.nodes[j] === "object") continue; // ignore already replaced way node objects
waynids[way.nodes[j]] = true;
Expand Down Expand Up @@ -942,6 +966,7 @@ osmtogeojson = function( data, options, featureCallback ) {
"coordinates" : coords,
}
}

if (ways[i].tainted) {
if (options.verbose) console.warn('Way',ways[i].type+'/'+ways[i].id,'is tainted');
feature.properties["tainted"] = true;
Expand Down
2 changes: 2 additions & 0 deletions osmtogeojson
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ var osmtogeojson = require('./'),
.boolean('n').describe('n', 'numeric properties. if set, the resulting GeoJSON feature\'s properties will be numbers if possible')
.boolean('v').describe('v', 'verbose mode. output diagnostic information during processing')
.boolean('m').describe('m', 'minify output json (no identation and linebreaks)')
.boolean('a').describe('a', 'return all nodes. if set, the resulting GeoJSON will contain ALL nodes as separate features, even those that do not have tags (i.e. that are parts of ways) ')
.boolean('ndjson').describe('ndjson', 'output newline delimited geojson instead of a single featurecollection (implies -m enabled)')
.boolean('version').describe('version','display software version')
.boolean('help').describe('help','print this help message'),
Expand Down Expand Up @@ -139,6 +140,7 @@ function legacyParsers(data) {
function convert(data) {
var geojson = osmtogeojson(data, {
flatProperties: !enhanced_geojson,
allNodes: argv.a,
verbose: argv.v
}, argv.ndjson ? outputNdgeojson : null);
if (!argv.ndjson)
Expand Down
3 changes: 1 addition & 2 deletions osmtogeojson.js

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@
},
"devDependencies": {
"expect.js": "~0.2.0",
"faucet": "~0.0.1",
"istanbul": "^0.4.5",
"mocha": "~1.12.0",
"tape": "~2.10.2",
"faucet": "~0.0.1"
"tinyify": "^2.5.0"
},
"engines": {
"node": ">=0.5"
Expand Down
Loading