Skip to content

Instantly share code, notes, and snippets.

@chrisregner
Created September 7, 2017 10:45
Show Gist options
  • Save chrisregner/dff1bd7a6fa053fd898d1ef70c451c88 to your computer and use it in GitHub Desktop.
Save chrisregner/dff1bd7a6fa053fd898d1ef70c451c88 to your computer and use it in GitHub Desktop.
My attempt at replicating a tutorial's (https://bost.ocks.org/mike/leaflet/) example with a world map and TopoJson.
import React from 'react'
import styled from 'styled-components'
import L from 'leaflet'
import d3 from 'services/d3'
import * as topojson from 'topojson-client'
import 'leaflet/dist/leaflet.css'
/*===========================================================
= Leaflet assets importing issues fix =
===========================================================*/
// see https://github.com/PaulLeCam/react-leaflet/issues/255#issuecomment-261904061
delete L.Icon.Default.prototype._getIconUrl
L.Icon.Default.mergeOptions({
iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
iconUrl: require('leaflet/dist/images/marker-icon.png'),
shadowUrl: require('leaflet/dist/images/marker-shadow.png'),
})
/*===== End of Leaflet assets importing issues fix ======*/
/*================================================
= The Actual Map component =
================================================*/
const position = [37.8, -96.9]
const zoom = 4
const openStreetMapTileLayerUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
const openStreetMapAttribution = '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
class Map extends React.Component {
componentDidMount = () => {
// Rendering leaflet without react-leaflet
const leafletMap = L.map(this.mapEl).setView(position, zoom)
L.tileLayer(openStreetMapTileLayerUrl, {
attribution: openStreetMapAttribution,
})
.addTo(leafletMap)
this.leafletMap = leafletMap
// Add an SVG element to Leaflet’s overlay pane
const svg = d3.select(leafletMap.getPanes().overlayPane)
.append("svg")
// .style('position', 'relative')
// Inside the SVG, you’ll also need a G (group) element. This will be used to translate the SVG
// elements so that the top-left corner of the SVG, (0,0), corresponds to Leaflet’s layer origin.
const g = svg.append("g")
.attr("class", "leaflet-zoom-hide")
// load the world data (TopoJSON) using d3.json
d3.json("assets/topojson-world-atlas-50m.json", (err, worldTopoJson) => {
if (err) throw err
// Convert an input geometry (such as polygons in spherical geographic coordinates) to a
// different output geometry (such as polygons in projected screen coordinates)
function projectPoint(x, y) {
const point = leafletMap.latLngToLayerPoint(new L.LatLng(y, x, true))
this.stream.point(point.x, point.y)
}
// create a d3.geo.path to convert GeoJSON to SVG
const transform = d3.geoTransform({ point: projectPoint })
const path = d3.geoPath().projection(transform)
// convert topojson to geojson (the format that d3 can handle)
const worldGeoJson = topojson.feature(worldTopoJson, worldTopoJson.objects.countries)
// create path elements for each of the features using D3’s data join
const feature = g.selectAll("path")
.data(worldGeoJson.features)
.enter().append("path")
// compute SVG's dimension and position in accordance to the Leaflet map
const fitSvg = () => {
const bounds = path.bounds(worldGeoJson)
const topLeft = bounds[0]
const bottomRight = bounds[1]
console.log("width", bottomRight[0] - topLeft[0])
console.log("height", bottomRight[1] - topLeft[1])
console.log("left", topLeft[0] + "px")
console.log("top", topLeft[1] + "px")
svg.attr("width", bottomRight[0] - topLeft[0])
.attr("height", bottomRight[1] - topLeft[1])
.style("left", topLeft[0] + "px")
.style("top", topLeft[1] + "px")
g.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")")
// initialize the path data by setting the d attribute
feature.attr("d", path)
.style("fill-opacity", 0.7)
}
leafletMap.on('zoomend', fitSvg)
fitSvg()
})
}
mapElRef = (el) => { this.mapEl = el }
render = () => {
console.log('rendered')
// Create div for initializing map with the Leaflet way
return (
<div ref={this.mapElRef} className="vh-100"></div>
)
}
}
export default Map
/*===== End of The Actual Map Component ======*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment