Export OpenStreetMap data using visual editor on rete.js

In my work, I often encounter the task of exporting data from OpenStreetMap. OSM is an amazing data source from where you can get at least sights , at least areas of the city , at least streets for studies of pedestrian accessibility , and generally anything.


That's just the process of working with them at some point began to bore me. To retrieve data for some non-trivial request, you need to either study the Overpass request language, or write scripts and poke around in the OSM XML format.


Carrying out these manipulations for the hundredth time, I thought about creating some simpler and more convenient tool. And now he is ready - https://yourmaps.io , the visual editor of export descriptions of OpenStreetMap. In the editor, you can mouse over a graph, each node of which will represent an operation or filter on a stream of OSM objects, and then download the result in GeoJSON.


Here is an example of a graph that selects all schools within a given municipal district, and then builds 300-meter buffers around them:



As a result of work, we get such a set of polygons in GeoJSON format, which can then be imported into QGIS or some other software.


β€” , Rete.js, -.


Rete.js


Rete.js β€” JS , . , .


, Rete . OpenStreetMap β€” .


image


(node), (component). , ( ). data, (, , )


Rete . : , ( ) .


, , , , - .


β€” , , ( ). , - , - jquery 220, . , .



InputControl Rete, .


var SelectComponent = {
    //  -  HTML ,       ,        this.root   
    template: '<select></select>',
    data() {
        return {
            value: ""
        };
    },
    methods: {
        update() {
            //        
            this.putData(this.ikey, $(this.root).val())
        }
    },
    //         
    mounted() {
        // this.root -  html ,    template, .. select   
        let jqueryRoot = $(this.root)
        //     
        for (let idx = 0; idx < this.values.length; ++idx) {
            let v = this.values[idx]
            jqueryRoot.append($("<option></option>")
                .attr("value", v[0])
                .text(v[1]));
        }
        //       -        ,  
        let currentVal = this.getData(this.ikey)
        if (currentVal === undefined) {
            currentVal = this.defaultValue
            this.putData(this.ikey, this.defaultValue)
        }
        jqueryRoot.val(currentVal);

        const _self = this;
        //          data
        jqueryRoot.change(function() {
            _self.root.update()
        })
    }
}
//          node.addControl(new SelectControl(...))
class SelectControl extends Rete.Control {
    constructor(emitter, key, values, defaultValue) {
        super(key);
        this.key = key;
        this.component = SelectComponent
        //          ,       
        this.props = { emitter, ikey: key, values: values, defaultValue: defaultValue};
    }
}

,
var AddTextFieldComponent = {
   //   -  ,        InputControl
    template: '<button type="button" class="btn btn-outline-light">' +
        '<i class="fa fa-plus-circle"></i>&nbsp;Add Value</button>',
    data() {
        return {
            value: ""
        };
    },
    methods: {
        //    ,    ,  InputControl,   id    
        getCount(node, prefix) {
            let count = 0;
            node.controls.forEach((value, key, map) => {
                if (key.startsWith(prefix) && value instanceof InputControl) {
                    ++count;
                }
            });

            return count;
        },
        //         ,     
        update(e) {
            let count = this.methods.getCount(this.node, this.prefix)
            this.node.addControl(new InputControl(this.editor, this.prefix + count))
            //     ,   Rete      
            this.node.update()
            this.emitter.view.updateConnections(this)
            //         ,      json  ,     
            this.putData(this.iKey, count + 1)
        }
    },
    mounted() {
        const _self = this;
        this.root.onclick = function(event) {
            _self.root.update()
        }
    }
};

class AddTextFieldControl extends Rete.Control {
    constructor(emitter, key, prefix, node, inputPlaceholder) {
        super(key);
        this.key = key;
        this.component = AddTextFieldComponent
        this.props = { emitter, iKey: key, prefix: prefix, node: node, inputPlaceholder: inputPlaceholder};
    }
}

class FilterByTagValueComponent extends Rete.Component {
    constructor(){
        super("Filter_by_Tag_Value");
    }

    builder(node) {
        //         ,       osm. 
        //     Rete  ,         
        var input = new Rete.Input('osm',"Map Data", osmSocket);
        var output = new Rete.Output('osm', "Filtered Map Data", osmSocket);
        //     
        var tagNameInput = new InputControl(this.editor, 'tag_name')
        //       
        var  modeControl = new SelectControl(this.editor,
            "mode",
            [["EQUAL", "=="], ["NOT_EQUAL", "!="], ["GREATER", ">"], ["LESS", "<"], ["GE", ">="], ["LE", ">="]],
            "EQUAL")
        //   
        node.addInput(input)
            .addControl(tagNameInput)
            .addControl(modeControl)
            .addControl(new AddTextFieldControl(this.editor, "tag_valueCount", "tag_value", node, "Tag Value"))
        //       json -  ,     ,    
       //  data.tag_valueCount  AddTextFieldControl,  
        let valuesCount = 1;
        if (node.data.tag_valueCount !== undefined) {
            valuesCount = node.data.tag_valueCount
        }
        //    InputControl
        node.addControl(new InputControl(this.editor, 'tag_value'))
        for (let i = 1; i < valuesCount; ++i) {
            node.addControl(new InputControl(this.editor, 'tag_value' + i))
        }

        return node
            .addOutput(output);
    }
}

:




.


, JSON ( Rete ), , .


OSM


, .


: ( leisure=park, OSM):



β€” , OSM , , . ( ( Java) β€” ), , , .


, :



: 500- :



, amenity=school, ( β€” ), .


, . - .


, , ? : ( ), , , Union. :



… . -, β€” , .. . , amenity=school , , . , .



, , - . :



. . β€” 4, 3. .
. .. - -, .


:




Rete.js YourMaps .


In the future I plan to add even more there - for example, the ability to download data not only from OSM, but also from my GeoJSON files, more types of operations and filters, etc.


This service helps me personally personally. For example, when a student needs to quickly show something on the OSM map - I don’t need to run QGIS anymore and remember the complex Overpass query language, I click on the desired graph with a couple of mouse movements, it is processed in a few seconds and you can immediately see the result.


I hope it will be useful to some of you. As always, I am ready to listen to suggestions and wishes either here in the comments, or you can send it to evsmirnov@itmo.ru


All Articles