2

I'm trying to create a Leaflet/Mapbox webmap with line layer (GeoJSON or KML) with some IDs and data coming from the .csv (after joining through this ID) file located on the same path as the index.html. Joined attributes need to show on lines popup windows.

I've found THIS article with exactly the same assumptions as mine:

  • Store the attributes of a GeoJSON in a CSV file
  • Allow a non-GIS user to edit a cloud-hosted CSV file which then updates a webmap
  • Have the web map be a static app with no GIS/PostGIS/SQL etc server involved
  • Load a CSV (from say Dropbox) and a GeoJSON stored on the web server
  • Join the CSV to the GeoJSON file using JavaScript
  • Style the polygons using the field added from the CSV

But honestly I've fallen during recreation this example for my needs. It seems to be more complicated than just joining the .csv with lines.

Here is my zipped example website (clean, without any joining implemented, just .csv and kml variables)

The working example of map from mentioned article is HERE.

My clean code (with no joining yet) looks like this:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset=utf-8 />
    <title>Projekty Budżetu Partycypacyjnego Dzielnicy Bielany</title>
    <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
    <link rel='stylesheet' href='css/leaflet.css'>
    <script src='js/mapbox.js'></script>
    <script src='js/leaflet-omnivore.min.js'></script>
    <script src='js/Autolinker.min.js'></script>
    <link href='css/checkbox.css' rel='stylesheet' />
    <script src='js/jquery_1_4_2_head.js'></script>
    <script src='jquery_checkall_1_0_forjquery_1_4_2_head.js'></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.2/papaparse.min.js"></script>
    <style>
      body { margin:0; padding:0; }
      #map { position:absolute; top:0; bottom:0; width:100%; }
    </style>
    </head>
    <body>

    <script src='https://api.mapbox.com/mapbox.js/plugins/leaflet-markercluster/v1.0.0/leaflet.markercluster.js'></script>
    <link rel='stylesheet' href='css/MarkerCluster.css'>
    <link rel='stylesheet' href='css/MarkerCluster.Default.css'/>

    <div id='map'></div>

    <script>
    L.mapbox.accessToken = 'pk.eyJ1IjoiZ2lzdXBhbmFsaXp5IiwiYSI6ImNqYjN5NWw2bzBibzIyd3F5MDlkOThjbHMifQ.BI3iHH34ZUaX7SFpXAa5Gw';

    var map = L.mapbox.map('map')
        .addLayer(L.mapbox.tileLayer('mapbox.streets'));


    function layer_popup(feature, layer) {
        var popupContent = '<meta name="viewport" initial-scale=1.0"><table>\
                <tr>\
                    <caption><b><font size="3">' + (feature.properties.line_id !== null ? Autolinker.link(String(feature.properties['line_id'])) : '') + '</font></b></caption>\
                </tr>\
                <tr>\
                    <th valign="top" align="right" scope="row">Description:</th>\
                    <td style="text-align:justify">' + (feature.properties.desc !== null ? Autolinker.link(String(feature.properties.desc)) : '') + '</td>\
                </tr>\
            </table>';
        layer.bindPopup(popupContent, {maxHeight: 400},'<button class="trigger">Say hi</button>');
            }


    var myStyle = {
        "color": "#ff7800",
        "weight": 5,
        "opacity": 0.65
    };
    var csvData = omnivore.csv('data_test_linie.csv')
    var runLayer = omnivore.kml('example_lines.kml')
        .on('ready', function() {
            map.fitBounds(runLayer.getBounds());
            // After the 'ready' event fires, the GeoJSON contents are accessible
            // and you can iterate through layers to bind custom popups.
            runLayer.eachLayer(function(layer) {
                // See the `.bindPopup` documentation for full details. This
                // dataset has a property called `name`: your dataset might not,
                // so inspect it and customize to taste.
                layer_popup(layer.feature,layer);
                layer.setStyle({fillColor :'red'});
                if(layer.feature.properties.desc == 'item 2') {
                    layer.setStyle(myStyle);
                    };          
            });
        })
        .addTo(map);

    </script>

    </body>
    </html>

Join method from other article is described here: Joining CSV to GeoJSON in Leaflet?

0

Thanks @NettaB for your reply. Honestly I've managed to achieve my goals my myself just collecting and editing some code samples from few websites.

Here it is:

        ///JOINING
    function featureJoinByProperty(fProps, dTable, joinKey) {
      var keyVal = fProps[joinKey];
      var match = {};
      for (var i = 0; i < dTable.length; i++) {
        if (dTable[i][joinKey] === keyVal) {
          match = dTable[i];
          for (key in match) {
            if (!(key in fProps)) {
              fProps[key] = match[key];
            }
          }
        }
      }
    };

    var runLayer = L.geoJson(null, {
      style: {
          color: 'red',
          weight: 3,
          fillOpacity: 1
        },
      smoothFactor: 0.5
    }).addTo(map)


    var omniLayer = omnivore.kml('linie_WTR_R10.kml',null, runLayer)

    omniLayer.on('ready', function() {
            map.fitBounds(runLayer.getBounds());
            Papa.parse('odcinki_dane.csv', {
                download: true,
                header: true,
                complete: function(results) {
            // After the 'ready' event fires, the GeoJSON contents are accessible
            // and you can iterate through layers to bind custom popups.
                    runLayer.eachLayer(function(layer) {
                        featureJoinByProperty(layer.feature.properties, results.data, "description");
                        });
                        // See the `.bindPopup` documentation for full details. This
                        // dataset has a property called `name`: your dataset might not,
                        // so inspect it and customize to taste.
                        runLayer.eachLayer(function(layer) {
                          layer_popup_lines(layer.feature,layer);
                          layer.setStyle(StanOdcinkaStyle(layer));
                        }); 
                      }
                  });

                });


    ///JOINING

Now I'm struggling with making filtering checkbox for the joined line layer. For now I have only previously created code for filtering clustered markers, but cannot figure out still how to use it for lines.

    <div id='map'></div>
    <div class="button-position">
        <button class="myButton" onclick="myFunction()">BLA BLA BLA</button>
    </div>
    <form style="position: fixed; top:29px; border-radius: 6px;" id='criteria'>
      <fieldset><label class="label-cbx">
        <input class="invisible" type='checkbox' name='filters' onchange='showStations();' value='2018' checked>
        <div class="checkbox">
          <svg width="20px" height="20px" viewBox="0 0 20 20">
            <path d="M3,1 L17,1 L17,1 C18.1045695,1 19,1.8954305 19,3 L19,17 L19,17 C19,18.1045695 18.1045695,19 17,19 L3,19 L3,19 C1.8954305,19 1,18.1045695 1,17 L1,3 L1,3 C1,1.8954305 1.8954305,1 3,1 Z"></path>
            <polyline points="4 11 8 15 16 6"></polyline>
          </svg>
        </div>
        <span><b>2018</b></span>
      </label>
      <label class="label-cbx sub">
        <input class="invisible2" type='checkbox' name='filters2' onchange='showStations();' value='WTR' checked>
        <div class="checkbox">
          <svg width="20px" height="20px" viewBox="0 0 20 20">
            <path d="M3,1 L17,1 L17,1 C18.1045695,1 19,1.8954305 19,3 L19,17 L19,17 C19,18.1045695 18.1045695,19 17,19 L3,19 L3,19 C1.8954305,19 1,18.1045695 1,17 L1,3 L1,3 C1,1.8954305 1.8954305,1 3,1 Z"></path>
            <polyline points="4 11 8 15 16 6"></polyline>
          </svg>
        </div>
        <span>WTR</span>
      </label>
      <label class="label-cbx sub">
        <input class="invisible2" type='checkbox' name='filters2' onchange='showStations();' value='R10' checked>
        <div class="checkbox">
          <svg width="20px" height="20px" viewBox="0 0 20 20">
            <path d="M3,1 L17,1 L17,1 C18.1045695,1 19,1.8954305 19,3 L19,17 L19,17 C19,18.1045695 18.1045695,19 17,19 L3,19 L3,19 C1.8954305,19 1,18.1045695 1,17 L1,3 L1,3 C1,1.8954305 1.8954305,1 3,1 Z"></path>
            <polyline points="4 11 8 15 16 6"></polyline>
          </svg>
        </div>
        <span>R10</span>
      </label></fieldset>
      <fieldset><label class="label-cbx">
        <input class="invisible" type='checkbox' name='filters' onchange='showStations();' value='2017' checked>
        <div class="checkbox">
          <svg width="20px" height="20px" viewBox="0 0 20 20">
            <path d="M3,1 L17,1 L17,1 C18.1045695,1 19,1.8954305 19,3 L19,17 L19,17 C19,18.1045695 18.1045695,19 17,19 L3,19 L3,19 C1.8954305,19 1,18.1045695 1,17 L1,3 L1,3 C1,1.8954305 1.8954305,1 3,1 Z"></path>
            <polyline points="4 11 8 15 16 6"></polyline>
          </svg>
        </div>
        <span><b>2017</b></span>
      </label>
      <label class="label-cbx sub">
        <input class="invisible2" type='checkbox' name='filters2' onchange='showStations();' value='WTR' checked>
        <div class="checkbox">
          <svg width="20px" height="20px" viewBox="0 0 20 20">
            <path d="M3,1 L17,1 L17,1 C18.1045695,1 19,1.8954305 19,3 L19,17 L19,17 C19,18.1045695 18.1045695,19 17,19 L3,19 L3,19 C1.8954305,19 1,18.1045695 1,17 L1,3 L1,3 C1,1.8954305 1.8954305,1 3,1 Z"></path>
            <polyline points="4 11 8 15 16 6"></polyline>
          </svg>
        </div>
        <span>WTR</span>
      </label>
      <label class="label-cbx sub">
        <input class="invisible2" type='checkbox' name='filters2' onchange='showStations();' value='R10' checked>
        <div class="checkbox">
          <svg width="20px" height="20px" viewBox="0 0 20 20">
            <path d="M3,1 L17,1 L17,1 C18.1045695,1 19,1.8954305 19,3 L19,17 L19,17 C19,18.1045695 18.1045695,19 17,19 L3,19 L3,19 C1.8954305,19 1,18.1045695 1,17 L1,3 L1,3 C1,1.8954305 1.8954305,1 3,1 Z"></path>
            <polyline points="4 11 8 15 16 6"></polyline>
          </svg>
        </div>
        <span>R10</span>
      </label>
    </fieldset>
    </form>


     layers = omnivore.csv('data.csv', null,
    L.mapbox.featureLayer()
        .on('ready', function(e) {
            layers = e.target;
            showStations();
        }));

    var filters = document.getElementById('criteria').filters;
    var filters2 = document.getElementById('criteria').filters2;

    // There are many ways to filter data. Mapbox.js has the .setFilter method,
    // but it only applies to L.mapbox.featureLayer layers, and that isn't what
    // we're creating - we're making marker groups in a MarkerClusterGroup layer.
    // Thus we distill filtering down to its essential part: an 'if' statement
    // in a loop.
    function showStations() {
        // first collect all of the checked boxes and create an array of strings
        // like ['green', 'blue']
        var list = [];
        for (var i = 0; i < filters.length; i++) {
            if (filters[i].checked) list.push(filters[i].value);
        }
        var list2 = [];
        for (var i = 0; i < filters2.length; i++) {
            if (filters2[i].checked) list2.push(filters2[i].value);
        }
        // then remove any previously-displayed marker groups
        overlays.clearLayers();
        // create a new marker group
        var clusterGroup = new L.MarkerClusterGroup().addTo(overlays);
        // and add any markers that fit the filtered criteria to that group.
        layers.eachLayer(function(layer) {
            if ((list.indexOf(layer.feature.properties.Attr1) !== -1) && (list2.indexOf(layer.feature.properties.Attr2) !== -1))  {
                clusterGroup.addLayer(layer);
                layer_popup(layer.feature,layer);
            }
        });

    }

Any ideas?

  • If you have a new question, please don't post it inside an answer (e.g. remove it from your answer here) and ask a new question. – Stefan Dec 22 '18 at 22:58
0

Running your code showed me couple of problems:

1. There is no callback for csvData, adding ready/error like this:

var csvData = omnivore.csv('data_test_linie.csv').on('ready', function() {
  console.log(this)
}).on('error', function(e) {
  console.log(e)
})

returned an error :

"Latitude and longitude fields not present", omnivore is a geo parser and can't locate spatial attributes in your csv, you can consider using simple csv parsers (or check the error for data, but that is not good practice)

2. In order to parse the geojson after the csv is ready, you'll need to call omnivore.kml inside the success function of the csv parsing function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.