TransWikia.com

Joining CSV to KML/GeoJSON in Leaflet webmap and showing joined data in popups?

Geographic Information Systems Asked by Jakub O on January 27, 2021

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?

2 Answers

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.

Answered by NettaB on January 27, 2021

I have managed to achieve my goals myself by just collecting and editing some code samples from a 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

Answered by Jakub O on January 27, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP