Skip to content

Instantly share code, notes, and snippets.

@rclark
Last active December 15, 2015 14:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rclark/5274320 to your computer and use it in GitHub Desktop.
Save rclark/5274320 to your computer and use it in GitHub Desktop.
Wrapped http://bost.ocks.org/mike/leaflet/ into a Leaflet Layer
root = @
L.GeoJSON.d3 = L.GeoJSON.extend
initialize: (geojson, options) ->
@geojson = geojson # This needs to be a FeatureCollection
# unlike L.GeoJSON layer, this won't work unless GeoJSON is passed up-front
# Make sure there's an options object
options = options or {}
# set a layerId
options.layerId = options.layerId or "leaflet-d3-layer-#{geojson.features.length}"
options.onEachFeature = (geojson, layer) ->
# Do stuff with each feature. Layer is a Leaflet wrapper around geojson
# Maybe you can style individual features here based on properties?
# Call the GeoJSON initilization function to utilize its perks
L.GeoJSON.prototype.initialize.call this, geojson, options
onAdd: (map) ->
# From Leaflet API docs:
# Should contain code that creates DOM elements for the overlay, adds
# them to map panes where they should belong and puts listeners on
# relevant map events. Called on map.addLayer(layer).
# Put an SVG element into Leaflet's .leaflet-overlay-pane
overlayPane = root.map.getPanes().overlayPane
d3Selector = d3.select overlayPane
@_svg = svg = d3Selector.append "svg"
svg.attr "class", "leaflet-d3-layer"
svg.attr "id", @options.layerId
# Put a group element within that
g = svg.append "g"
g.attr "class", "leaflet-zoom-hide leaflet-d3-group"
# Use Leaflet to project from geographic to pixel coordinates
project = (d3pnt) ->
geoPnt = new L.LatLng d3pnt[1], d3pnt[0]
pixelPnt = root.map.latLngToLayerPoint geoPnt
return [ pixelPnt.x, pixelPnt.y ]
# Create a d3.geo.path (https://github.com/mbostock/d3/wiki/Geo-Paths#wiki-path)
# doesn't appear to have any data bound to it at this point
path = d3.geo.path().projection project
# Find the bounds of the geojson collection
bounds = d3.geo.bounds @geojson
# Create path elements for each feature using D3's data join (http://bost.ocks.org/mike/join/)
# path elements are empty, will later be initialized by adding the d attribute
# this seems to create a path element for every feature
paths = g.selectAll "path"
data = paths.data @geojson.features
feature = data.enter().append "path"
# Function to define the appropriate size for the svg element
reset = () ->
# Setup a buffer so you don't truncate any symbols
bufferPixels = 15
bottomLeft = project bounds[0]
topRight = project bounds[1]
svg.attr "width", topRight[0] - bottomLeft[0] + 2*bufferPixels
svg.attr "height", bottomLeft[1] - topRight[1] + 2*bufferPixels
svg.style "margin-left", "#{bottomLeft[0] - bufferPixels}px"
svg.style "margin-top", "#{topRight[1] - bufferPixels}px"
g.attr "transform", "translate(#{-bottomLeft[0] + bufferPixels},#{-topRight[1] + bufferPixels})"
# Here is where we apparently "initialize the path data by setting the d attribute" (http://bost.ocks.org/mike/leaflet/)
feature.attr "d", path
# Bind that reset function to a Leaflet map event (resize svg whenever the map is moved)
root.map.on "viewreset", reset
# Then call it to get things started
reset()
# Bind the reset function to something in broader scope so it can later be unbound
# from the viewreset event
root["d3ResetFunctionFor#{@options.layerId}"] = reset
# can you say "polute the global namespace"?
# If you want to call the default...
#L.LayerGroup.prototype.onAdd.call this, map
onRemove: (map) ->
# From Leaflet API docs:
# Should contain all clean up code that removes the overlay's elements
# from the DOM and removes listeners previously added in onAdd.
# Called on map.removeLayer(layer).
@_svg.remove()
map.off "viewreset", root["d3ResetFunctionFor#{@options.layerId}"]
# If you want to call the default...
#L.LayerGroup.prototype.onRemove.call this, map
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment