mapviewer.md 14.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
# g1.mapviewer

Mapviewer is an abstraction over [Leaflet](http://leafletjs.com/) that can
create common GIS applications using configurations.

Mapviewer requires `npm install leaflet d3 d3-scale-chromatic g1`.

```html
<link rel="stylesheet" href="node_modules/leaflet/dist/leaflet.css">
<script src="node_modules/leaflet/dist/leaflet.js"></script>
<script src="node_modules/d3/build/d3.js"></script>
<script src="node_modules/d3-scale-chromatic/dist/d3-scale-chromatic.min.js"></script>
<script src="node_modules/g1/dist/mapviewer.min.js"></script>
```

This creates a simple base map:

```html
<div id="base-map" style="height:300px"></div>
<script>
  var map = g1.mapviewer({
    id: 'base-map',
    layers: {
      worldMap: { type: 'tile', url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' }
    }
  })
</script>
```

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
This loads a [GeoJSON file](test/india-states.geojson), links data from
[state_score.json](test/state_score.json), and sets the fill color from a merged
attribute.

```html
<div id="geojson-map" style="height:300px">
<script>
  var map = g1.mapviewer({
    id: 'geojson-map',
    layers: {
      worldMap: { type: 'tile', url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' },
      indiaGeojson: {
        type: 'geojson',
        url: 'india-states.geojson',
        link: {
          url: 'state_score.json',    // Load data from this file
          dataKey: 'name',            // Join this column from the URL (data)
          mapKey: 'ST_NM'             // with this property in the GeoJSON
        },
        options: {
          style: {
            fillColor: '#a00',
            fillOpacity: 1
          }
        },
        tooltip: function(prop) {   // On hover, show this HTML tooltip
          return prop.ST_NM + ': ' + prop.TOT_P
        },
        attrs: {
          fillColor: {                // Fill the regions
            metric: 'score',          // with the "score" column from state_score.json
            scheme: 'RdYlGn'           // using a RdYlGn gradient
          }
        }
      }
    }
  })
</script>
```
**Note**: You can use `type: 'topojson'` when loading TopoJSON maps.


## g1.mapviewer options

- `id`: ID of the map DOM element (example: `mapid`), or the DOM element
- `map`:
    - `options`: supports same options as [Map options](http://leafletjs.com/reference-1.3.0.html#map)
- `layers`: builds layers one on top of another in the specified order.
  - `{layername: {...} }` dict with layer name as keys
  - Each layer MUST have a type. Currently supported types are
    - tile
    - geojson
    - topojson
    - marker (`link`: option is not yet supported )
    - circleMarker
  - `tile` layer MUST have a url: that has the URL template for the leaflet tile layer.
    - `url`: A string of the form - `http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png`
    - `options`: supports same options as [Tile options](http://leafletjs.com/reference-1.3.0.html#tilelayer-minzoom)
  - `geojson` layer MUST have a data as an array of objects or else MUST have a url (string).
    - `url`: String
    - `data`: An array of objects. data: takes preference over url.
    - `options`: supports same options as [GeoJSON options](http://leafletjs.com/reference-1.3.0.html#geojson-style)
    - `link`: adds attributes from input dataset to geojson/topojson
      - `url`: url (String) to fetch data
      - `mapKey`: attribute name in geojson/topojson to match
      - `dataKey`: column name in input dataset that matches with geojson/topojson `mapKey`
      - `mismatch`: `true` (default) / `false` / function. Displays the number of mismatches between `dataKey` and `mapKey`. By default, appears as a label at the bottom left corner of the map. To customise the message, use function
        - function accepts an list of objects. Each object has `status` and all geojson/topojson feature properties:
          - `status`: A boolean value representing whether the geojson/topojson feature found a match from input dataset or not.
    - `popup`: string / function that returns formatted value
    - `popupOptions`: An object with properties and values from [leaflet popup options](https://leafletjs.com/reference-1.3.4.html#popup-l-popup)
    - `tooltip`: string / function that returns formatted value.
      - function(properties) must return a string. feature properties are passed as argument.
    - `tooltipOptions`: An object with properties and values from [leaflet tooltip options](https://leafletjs.com/reference-1.3.0.html#tooltip-option)
      - `direction` property can be a string or function. function is passed the following arguments.
        - `centerPoint` is center coordinates of map view
        - `tooltipPoint` is center coordinates of tooltip
        - `properties` are feature properties

    NOTE: `tooltip` and `tooltipOptions` are previously (till v0.9.l) are inside `attrs`. This spec breaking change is mage to maintain consistency with `popup` and `popupOptions`
    - `attrs` Data driven styles. same as `options`. (`attrs` take priority over `options`)
      - For `color`, `weight`, `opacity`, `fillColor`, `fillOpacity` properties, the options are:.
        - `metric` string / function
          - If `metric`: is a string, can be any numeric property of geojson
          - To have a metric that is formala based on multiple properties, use function. Example: `function(row) { return row['congress_votes'] - row['bjp_votes']}`
        - `domain` An array of two numbers. Defaults to calculated values of given `metric`.
        - `range`
          - For `fillColor` and `color`, must be a [interpolate color scheme](https://github.com/d3/d3-scale-chromatic#diverging)
  - `topojson` - same as `geojson`
  - `marker` layer MUST have a data as an array of objects or else MUST have a url (string).
    - `url`: String
    - `data`: An array of objects. data: takes preference over url.
    - `latitude`: String (mandatory). Must be column name that contains latitude of marker
    - `longitude`: String (mandatory). Must be column name that contains longitude of marker
    - `options`: supports same options as [marker options](http://leafletjs.com/reference-1.3.0.html#marker-icon)
  - `circleMarker` layer MUST have a data as an array of objects or else MUST have a url (string).
    - `url`: String
    - `data`: An array of objects. data: takes preference over url.
    - `latitude`: String (mandatory). Must be column name that contains latitude of marker
    - `longitude`: String (mandatory). Must be column name that contains longitude of marker
    - `options`: supports same options as [circleMarker options](http://leafletjs.com/reference-1.3.0.html#circlemarker-radius)
    - `attrs` same as `attrs` for `geojson` type layer
- `drilldown`:
    - `rootLayer`: `geojson/topojson` layer that acts as root layer to drilldown further.
    - `levels`: Array of objects that provides layer info
      - `layerName`: Can be a string or function. Function takes argument as `properties` of parentLayer feature
      - `layerOptions`: Same as layer options in `layers` option. If `url` is function, `url` takes argument as `properties` of parentLayer feature
- `legend`: configuration of legend to be added to layer. It requires [d3-legend](https://cdnjs.com/libraries/d3-legend). This creates a `<div class="map-legend">`.
    - `position`: can be `topright`, `topleft`, `bottomleft` or `bottomright`(Defaults to `bottomright`)
    - `format`: accepts d3 formats and applies to legend labels. (Defaults to `d`)
    - `shape`: can be a d3 symbol or an svg path. Default `d3.symbolSquare`
    - `size`: size of legend cell
    - `cells`: number of cells in legend. Default `5`
    - `width`: width of legend
    - `height`: height of legend
    - `scale`: accepts d3 scale format (mandatory). For examples, refer [d3-legend](https://d3-legend.susielu.com/#color-examples)
    - `orient`: orientation of legend. Can be `vertical` (Default) or `horizontal`
    - `shapeWidth`: width of legend cell. Default `20`
    - `shapePadding`: padding of legend cell. Default `20`
    - `labelOffset`: value to determine distance of label from each legend cell. Default `20`

## g1.mapviewer methods
`fitToLayer(layerName, options)`
Zooms map view to fit the layer. Supports same options as [fitBounds options](http://leafletjs.com/reference-1.3.0.html#fitbounds-options)

`zoomHandler(layerName, minZoomLevel, maxZoomLevel(optional) )`
Shows the layer with `layerName` only between `minZoomLevel` and `maxZoomLevel`.

`addNote(options)`
Adds a html label on the map. `options` is an object with following properties
  - `html`: html object node
  - `style`: Defaults to `bottom-left`. Specify styles for the `html`
  - `position`: (Optional) Specifies the position of `html` on the map


`removeLayer(layerName)`
Removes the layer from the map and returns the layer if the layer exists on the map, else throws an error. 
Note: This function will remove the layer from the map only. The layer object still exists in memory. Use `addLayer(layerName)` to add the layer back to the map.

`addLayer(layerName, layerConfig)`
- `addLayer(layerName)` if `layerName` layer does not exist on map, adds the layer to the map.
- `addLayer(layerName, layerConfig)` will creates a new layer with `layerConfig` options and adds it to the map.


## g1.mapviewer events

- `layersloaded` is fired when all layers are saved in mapviewer.gLayers (used interally).
  - tooltip is rendered on each layer only after `layersload` is fired
- `layerName + 'loaded'` is fired for each layer with name as `layerName`

<!------------------------------------------------------------------------------------------->

## g1.mapviewer features examples

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
This creates a set of markers for each row in [cities.json](test/cities.json).

```html
<div id="marker-map" style="height:300px">
<script>
  var map = g1.mapviewer({
    id: 'marker-map',
    layers: {
      worldMap: { type: 'tile', url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' },
      cityMarkers: {
        type: 'marker',
        url: 'cities.json',
        latitude: 'lat',
        longitude: 'long',
      }
    }
  })
</script>
```

This creates a set of circle markers for each row in [cities.json](test/cities.json).
You can apply styles based on any attribute or function.

```html
<div id="circle-marker-map" style="height:300px">
<script>
  var map = g1.mapviewer({
    id: 'circle-marker-map',
    layers: {
      worldMap: { type: 'tile', url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' },
      cityMarkers: {
        type: 'circleMarker',
        url: 'cities.json',
        latitude: 'lat',
        longitude: 'long',
        attrs: {
          fillColor: {
            metric: 'pollution',
            scheme: 'RdYlGn'
          }
        }
      }
    }
  })
</script>
```

231
This adds legend to the map
232 233

```html
234
<div id="choropleth" class="map"></div>
235
<script>
236 237 238 239 240 241 242 243 244 245 246 247
  var choro_map = g1.mapviewer({
    id: 'choropleth',
    legend: {
      position: 'topright',
      format: 'd',
      shape: d3.symbolCircle,
      size: 100,
      scale: d3.scaleLinear().domain([10, 20, 30]).range(['red', 'yellow', 'green']),
      orient: 'horizontal',
      width: 300,
      height: 100
    },
248
    layers: {
249
      worldMap2: { type: 'tile', url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' },
250 251 252 253
      indiaGeojson: {
        type: 'geojson',
        url: 'india-states.geojson',
        link: {
254 255 256
          url: 'state_score.json',
          dataKey: 'name',
          mapKey: 'ST_NM'
257 258 259
        },
        options: {
          style: {
260
            fillColor: '#ccc',
261 262 263 264
            fillOpacity: 1
          }
        },
        attrs: {
265 266 267 268 269
          fillColor: {
            metric: 'score',
            scale: 'linear',
            domain: [10, 20, 30],
            range: ['red', 'yellow', 'green'],
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
          }
        }
      }
    }
  })
</script>
```

Drilldown feature example:

```html
<div id="geojson-map" style="height:300px">
<script>
  var map = g1.mapviewer({
    id: 'geojson-map',
    layers: {
      worldMap: { type: 'tile', url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' },
      indiaGeojson: {
        type: 'geojson',
        url: 'india-states.geojson',
        link: {
          url: 'state_score.json',    // Load data from this file
          dataKey: 'name',            // Join this column from the URL (data)
          mapKey: 'ST_NM'             // with this property in the GeoJSON
        },
295 296 297
        tooltip: function(prop) {   // On hover, show this HTML tooltip
          return prop.ST_NM + ': ' + prop.TOT_P
        },
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
        attrs: {
          fillColor: {                // Fill the regions
            metric: 'score',          // with the "score" column state_score.json
            range: 'RdYlGn'           // using a RdYlGn gradient
          }
        }
      }
    },
    drilldown: {
      rootLayer: 'indiaGeojson',
      levels: [
        {
          layerName: function(properties) {return properties['STATE'] + '-layer'},
          layerOptions: {
            url: function(properties) {return properties['STATE'] + '-census.json'},
            type: 'geojson',
            attrs: {
              fillColor: {
                metric: 'DT_CEN_CD',
                range: 'RdYlGn'
              }
319 320 321
            },
            tooltip: function (properties) {
              return 'DISTRICT: ' + properties['DISTRICT']
322 323 324 325 326 327 328 329 330
            }
          }
        }
      ]
    }
  })
</script>
```

331
Examples showing usage of mismatch label:
332

333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
```html
<div class="error-pange"></div>
<div id="mismatch_map" class="map"></div>
<script>
  var custom_mismatch_map = g1.mapviewer({
    id: 'custom-mismatch-map',
    layers: {
      indiaGeojson: {
        type: 'geojson',
        url: 'india-states.geojson',
        link: {
          url: state_scores,
          dataKey: 'name',
          mapKey: 'ST_NM',
          mismatch: function (mismatch_array ) {
            // Render custom message
            var custom_message = `<h2>List of Data Merge Mismatches</h2>`
            custom_message += `<table>`
            mismatch_array.forEach(function(obj){
              custom_message +=  `<tr><td>${obj.feature.properties.ST_NM}</td></tr>`
            })
            custom_message += `</table>`
            $('.error-pane')
              .html(custom_message)
          }
        }
      }
    }
  })
362

363
</script>
364

365
```