A JavaScript library for making data-driven maps. See it in action.
Swiftmap is still in development mode (v0.x). This is not a stable release. The API is subject to breaking changes.
var map = swiftmap.map()
.layerPolygons(UsaStatesTopoJSON, d => d.stateName)
.draw();
var colors = ["red", "orange", "yellow", "green", "blue", "purple"];
map.layers[0].polygons.style("fill", (d, i) => colors[i % colors.length]);
window.onresize = () => map.resize();
var scheme = swiftmap.schemeSequential()
.data(UsaStatesByPopulation, d => d.stateName)
.from(d => d.population)
.to(["#ffffcc", "#a1dab4", "#41b6c4", "#2c7fb8", "#253494"]);
map.layers[0].polygons.style("fill", scheme);
In vanilla, a swiftmap
global is exported. You can use the CDN from unpkg.
<script src="https://unpkg.com/swiftmap/dist/swiftmap.js"></script>
<script src="https://unpkg.com/swiftmap/dist/swiftmap.min.js"></script>
If you'd rather host it yourself, download swiftmap.js
or swiftmap.min.js
from the dist
directory.
<script src="https://github.com/HarryStevens/swiftmap/raw/master/path/to/swiftmap.js"></script>
<script src="https://github.com/HarryStevens/swiftmap/raw/master/path/to/swiftmap.min.js"></script>
npm install swiftmap --save
var swiftmap = require("swiftmap");
Before drawing and styling a map, you can tell Swiftmap where on the DOM to place the map. You may also specify a map projection and call methods for resizing the map when the browser window's dimensions change.
Initializes a map.
parent
If parent is specified, the map will be placed in the DOM element referenced by the parent's selector. The parent must be specified as a string. If parent is not specified, "body"
will be used as the parent. The map will inherit the dimensions of its parent.
Adds a graticule to the map. See it in action.
step
To set the frequency of the graticule's lines, a step may be specified as an array of two numbers, where the first number is the frequency in degrees of the lines of longitude, and the second number is the frequency in degrees of latitude. Defaults to [10, 10]
.
# map.projection([projection]) <>
Sets or gets a map's projection.
projection
If projection is specified, sets the map's projection. The projection must be specified as a string, and can be one of three options:
"mercator"
, for the Mercator projection"equirectangular"
, for the equirectangular projection"albersUsa"
, for the Albers USA projection, which is a composite of three Albers' equal-area conic projectionsIf projection is not specified, returns the projection associated with the map. For more information, see the documentation in d3-geo.
Resizes the map. This method is useful if your map must respond to window resizes.
# map.tiles([tilesource | tilefunction]) <>
Adds raster tiles to the map's background and exposes the CSS class tile
. Note that these raster tiles only work with a Mercator projection. If you use these raster tiles, please include a copyright notice with your map. See it in action.
tilesource
You can specify a tilesource as a string to determine the style of raster tiles. Defaults to "openStreetMap"
. Your options are:
"cartoDark"
Carto dark tiles. Copyright: Map tiles by Carto."cartoDarkLabels"
Carto dark tiles' labels. Copyright: Map tiles by Carto."cartoLight"
Carto light tiles. Copyright: Map tiles by Carto."cartoLightLabels"
Carto light tiles' labels. Copyright: Map tiles by Carto."cartoLightNoLabels"
Carto light tiles without labels. Copyright: Map tiles by Carto."mapboxNaturalEarth"
Mapbox Natural Earth II. Copyright: Map tiles by MapBox."openStreetMap"
Open Street Map tiles. Copyright: © OpenStreetMap contributors."stamenToner"
Stamen toner tiles. Copyright: Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL."stamenTerrain"
Stamen terrain tiles. Copyright: Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL."stamenTerrainLabels"
Stamen terrain tiles' labels. Copyright: Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL."stamenTerrainNoLabels"
Stamen terrain tiles without labels. Copyright: Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL."stamenWatercolor"
Stamen watercolor tiles. Copyright: Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under CC BY SA."wikimedia"
Wikimedia tiles. Copyright: Wikimedia | © OpenStreetMap.tilefunction
Instead of a tilesource, you can specify a tilefunction to generate a tiles URL. The tiles are generated by d3-tile and have the properties x
, y
, z
, tx
and ty
. You can read more about these properties in the documentation. For example, a tilefunction could be specified as:
var tileGenerator = tile => "http://" + "abc"[tile.y % 3] + ".tile.openstreetmap.org/" + tile.z + "/" + tile.x + "/" + tile.y + ".png";
map.tiles(tileGenerator);
Map attributes
The map's dimensions.
# map.svg
The D3 selection of the SVG element containing the map.
# map.parent
A string of the map's parent element.
Layers let you add geospatial data to a map, as well as decide how that data should be drawn to the DOM. The recommended indenting pattern is to indent two spaces to declare a new layer, calling either map.layerPolygons() or map.layerPoints(), and to indent four spaces when calling drawing functions on the preceding layer.
var map = swiftmap.map("#map")
.layerPolygons(TopoJSON, d => d.state_name, "states")
.drawPolygons()
.drawBoundary()
.layerPolygons(TopoJSON, d => d.county_name, "counties")
.draw()
.layerPoints(TopoJSON, d => d.city_name, "cities")
.drawPoints()
.drawLabels(d => d.city_name);
map.layers[1].polygons.style("fill", scheme);
Layers can be styled with CSS selectors.
#map .boundary {
stroke-width: 2px;
}
#map .boundary.boundary-states {
stroke-width: 3px;
}
#map .polygon {
fill: none;
}
#map .polygon.polygon-counties {
stroke-dasharray: 5, 5;
}
#map .point.point-cities {
fill: blue;
}
#map .label.label-cities {
font-size: 1.2em;
}
# map.layerPolygons([data][, key][, layer]) <>
Sets or gets a polygons layer.
data
The data must be specified as a TopoJSON object. If no data is passed, returns the geospatial data associated with the layer.
key
Each datum will be assigned a key value based on the value returned by an optional key function. This key will be used to match each datum of geospatial data to a corresponding datum of tabular data when a scheme is passed to a style or attribute of the layer. If no key is specified, each datum will be assigned a key according to its index.
layer
If a layer is passed, the geospatial data will be associated with the name of the layer, which must be specified as a string. In the layer string, space characters will be converted to hyphens so that DOM elements produced by the layer's geospatial data can be referenced with CSS selectors.
If a layer is not passed, the geospatial data will be associated with the index of the layer, where the first layer is indexed to 0
. For instance, the name of the third layer added to the map will default to 2
, and can be styled with CSS using the following pattern:
#map .boundary.boundary-2 {
stroke-width: 2px;
}
Draws a polygons layer. This is a convenience method equivalent to map.fit().drawPolygons().drawBoundary().
layer
If layer is not specified, the most recently added layer will be drawn by default. If you wish to change the default behavior, you may specify a layer as a string or a number corresponding to a layer that has already been added to the map, and Swiftmap will draw or redraw the specified layer.
# polygons.drawBoundary([layer]) <>
Draws a polygons layer's outer boundary. The boundary is rendered as an SVG path
element, can be accessed as a D3 selection via map.layers.< layername >.boundary
, and can be styled with the CSS class .boundary
or .boundary-< layername >
or both.
layer
If layer is not specified, the boundary of the most recently added layer will be drawn by default. If you wish to change the default behavior, you may specify a layer as a string or a number corresponding to a layer that has already been added to the map, and Swiftmap will draw or redraw the boundary of the specified layer.
# map.layerPoints([data][, key][, layer]) <>
Sets or gets a points layer. See map.polygons() for descriptions of the arguments.
# polygons.drawLabels(key[,offset][, layer]) <>
Labels each polygon at its pole of inaccessibility or, in the case of complex multipolygons, its centroid. Labels are rendered as SVG text
elements, can be accessed as D3 selections via map.layers.< layername >.labels
, and can be styled with the CSS class .label
or .label-< layername >
or both.
key
A key function tells Swiftmap how each datum should be labeled.
var key = d => d.properties.name;
map.drawLabels(key);
offset
By default, labels are centered on the coordinates of the polygons' centroid. To offset the labels, you may specify the offset as true
. If the polygons's centroid is on the left half of the map, the label will be placed to the left side of the point. If the polygons's centroid is on the right half of the map, the label will be placed to the right side of the point.
layer
If layer is not specified, the labels of the most recently added layer will be drawn by default. If you wish to change the default behavior, you may specify a layer as a string or a number corresponding to a layer that has already been added to the map, and Swiftmap will draw or redraw the labels of the specified layer.
# polygons.drawPoints([radius][, layer]) <>
Draws points at the centroid of each polygon in a polygons layer. Points are rendered as SVG circle
elements, can be accessed as D3 selections via map.layers.< layername >.points
, and can be styled with the CSS class .point
or .point-< layername >
or both.
radius
If radius is specified, sets each point's radius in pixels. Defaults to 2
.
layer
If layer is not specified, the points of the most recently added layer will be drawn by default. If you wish to change the default behavior, you may specify a layer as a string or a number corresponding to a layer that has already been added to the map, and Swiftmap will draw or redraw the polygons of the specified layer.
# polygons.drawPolygons([layer]) <>
Draws a polygons layer's polygons. For example, if the layer's TopoJSON contains states, the polygons are the states. Polygons are rendered as SVG path
elements, can be accessed as D3 selections via map.layers.< layername >.polygons
, and can be styled with the CSS class .polygon
or .polygon-< layername >
or both.
layer
If layer is not specified, the polygons of the most recently added layer will be drawn by default. If you wish to change the default behavior, you may specify a layer as a string or a number corresponding to a layer that has already been added to the map, and Swiftmap will draw or redraw the polygons of the specified layer.
Updates the projection so that a layer's outer boundary fits the map's parent element. Overrides any previous invocations of map.fit(), as the map can only have one projection.
layer
If layer is not specified, the most recently added layer will be fit to the boundary of the parent element. If you wish to change the default behavior, you may specify a layer as a string or a number corresponding to a layer that has already been added to the map, and Swiftmap will fit the specified layer's outer boundary to the parent element.
# map.layerPoints([data][, key][, layer]) <>
Sets or gets a points layer. See map.polygons() for descriptions of the arguments.
# points.drawLabels(key[,offset][, layer]) <>
Labels the points. Labels are rendered as SVG text
elements, can be accessed as D3 selections via map.layers.< layername >.labels
, and can be styled with the CSS class .label
or .label-< layername >
or both.
key
A key function tells Swiftmap how each datum should be labeled.
var key = d => d.properties.name;
map.drawLabels(key);
offset
By default, labels are centered on the coordinates of the point. To offset the labels, you may specify the offset as true
. If the label's point is on the left half of the map, the label will be placed to the left side of the point. If the label's point is on the right half of the map, the label will be placed to the right side of the point.
layer
If layer is not specified, the labels of the most recently added layer will be drawn by default. If you wish to change the default behavior, you may specify a layer as a string or a number corresponding to a layer that has already been added to the map, and Swiftmap will draw or redraw the labels of the specified layer.
# points.drawPoints([radius][, layer]) <>
Draws circles, located at each point's coordinates, to a layer. Points are rendered as SVG circle
elements, can be accessed as D3 selections via map.layers.< layername >.points
, and can be styled with the CSS class .point
or .point-< layername >
or both.
radius
If radius is specified, sets each point's radius in pixels. Defaults to 2
.
layer
If layer is not specified, the points of the most recently added layer will be drawn by default. If you wish to change the default behavior, you may specify a layer as a string or a number corresponding to a layer that has already been added to the map, and Swiftmap will draw or redraw the points of the specified layer.
See polygons.fit().
Schemes provide an interface for mapping values of your data to visual attributes, such as a choropleth map's color palette or the radii of circles in a bubble map. Schemes can be added to a map like so:
map.layers[0].style("fill", schemeSequential);
map.layers[1].attr("r", schemeContinuous);
map.layers[2].style("opacity", schemeCategorical);
# swiftmap.schemeCategorical() <>
Categorical schemes are used to assign styles or attributes of layer elements, such as polygons' fill color, to non-numerical categories of data, such as political parties in an election.
var scheme = swiftmap.schemeCategorical()
.data(JSON, d => d.state)
.from(d => d.party)
.to({
"Republican": "tomato",
"Democratic": "steelblue"
})
.toOther("yellow");
# categorical.data([data][, key])
Adds data to a scheme, where each datum corresponds to each element of a layer.
data
The data must be specified as a JSON array. If no data is passed, returns the data associated with the scheme.
key
Each datum will be assigned a key value returned by an optional key function. This key will be used to match each datum of tabular data to a corresponding datum of geospatial data when the scheme is passed to a style or attribute of a layer. If no key is specified, each datum will be assigned a key according to its index.
# categorical.from(function)
Sets the values accessor to the specified function, allowing the scheme to interact with a layer's data.
function
When the scheme is passed to a style or attribute of a layer, the function will be called for each datum in the layer's data array, being passed the datum d
, the index i
, and the array data
as three arguments. For example, if you want your scheme to be based on each polygon's party:
var data = [
{party: "Democratic", state: "California"},
{party: "Republican", state: "Texas"},
...
];
scheme
.data(data, d => d.state)
.from(d => d.party);
map.layers[0].polygons.style("fill", scheme);
# categorical.to([object])
Specifies how the scheme should visually render the values returned by categorical.from().
object
If an object is specified, it must be specified as an object where each property is one of the scheme's categories – that is, a value returned by categorical.from() – and each value is the visual style or attribute associated with that category.
scheme.to({
"Republican": "tomato",
"Democratic": "steelblue"
});
If object is not specified, returns the object associated with the scheme.
# categorical.toOther([value])
Sets or gets an alternative value in the scheme.
value
If a value is specified, assigns values to those DOM elements whose category is not present among the properties of the object passed to categorical.to(). If value is not specified, returns the scheme's alternative value, which defaults to null
.
# swiftmap.schemeContinuous() <>
Continuous schemes are used to map values of data to corresponding visual attributes along a continuum. You can use a continuous scheme to make a bubble map where the radius of each bubbble corresponds to the magnitude of each datum. You can also use a continuous scheme to create choropleth maps with a gradient scale.
var scheme = swiftmap.schemeContinuous()
.data(JSON)
.from(d => d.value)
.to([2, 20]);
# continuous.data([data][, key])
See categorical.data().
# continuous.from(function)
See categorical.from().
# continuous.to([array])
Sets or gets the minimum and maximum values of a visual attribute associated with the scheme.
array
If a array is specified, sets the sequence of values of the the visual attribute associate with the scheme. The array can contain as many items as you like, and the scheme will map data values returned by continuous.from() to corresponding values in the array. If a array is not specified, returns the array associated with the scheme, which defaults to [0, 1]
.
# continuous.toOther([value])
# swiftmap.schemeSequential() <>
Sequential schemes are used to assign styles or attributes of layer elements, such as polygons' fill color, to discrete ranges in a series of values that progress from low to high.
var scheme = swiftmap.schemeSequential()
.data(JSON)
.from(d => d.value)
.to(["#ffffcc", "#a1dab4", "#41b6c4", "#2c7fb8", "#253494"])
.breaks("q");
# sequential.breaks([breaktype | breaklist])
Computes class breaks based on data. If no argument is passed, returns the scheme's breaklist – an array of numbers representing the breaks associated with the scheme.
breaktype
If a breaktype is specified, the scheme will compute the class breaks based on the values returned by the function passed to sequential.from(). The breaktype must be specified as a string, either "e"
, "q"
, "l"
or "k"
.
"e"
specifies equidistant breaks, where each break spans an equal numeric range."l"
specifies logarithmic breaks, which are just like equidistant breaks but on a logarithmic scale."q"
specifies quantile breaks, where an equal number of data points are placed into each break."k"
specifies k-means breaks, which use a k-means clustering algorithm to group similar data points with each other.The breaktype will default to "q"
if sequential.breaks() is not called.
breaklist
If you'd rather use custom breaks, you can specify a breaklist as an array of numbers. The length of the breaklist should be one greater than the length of the array passed to sequential.to(), and its extent should span the range of values returned by the function passed to sequential.from().
var scheme = swiftmap.schemeSequential()
.from(d => d.value)
.to(["tomato", "lightblue", "steelblue", "darkblue"])
.breaks([-.5, 0, 20, 25, 55]);
# sequential.data([data][, key])
See categorical.data().
# sequential.from(function)
See categorical.from(). For example, if you want your scheme to be based on each polygon's population density:
var data = [
{population: "15324", area: "124", county: "Foo"},
{population: "23540", area: "365", county: "Bar"},
...
];
scheme
.data(data, d => d.county)
.from(d => +d.population / +d.area);
map.layers[0].polygons.style("fill", scheme);
# sequential.to([array])
Specifies the series of styles or attributes to which values should be assigned, such as a serious of color buckets in a choropleth map.
array
If an array is specified, the scheme will assign a series of values to each item in the array. If array is not specified, returns the array associated with the scheme.
The array will default to []
if this method is not called.
# sequential.toOther([value])
git clone https://github.com/HarryStevens/swiftmap # clone this repository
cd swiftmap # navigate into the directory
npm install # install node modules
Swiftmap is compiled with rollup. Each function can be found in the src
directory.
npm run rollup # compile the library
npm run minify # minify the library
npm run build # compile and minify the library
Swiftmap also uses a custom version of D3.js, which can be found in lib/d3
. If you need to update the bundle, do cd lib/d3
, where you can install additional dependencies and update the index.js
file. You will also have to update the globals
object and the only
array in the resolve()
function in rollup.config.js
.