Search code examples
javascriptleafletgistopojson

TopoJSON in Leaflet, via Omnivore: reading properties


While following along a tutorial on choropleth maps in Leaflet I realized that a Shapefile > GeoJSON conversion generated a very large file. TopoJSON proved a better alternative, except for being unable to access the properties for each geometry. I merged data from https://github.com/centraldedados/violencia-domestica into a TopoJSON file converted from a Shapefile, structured like so:

{
  "transform": {
    "scale": [
      0.0025081552497290688,
      0.0012125352320998587
    ],
    "translate": [
      -31.26818656921381,
      30.03017616271984
    ]
  },
  "objects": {"PRT_adm1": {
    "geometries": [
      {
        "type": "Polygon",
        "arcs": [[
          0,
          1,
          2,
          3,
          4
        ]],
        "properties": {
          "ENGTYPE_1": "District",
          "ISO": "PRT",
          "NL_NAME_1": null,
          "HASC_1": "PT.EV",
          "ID_0": 182,
          "NAME_0": "Portugal",
          "TYPE_1": "Distrito",
          "ID_1": 1,
          "NAME_1": "Évora",
          "CCN_1": 0,
          "CCA_1": null,
          "dgai_violencia_domestica_2008_2014": {
            "Valores": [
              {
                "Ano": "2009",
                "Entidade": [
                  {"GNR": "216"},
                  {"PSP": "171"}
                ]
              },
              {
                "Ano": "2010",
                "Entidade": [
                  {"GNR": "247"},
                  {"PSP": "162"}
                ]
              },
              {
                "Ano": "2011",
                "Entidade": [
                  {"GNR": "248"},
                  {"PSP": "181"}
                ]
              },
              {
                "Ano": "2012",
                "Entidade": [
                  {"GNR": "277"},
                  {"PSP": "150"}
                ]
              },
              {
                "Ano": "2013",
                "Entidade": [
                  {"GNR": "207"},
                  {"PSP": "169"}
                ]
              },
              {
                "Ano": "2014",
                "Entidade": [
                  {"GNR": "226"},
                  {"PSP": "137"}
                ]
              }
            ],
            "Distrito": "Évora",
            "Factor_amostra": 0.020521270111203194,
            "Somatorio_amostra": 2391
          },
          "VARNAME_1": null
        }
      },
      ...

and would like to access the "dgai_violencia_domestica_2008_2014.Factor_amostra" property of each object, so as to encode a color value.

Is this possible with Leaflet+Omnivore, or would d3 or another library be needed?

A current, broken version of the map lives here, with the relevant code being:

L.mapbox.accessToken = '...';
var map = L.mapbox.map('map', 'mapbox.streets').setView([39.5, -8.60], 7);

// Omnivore will AJAX-request this file behind the scenes and parse it:
// note that there are considerations:
// - The file must either be on the same domain as the page that requests it,
//   or both the server it is requested from and the user's browser must
//   support CORS.

// Internally this function uses the TopoJSON library to decode the given file
// into GeoJSON.

function getColor(d) {
    return d > 0.75 ? '#800026' :
           d > 0.50  ? '#FC4E2A' :
           d > 0.25   ? '#FD8D3C' :
                      '#FFEDA0';
}

var pt = omnivore.topojson('dgai_violencia_domestica_2008_2014.topo.json');

function style(geometry) {
  return {
    fillColor: getColor(geometry.properties.dgai_violencia_domestica_2008_2014.Peso_na_duracao_da_amostra),
    weight: 1,
    opacity: 1,
    color: 'white',
    dashArray: '3',
    fillOpacity: 1
};
}

pt.setStyle(style);
pt.addTo(map);

A nudge in the right direction, anyone?

Thanks in advance.


Solution

  • Did some testing and as it turns out, the setStyle function won't execute:

    pt.setStyle(function (feature) {
        console.log('Yay, i am executed!');
        return {
            'color': 'red'
        }
    });
    

    Using that Yay, i am executed! never gets logged to the console. Which made me suspect that there's something wrong with the setStyle method of Omnivore. So i tried it with a custom layer:

    var geojson = new L.GeoJSON(null);
    
    var pt = omnivore.topojson(url, null, geojson).addTo(map);
    
    geojson.setStyle(function (feature) {
        console.log('Yay, i am executed!');
        return {
            'color': 'red'
        };
    });
    

    Same thing, Yay, i am executed! never get logged to the console. One thing left to try was the style option of L.GeoJSON:

    var geojson = new L.GeoJSON(null, {
        'style': function (feature) {
            console.log('Yay, i am executed!');
            return {
                'color': 'red'
            };
        }
    });
    
    var pt = omnivore.topojson(url, null, geojson).addTo(map);
    

    And lo and behold, that does what it's supposed to do. So i tried it with your style and color function and dataset:

    function getColor(d) {
        return d > 0.75 ? '#800026' :
               d > 0.50 ? '#FC4E2A' :
               d > 0.25 ? '#FD8D3C' :
                          '#FFEDA0';
    }
    
    function getStyle (feature) {
        return {
            fillColor: getColor(feature.properties.dgai_violencia_domestica_2008_2014.Peso_na_duracao_da_amostra),
            weight: 1,
            opacity: 1,
            color: 'white',
            dashArray: '3',
            fillOpacity: 1
        };
    }
    
    var geojson = new L.GeoJSON(null, {
        'style': getStyle
    });
    
    var pt = omnivore.topojson(url, null, geojson).addTo(map);
    

    This gives me the following error:

    Uncaught TypeError: Cannot read property 'Peso_na_duracao_da_amostra' of undefined

    That's because on the third feature, that property is missing. So your dataset is corrupt or incomplete. Hopefully this will help you along, good luck, here's a Plunker of my experiment which works (so far):

    http://plnkr.co/edit/P6t96hTaOgSxTc4TPUuV?p=preview