Skip to content

Instantly share code, notes, and snippets.

@emeeks
Last active August 29, 2015 14:05
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save emeeks/905bb4f399e0855e08fe to your computer and use it in GitHub Desktop.
Custom Infobox Formatter - d3.carto.map

A demonstration of custom modal infoboxes from d3.carto.map.

If you click on a site point (the pink circles) you'll see an infobox that reads something like "Lugdunum Convenarum in modern day France" whereas if you click on a country, you'll see an infobox that reads something like "The country of Spain has a short name".

How a modal handles the data in a datapoint for display is handled by defining a custom formatter for the modal, which requires that you create a new d3.carto.modal. The new modal is assigned to a d3.carto.layer using .modal() as shown in the example for the CSV Points and Feature Array layers.

Formatters are defined using the standard D3 accessor syntax. The default formatter, demonstrated in the basic modal example. Since you know your data better than d3.carto does, you can specify how to parse what attributes in as specific a manner as you want. The function should return a string of HTML suitable to be dropped into the modal div.

The key lines for defining the modal and formatter are:

    csvModal = d3.carto.modal();
    csvModal.formatter(
      function(d) {return "<h1>" + d.label + " in modern day " + d.modern + "</h1>"}
    )

and:

    featureModal = d3.carto.modal();
    featureModal.formatter(
      function(d) {return "<p class='countryInfobox'>The country of " + d.properties.name + " has a short name</p>"}
    )
path,circle,rect,polygon,ellipse,line {
vector-effect: non-scaling-stroke;
}
svg, canvas {
top: 0;
}
#d3MapZoomBox {
position: absolute;
z-index: 10;
height: 100px;
width: 25px;
top: 10px;
right: 50px;
}
#d3MapZoomBox > button {
height:25px;
width: 25px;
line-height: 25px;
}
.d3MapControlsBox > button {
font-size:22px;
font-weight:900;
border: none;
height:25px;
width:25px;
background: rgba(35,31,32,.85);
color: white;
padding: 0;
cursor: pointer;
}
.d3MapControlsBox > button:hover {
background: black;
}
#d3MapPanBox {
position: absolute;
z-index: 10;
height: 100px;
width: 25px;
top: 60px;
right: 50px;
}
#d3MapPanBox > button {
height:25px;
width: 25px;
line-height: 25px;
}
#d3MapPanBox > button#left {
position: absolute;
left: -25px;
top: 10px;
}
#d3MapPanBox > button#right {
position: absolute;
right: -25px;
top: 10px;
}
#d3MapLayerBox {
position: relative;
z-index: 10;
height: 100px;
width: 120px;
top: 10px;
left: 10px;
overflow: auto;
color: white;
background: rgba(35,31,32,.85);
}
#d3MapLayerBox > div {
margin: 5px;
border: none;
}
#d3MapLayerBox ul {
list-style: none;
padding: 0;
margin: 0;
cursor: pointer;
}
#d3MapLayerBox li {
list-style: none;
padding: 0;
}
#d3MapLayerBox li:hover {
font-weight:700;
}
#d3MapLayerBox li input {
cursor: pointer;
}
div.d3MapModal {
position: absolute;
z-index: 11;
background: rgba(35,31,32,.90);
top: 50px;
left: 50px;
color: white;
max-width: 400px;
}
div.d3MapModalContent {
width:100%;
height: 100%;
overflow: auto;
}
div.d3MapModalContent > p {
padding: 0px 20px;
margin: 5px 0;
}
div.d3MapModalContent > h1 {
padding: 0px 20px;
font-size: 20px;
}
div.d3MapModalArrow {
content: "";
width: 0;
height: 0;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
border-top: 20px solid rgba(35,31,32,.90);
position: absolute;
bottom: -20px;
left: 33px;
}
#d3MapSVG {
}
rect.minimap-extent {
fill: rgba(200,255,255,0.35);
stroke: black;
stroke-width: 2px;
stroke-dasharray: 5 5;
}
circle.newpoints {
fill: black;
stroke: red;
stroke-width: 2px;
}
path.newfeatures {
fill: steelblue;
fill-opacity: .5;
stroke: pink;
stroke-width: 2px;
}
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>d3.carto.map - custom infobox formatters</title>
<meta charset="utf-8" />
<link type="text/css" rel="stylesheet" href="d3map.css" />
<link type="text/css" rel="stylesheet" href="https://raw.githubusercontent.com/emeeks/d3-carto-map/master/examples/example.css" />
</head>
<style>
html,body {
height: 100%;
width: 100%;
margin: 0;
}
#map {
height: 100%;
width: 100%;
position: absolute;
}
.countryInfobox {
color: pink;
font-style: italic;
padding: 20px;
}
rect.bluesquare {
fill: #4682B4;
stroke: black;
stroke-width: 1px;
}
.countryborders {
fill: rgba(0,0,0,0);
stroke-width: 1px;
stroke: gray;
cursor: pointer;
}
.roads {
stroke: brown;
stroke-width: 1px;
fill: none;
}
.halffilledcountries {
fill: rgba(224,224,209,0.5);
stroke-width: 1px;
stroke: black;
opacity: 1;
}
</style>
<script>
function makeSomeMaps() {
map = d3.carto.map();
d3.select("#map").call(map);
tileLayer = d3.carto.layer.tile();
tileLayer
.path("examples.map-zgrqqx0w")
.label("Base")
.on("load", recenter);
geojsonLayer = d3.carto.layer.geojson();
geojsonLayer
.path("http://bl.ocks.org/emeeks/raw/c970c9ee3e242e90004b/world.geojson")
.label("GeoBorders")
.cssClass("countryborders")
.renderMode("canvas")
.on("load", createFeatureLayer);
topojsonLayer = d3.carto.layer.topojson();
topojsonLayer
.path("http://bl.ocks.org/emeeks/raw/c970c9ee3e242e90004b/sample_routes.topojson")
.label("TopoRoutes")
.cssClass("roads")
.renderMode("svg")
.clickableFeatures(true);
csvModal = d3.carto.modal();
csvModal.formatter(
function(d) {return "<h1>" + d.label + " in modern day " + d.modern + "</h1>"}
)
csvLayer = d3.carto.layer.csv();
csvLayer
.type("csv")
.path("http://bl.ocks.org/emeeks/raw/c970c9ee3e242e90004b/sample_points.csv")
.label("CSV Points")
.cssClass("pinkcircle")
.renderMode("svg")
.markerSize(8)
.x("x")
.y("y")
.modal(csvModal)
.clickableFeatures(true);
map.addCartoLayer(tileLayer);
map.addCartoLayer(topojsonLayer);
map.addCartoLayer(geojsonLayer);
map.addCartoLayer(csvLayer);
function recenter() {
map.centerOn([3,43],"latlong");
}
function createFeatureLayer() {
var featuresArray = [];
var mapLayers = map.layers();
mapLayers.forEach(function (layer) {
if (layer.label() == "GeoBorders") {
featuresArray = layer.features();
}
})
shortNameCountries = featuresArray.filter(function(d) {return d.properties.name.length < 7})
featureModal = d3.carto.modal();
featureModal.formatter(
function(d) {return "<span class='countryInfobox'>The country of " + d.properties.name + " has a short name</span>"}
)
featureLayer = d3.carto.layer.featureArray();
featureLayer
.features(shortNameCountries)
.label("Feature Array")
.cssClass("halffilledcountries")
.renderMode("svg")
.clickableFeatures(true)
.modal(featureModal);
map.addCartoLayer(featureLayer);
}
}
</script>
<body onload="makeSomeMaps()">
<div id="map"></div>
<footer>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8" type="text/javascript"></script>
<script src="http://d3js.org/topojson.v1.min.js" type="text/javascript">
</script>
<script src="http://d3js.org/d3.geo.projection.v0.min.js" type="text/javascript">
</script>
<script src="http://d3js.org/colorbrewer.v1.min.js"></script>
<script src="http://bl.ocks.org/emeeks/raw/f3105fda25ff785dc5ed/tile.js" type="text/javascript">
</script>
<script src="http://bl.ocks.org/emeeks/raw/f3105fda25ff785dc5ed/d3.quadtiles.js" type="text/javascript">
</script>
<script src="http://bl.ocks.org/emeeks/raw/f3105fda25ff785dc5ed/d3.geo.raster.js" type="text/javascript">
</script>
<script src="https://rawgit.com/emeeks/d3-carto-map/master/d3.carto.map.js" type="text/javascript">
</script>
</footer>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment