Source: src/togeojson/togeojson.factory.js

/**
 * @constructor ToGeoJSON
 * @param {Object} options provided by the provider
 * @description osm to geojson without dependencies :)
   Import Note : geojson wait for lon/lat where every body else use lat/lon
 */
function factory(options) {
    //copy/pasted/adapter from https://github.com/jfirebaugh/leaflet-osm/blob/master/leaflet-osm.js
    var service = {
        options,
        getAsArray,
        getFeatures,
        getNodes,
        getWays,
        getRelations,
        getTags,
        buildFeatures,
        isWayArea,
        interestingNode,
        togeojson
    };
    return service;

    function getFeatures(data) {
        var features = [];
        if (!(data instanceof Array)) {
            data = buildFeatures(data);
        }

        for (let i = 0; i < data.length; i++) {
            var d = data[i];
            var feature = {
                type: 'Feature',
                geometry: {},
                properties: {
                    id: d.id,
                    tags: d.tags
                }
            };
            if (d.type === "changeset") {
                //build rectangle
                // X = Long; Y = Lat, lets do it clockwise
                feature.geometry.type = 'Polygon';
                var bounds = d.latLngBounds;
                feature.geometry.coordinates = [[
                    [parseFloat(bounds._min_lon), parseFloat(bounds._min_lat)],
                    [parseFloat(bounds._min_lon), parseFloat(bounds._max_lat)],
                    [parseFloat(bounds._max_lon), parseFloat(bounds._max_lat)],
                    [parseFloat(bounds._max_lon), parseFloat(bounds._min_lat)],
                    [parseFloat(bounds._min_lon), parseFloat(bounds._min_lat)]
                ]];
            } else if (d.type === "node") {
                //add a Point
                feature.geometry.type = 'Point';
                feature.geometry.coordinates = [d.latLng[1], d.latLng[0]];
            } else {
                var lngLats = new Array(d.nodes.length);

                for (let j = 0; j < d.nodes.length; j++) {
                    let latLng = d.nodes[j].latLng;
                    lngLats[j] = [latLng[1], latLng[0]];
                }

                if (isWayArea(d)) {
                    feature.geometry.type = 'Polygon';
                    feature.geometry.coordinates = [lngLats];
                } else {
                    feature.geometry.type = 'LineString';
                    feature.geometry.coordinates = lngLats;
                }
            }
            features.push(feature);
        }
        return features;
    }
    function getAsArray(data) {
        if (Array.isArray(data)) {
            return data;
        } else if (typeof data === "object") {
            return [data];
        } else {
            return [];
        }
    }
    function getTags(data) {
        var rawtags = getAsArray(data.tag);
        var tags = {};
        rawtags.forEach(function (t) {
            tags[t._k] = t._v;
        });
        return tags;
    }
    function getChangesets(data) {
        var result = [];

        var nodes = getAsArray(data.osm.changeset);
        for (var i = 0; i < nodes.length; i++) {
            var node = nodes[i];
            result.push({
                id: node._id,
                type: "changeset",
                
                latLngBounds: node,
                tags: getTags(node)
            });
        }

        return result;
    }

    function getNodes(data) {
        var nodesAsArray = getAsArray(data.osm.node);
        var nodesById = {};
        nodesAsArray.forEach(function (node) {
            nodesById[node._id] = {
                id: node._id,
                type: 'node',
                latLng: [parseFloat(node._lat), parseFloat(node._lon)],
                tags: getTags(node)
            };
        });
        return nodesById;
    }
    function getWays(data, nodes) {
        var result = [];
        var ways = getAsArray(data.osm.way);
        var features = [];
        ways.forEach(function (way) {
            var nds = way.nd;
            var way_object = {
                id: way._id,
                type: "way",
                nodes: new Array(nds.length),
                tags: getTags(way)
            };
            for (let j = 0; j < nds.length; j++) {
                way_object.nodes[j] = nodes[nds[j]._ref];
            }
            result.push(way_object);
        });
        return result;
    }
    function getRelations(data, nodes, way) {
        var result = [];

        var rels = getAsArray(data.osm.relation);
        for (let i = 0; i < rels.length; i++) {
            var rel = rels[i];
            var members = getAsArray(rel.member);
            var rel_object = {
                id: rel._id,
                type: "relation",
                members: new Array(members.length),
                tags: getTags(rel)
            };
            for (let j = 0; j < members.length; j++) {
                if (members[j]._type === "node") {
                    rel_object.members[j] = nodes[members[j]._ref];
                } else{
                    // relation-way and relation-relation membership not implemented
                    rel_object.members[j] = null;
                }
            }
            result.push(rel_object);
        }
        return result;
    }

    function buildFeatures(data) {
        var features = [];
        var nodes = getNodes(data); //-> {id: data, ...}
        var ways = getWays(data, nodes); //->[]
        var relations = getRelations(data, nodes, ways); //->[]

        for (let node_id in nodes) {
            var node = nodes[node_id];
            if (interestingNode(node, ways, relations)) {
                features.push(node);
            }
        }

        for (let i = 0; i < ways.length; i++) {
            var way = ways[i];
            features.push(way);
        }

        return features;
    }

    function isWayArea(way) {
        if (way.nodes[0] != way.nodes[way.nodes.length - 1]) {
            return false;
        }

        for (let key in way.tags) {
            if (options.areaTags.indexOf(key)) {
                return true;
            }
        }

        return false;
    }

    function interestingNode(node, ways, relations) {
        var used = false;

        for (let i = 0; i < ways.length; i++) {
            if (ways[i].nodes.indexOf(node) >= 0) {
                used = true;
                break;
            }
        }

        if (!used) {
            return true;
        }

        for (let i = 0; i < relations.length; i++) {
            if (relations[i].members.indexOf(node) >= 0) {
                return true;
            }
        }

        return false;
    }
    function togeojson(data, options) {
        var res = {
            type: 'FeatureCollection',
            features: []
        };
        if (data) {
            res.features = getFeatures(data);
        }
        return res;
    }

}

export default factory;