Skip to content

Instantly share code, notes, and snippets.

Last active February 8, 2016 23:05
Show Gist options
  • Save mbostock/590744 to your computer and use it in GitHub Desktop.
Save mbostock/590744 to your computer and use it in GitHub Desktop.
Fancy Markers
license: gpl-3.0

The default display for GeoJSON points in Polymaps is a 4.5px-radius circle. In this example, I show how to create more traditional pin-style markers for points. This is implemented using a “load” event handler which replaces the default circles with custom path elements.

The marker outline is courtesy of Dmitry Baranovskiy; he has graciously provided over 100 free vector icons to choose from. Of course, there’s no requirement to use vector icons. You could replace the circles with image elements instead, if you prefer raster. But the nice thing about vectors is that we can color them dynamically based on the data, as we do here by generating three categorical color gradients.

<!DOCTYPE html>
<script type="text/javascript" src=""></script>
<script type="text/javascript" src=""></script>
<script type="text/javascript" src=""></script>
<style type="text/css">
@import url("");
html, body {
height: 100%;
background: #E6E6E6;
svg {
display: block;
.layer use {
stroke: #ccc;
stroke-opacity: .5;
<script type="text/javascript">
var po = org.polymaps;
var svg = document.body.appendChild(po.svg("svg")),
defs = svg.appendChild(po.svg("defs"));
/* Create three linear gradients for each category. */
defs.appendChild(gradient("#D90000", "#A30000")).setAttribute("id", "gradient-violent");
defs.appendChild(gradient("#23965E", "#1A7046")).setAttribute("id", "gradient-property");
defs.appendChild(gradient("#3489BA", "#27678B")).setAttribute("id", "gradient-quality");
/* Create a marker path. */
defs.appendChild(icons.marker()).setAttribute("id", "marker");
var map =
.center({lat: 37.787, lon: -122.228})
.zoomRange([10, 16])
+ "/1a1b06b230af4efdbb989ea99e9841af" //
+ "/998/256/{Z}/{X}/{Y}.png")
.hosts(["a.", "b.", "c.", ""])));
+ "/crime-data"
+ "?count=100"
+ "&format=json"
+ "&bbox={B}"
+ "&dstart=2010-04-01"
+ "&dend=2010-04-01"))
.on("load", load)
/* Post-process the GeoJSON points and replace them with markers! */
function load(e) {
e.features.sort(function(a, b) {
return[1] -[1];
for (var i = 0; i < e.features.length; i++) {
var f = e.features[i],
d =,
c = f.element,
p = c.parentNode,
u = f.element = po.svg("use");
u.setAttributeNS(po.ns.xlink, "href", "#marker");
u.setAttribute("transform", c.getAttribute("transform"));
u.setAttribute("fill", "url(#gradient-" + crimespotting.categorize(d) + ")");
/* Helper method for constructing a linear gradient. */
function gradient(a, b) {
var g = po.svg("linearGradient");
g.setAttribute("x1", 0);
g.setAttribute("y1", 1);
g.setAttribute("x2", 0);
g.setAttribute("y2", 0);
var s0 = g.appendChild(po.svg("stop"));
s0.setAttribute("offset", "0%");
s0.setAttribute("stop-color", a);
var s1 = g.appendChild(po.svg("stop"));
s1.setAttribute("offset", "100%");
s1.setAttribute("stop-color", b);
return g;
<span id="copy">
&copy; 2010
<a href="">CloudMade</a>,
<a href="">OpenStreetMap</a> contributors,
<a href="">CCBYSA</a>.
Copy link

Looks like Firefox doesn't display defs if you use "url(#foo)" in the use element's href. It works without the url() part as far as I can tell.

Copy link

Uh, you're right. Looks like the url() part isn't needed for that parameter. XLink is hard!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment