Paul Greyson
Committed by Gerrit Code Review

better readme, do scaling properly

implement pan/zoom on topo
R to reset pan/zoom
require meta to drag or select nodes

Change-Id: I15e20296e76d5cd8656b144b2d61a6923a5509ad
......@@ -23,7 +23,7 @@
(function (onos) {
'use strict';
function createDragBehavior(force, selectCb, atDragEnd) {
function createDragBehavior(force, selectCb, atDragEnd, requireMeta) {
var draggedThreshold = d3.scale.linear()
.domain([0, 0.1])
.range([5, 20])
......@@ -51,29 +51,39 @@
drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on('dragstart', function(d) {
d.oldX = d.x;
d.oldY = d.y;
d.dragged = false;
d.fixed |= 2;
if (requireMeta ^ !d3.event.sourceEvent.metaKey) {
d3.event.sourceEvent.stopPropagation();
d.oldX = d.x;
d.oldY = d.y;
d.dragged = false;
d.fixed |= 2;
d.dragStarted = true;
}
})
.on('drag', function(d) {
d.px = d3.event.x;
d.py = d3.event.y;
if (dragged(d)) {
if (!force.alpha()) {
force.alpha(.025);
if (requireMeta ^ !d3.event.sourceEvent.metaKey) {
d.px = d3.event.x;
d.py = d3.event.y;
if (dragged(d)) {
if (!force.alpha()) {
force.alpha(.025);
}
}
}
})
.on('dragend', function(d) {
if (!dragged(d)) {
// consider this the same as a 'click' (selection of node)
selectCb(d, this); // TODO: set 'this' context instead of param
}
d.fixed &= ~6;
if (d.dragStarted) {
d.dragStarted = false;
if (!dragged(d)) {
// consider this the same as a 'click' (selection of node)
selectCb(d, this); // TODO: set 'this' context instead of param
}
d.fixed &= ~6;
// hook at the end of a drag gesture
atDragEnd(d, this); // TODO: set 'this' context instead of param
// hook at the end of a drag gesture
atDragEnd(d, this); // TODO: set 'this' context instead of param
}
});
return drag;
......
......@@ -5,11 +5,57 @@ npm install -g topojson
To generate continental US map:
$ wget 'http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/50m/cultural/ne_50m_admin_1_states_provinces_lakes.zip'
$ unzip ne_50m_admin_1_states_provinces_lakes.zip
$ ogr2ogr -f GeoJSON -where "sr_adm0_a3 IN ('USA')" states.json ne_50m_admin_1_states_provinces_lakes.shp
$ wget 'http://www.naturalearthdata.com/download/50m/cultural/ne_50m_admin_1_states_provinces_lakes.zip'
$ unzip ne_50m_admin_1_states_provinces_lakes.zip
$ ogr2ogr -f GeoJSON -where "sr_adm0_a3 IN ('USA')" states.json ne_50m_admin_1_states_provinces_lakes.shp
edit states.json to remove data for Hawaii and Alaska
$ topojson states.json > topology.json
$ topojson states.json > topology.json
The .shp file above is incomplete (USA and part of Candada.)
So it may be that each region requires a bit of research to generate.
Ideally a source for public domain shp files can be found that covers all geographic regions.
For Canada:
# wget 'http://www12.statcan.gc.ca/census-recensement/2011/geo/bound-limit/files-fichiers/gpr_000b11a_e.zip'
# unzip gpr_000b11a_e.zip
# ogr2ogr -f "GeoJSON" -s_srs EPSG:21781 -t_srs EPSG:4326 canada.json gpr_000b11a_e.shp
# topojson --id-property CFSAUID -p name=PRNAME -p name canada.json > topology.json
This produces a very large (5MB) file and draws very slowly in Chrome.
So some additional processing is required to simplify the geometry. (It is not checked in.)
Also, the specification of object structure within the geojson is unclear.
In the US map the geojson structure is
json.objects.states
but in the Canadian data it's
json.objects.canada
Lastly, the projection that is used may be tailored to the region.
The preferred projection for the US is "albers" and d3 provides a "albersUSA" which can be used to
project hawaii and alaska as well
For Canada, apparantly a "Lambert" projection (called conicConformal in d3) is preferred
see:
https://github.com/mbostock/d3/wiki/Geo-Projections
http://www.statcan.gc.ca/pub/92-195-x/2011001/other-autre/mapproj-projcarte/m-c-eng.htm
Summary:
- some additional work is required to fully generalize maps functionality.
- it may be worthwhile for ON.LAB to provide the topo files for key regions since producing these
files is non-trivial
......
......@@ -128,6 +128,7 @@
L: cycleLabels,
P: togglePorts,
U: unpin,
R: resetZoomPan,
W: requestTraffic, // bag of selections
X: cancelTraffic,
......@@ -168,6 +169,7 @@
// D3 selections
var svg,
zoomPanContainer,
bgImg,
topoG,
nodeG,
......@@ -179,6 +181,9 @@
// the projection for the map background
var geoMapProjection;
// the zoom function
var zoom;
// ==============================
// For Debugging / Development
......@@ -1358,6 +1363,33 @@
});
}
function zoomPan(scale, translate) {
zoomPanContainer.attr("transform", "translate(" + translate + ")scale(" + scale + ")");
// keep the map lines constant width while zooming
bgImg.style("stroke-width", 2.0 / scale + "px");
}
function resetZoomPan() {
zoomPan(1, [0,0]);
zoom.scale(1).translate([0,0]);
}
function setupZoomPan() {
function zoomed() {
if (!d3.event.sourceEvent.metaKey) {
zoomPan(d3.event.scale, d3.event.translate);
}
}
zoom = d3.behavior.zoom()
.translate([0, 0])
.scale(1)
.scaleExtent([1, 8])
.on("zoom", zoomed);
svg.call(zoom);
}
// ==============================
// Test harness code
......@@ -1438,11 +1470,15 @@
svg = view.$div.append('svg').attr('viewBox', viewBox);
setSize(svg, view);
zoomPanContainer = svg.append('g').attr('id', 'zoomPanContainer');
setupZoomPan();
// add blue glow filter to svg layer
d3u.appendGlow(svg);
d3u.appendGlow(zoomPanContainer);
// group for the topology
topoG = svg.append('g')
topoG = zoomPanContainer.append('g')
.attr('id', 'topo-G')
.attr('transform', fcfg.translate());
......@@ -1501,7 +1537,7 @@
.linkStrength(lstrg)
.on('tick', tick);
network.drag = d3u.createDragBehavior(network.force, selectCb, atDragEnd);
network.drag = d3u.createDragBehavior(network.force, selectCb, atDragEnd, true); // true=require meta
// create mask layer for when we lose connection to server.
mask = view.$div.append('div').attr('id','topo-mask');
......@@ -1593,15 +1629,15 @@
// [[x1,y1],[x2,y2]]
var b = path.bounds(topoData);
// TODO: why 1.75?
var s = 1.75 / Math.max((b[1][0] - b[0][0]) / config.logicalSize, (b[1][1] - b[0][1]) / config.logicalSize);
// size map to 95% of minimum dimension to fill space
var s = .95 / Math.min((b[1][0] - b[0][0]) / config.logicalSize, (b[1][1] - b[0][1]) / config.logicalSize);
var t = [(config.logicalSize - s * (b[1][0] + b[0][0])) / 2, (config.logicalSize - s * (b[1][1] + b[0][1])) / 2];
geoMapProjection
.scale(s)
.translate(t);
bgImg = svg.insert("g", '#topo-G');
bgImg = zoomPanContainer.insert("g", '#topo-G');
bgImg.attr('id', 'map').selectAll('path')
.data(topoData.features)
.enter()
......