I want to create a simple map for a country (perhaps an island) that consists of three regions. Here's how this country looks like:
The three regions are confined by what TopoJSON refers to as
arcs: A red arc to the West, an orange arc to the North East, a blue arc
to the South east, and a light blue, a purple and a green arc going to the center of the country.
To draw the map with these regions, I need to create a TopoJSON topology object. Usually, this object is specified in JSON, but for simplicity's
sake, I just create an ordinary JavaScript object. This JavaScript object has a member
arcs
that is an array.
arcs: [ ..... ]
For each arc in the map, another array is inserted into this
arcs:
array:
arcs: [
[ ... ], // Red arc
[ ... ], // light blue arc
[ ... ], // orange arc
/* etc */
]
The elements in these nested arrays deteremine the coordinates of the respective arc. These coordinates are specified in a special format:
the first element is the absolute coordinates and the following coordinates are relative to their previous coordinates. I hope the following
picture makes this a bit clearer:
The red arc starts at
-1,-3
and goes to
-3,-1
which is
-2,2
relative to the first coordinate.
The third (and last) coordinate is relative
1,3
to the second coordinate. Hence, the entries in the
arcs
array
become:
arcs: [
[ // Red arc
[-1, -3],
[-2, 2],
[ 1, 3]
],
[ // light blue arc
[ .. ],
[ .. ]
]
/* etc */
]
We can now refer to one of these arcs with an integer. The first arc is
0
, the second
1
etc.
If we want to refer to one of these arcs against its direction, the integer for the first arc is
-1
, for
the second arc, its
-2
etc. This allows us to define the regions with the integers for the respective
confining arc.
For example, the first region looks like
{
id: 'foo',
type: 'Polygon',
arcs: [ [0, -2, -5] ]
},
This indicates that the region with
id=foo
is confined by the first arc (
0
) in direction of the arc, the
second arc (
-2
, note the negative sign) against the direction of the arc (negative sign!) and the fifth arc (again
against its direction, negative sign).
Finally, the country (or island) needs to be placed somewhere on the earth. The latitute-spread of the Northernmost and Southernmost
point of the country is 20 degrees, therefore, the
scale
is
scale: [ 10/3, 10/3 ]
The middle of the country is 45 degrees north and 0 degrees west, so the translation becomes
translate: [0, 45]
So, the complete TopoJSON object for the country looks like this:
topology = {
type: 'Topology',
objects: {
regions: {
type: 'GeometryCollection',
geometries: [
{id: 'foo',
type: 'Polygon',
arcs: [ [0, -2, -5] ]
},
{id: 'bar',
type: 'Polygon',
arcs: [ [3, 1, -3 ] ]
},
{id: 'baz',
type: 'Polygon',
arcs: [ [ -6, 4, -4 ] ]
}
]
}
},
arcs: [
[ // Red arc # 0 / -1 {
[-1, -3],
[-2, 2],
[ 1, 3]
], // }
[ // light blue arc # 1 / -2 {
[ 0, 1],
[-1, -1],
[-1, 2]
], // }
[ // orange arc # 2 / -3 {
[ 3, 0],
[-1, 1],
[ 1, 2],
[-5,-1]
], // }
[ // green arc # 3 / -4 {
[ 3, 0],
[-3, 1]
], // }
[ // purple arc # 4 / -5 {
[ -1, -3],
[ 1, 4]
], // }
[ // blue arc # 5 / -6 {
[ -1, -3],
[ 4, 3]
] // }
],
transform: {
scale: [ 10/3, 10/3 ],
translate: [0, 45]
}
}
To show this topology with
d3.js
, the folllowing code should do:
var width = 1000;
var height = 500;
var projection = d3.geo.albers()
.center([0, 45])
.rotate([0,0])
.parallels([ 5,9])
.scale(1000)
.translate([width / 2, height / 2]);
// Create «SVG window»
var svg = d3.select("body").append("svg").attr("width", width ).attr("height", height);
// Create path generator
var path = d3.geo.path().projection(projection).pointRadius(2);
var regions = topojson.feature(topology, topology.objects.regions);
svg.selectAll(".region")
.data(regions.features)
.enter().append("path")
.attr("class", function(d) { return "region-" + d.id; })
.attr("d" , path);
Some CSS to change the colors of the regions:
svg {
background-color: #eee;
}
path {
stroke: #444;
stroke-width:2px;
}
.region-foo { fill: #7ad; }
.region-bar { fill: #d77; }
.region-baz { fill: #da7; }
The complete
html file is on github, as well
as the
inkscape/svg file for the graphics.