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
Showing
3 changed files
with
119 additions
and
27 deletions
... | @@ -23,7 +23,7 @@ | ... | @@ -23,7 +23,7 @@ |
23 | (function (onos) { | 23 | (function (onos) { |
24 | 'use strict'; | 24 | 'use strict'; |
25 | 25 | ||
26 | - function createDragBehavior(force, selectCb, atDragEnd) { | 26 | + function createDragBehavior(force, selectCb, atDragEnd, requireMeta) { |
27 | var draggedThreshold = d3.scale.linear() | 27 | var draggedThreshold = d3.scale.linear() |
28 | .domain([0, 0.1]) | 28 | .domain([0, 0.1]) |
29 | .range([5, 20]) | 29 | .range([5, 20]) |
... | @@ -51,29 +51,39 @@ | ... | @@ -51,29 +51,39 @@ |
51 | drag = d3.behavior.drag() | 51 | drag = d3.behavior.drag() |
52 | .origin(function(d) { return d; }) | 52 | .origin(function(d) { return d; }) |
53 | .on('dragstart', function(d) { | 53 | .on('dragstart', function(d) { |
54 | - d.oldX = d.x; | 54 | + if (requireMeta ^ !d3.event.sourceEvent.metaKey) { |
55 | - d.oldY = d.y; | 55 | + d3.event.sourceEvent.stopPropagation(); |
56 | - d.dragged = false; | 56 | + |
57 | - d.fixed |= 2; | 57 | + d.oldX = d.x; |
58 | + d.oldY = d.y; | ||
59 | + d.dragged = false; | ||
60 | + d.fixed |= 2; | ||
61 | + d.dragStarted = true; | ||
62 | + } | ||
58 | }) | 63 | }) |
59 | .on('drag', function(d) { | 64 | .on('drag', function(d) { |
60 | - d.px = d3.event.x; | 65 | + if (requireMeta ^ !d3.event.sourceEvent.metaKey) { |
61 | - d.py = d3.event.y; | 66 | + d.px = d3.event.x; |
62 | - if (dragged(d)) { | 67 | + d.py = d3.event.y; |
63 | - if (!force.alpha()) { | 68 | + if (dragged(d)) { |
64 | - force.alpha(.025); | 69 | + if (!force.alpha()) { |
70 | + force.alpha(.025); | ||
71 | + } | ||
65 | } | 72 | } |
66 | } | 73 | } |
67 | }) | 74 | }) |
68 | .on('dragend', function(d) { | 75 | .on('dragend', function(d) { |
69 | - if (!dragged(d)) { | 76 | + if (d.dragStarted) { |
70 | - // consider this the same as a 'click' (selection of node) | 77 | + d.dragStarted = false; |
71 | - selectCb(d, this); // TODO: set 'this' context instead of param | 78 | + if (!dragged(d)) { |
72 | - } | 79 | + // consider this the same as a 'click' (selection of node) |
73 | - d.fixed &= ~6; | 80 | + selectCb(d, this); // TODO: set 'this' context instead of param |
81 | + } | ||
82 | + d.fixed &= ~6; | ||
74 | 83 | ||
75 | - // hook at the end of a drag gesture | 84 | + // hook at the end of a drag gesture |
76 | - atDragEnd(d, this); // TODO: set 'this' context instead of param | 85 | + atDragEnd(d, this); // TODO: set 'this' context instead of param |
86 | + } | ||
77 | }); | 87 | }); |
78 | 88 | ||
79 | return drag; | 89 | return drag; | ... | ... |
... | @@ -5,11 +5,57 @@ npm install -g topojson | ... | @@ -5,11 +5,57 @@ npm install -g topojson |
5 | 5 | ||
6 | To generate continental US map: | 6 | To generate continental US map: |
7 | 7 | ||
8 | -$ wget 'http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/50m/cultural/ne_50m_admin_1_states_provinces_lakes.zip' | 8 | + $ wget 'http://www.naturalearthdata.com/download/50m/cultural/ne_50m_admin_1_states_provinces_lakes.zip' |
9 | -$ unzip ne_50m_admin_1_states_provinces_lakes.zip | 9 | + $ unzip ne_50m_admin_1_states_provinces_lakes.zip |
10 | -$ ogr2ogr -f GeoJSON -where "sr_adm0_a3 IN ('USA')" states.json ne_50m_admin_1_states_provinces_lakes.shp | 10 | + $ ogr2ogr -f GeoJSON -where "sr_adm0_a3 IN ('USA')" states.json ne_50m_admin_1_states_provinces_lakes.shp |
11 | 11 | ||
12 | edit states.json to remove data for Hawaii and Alaska | 12 | edit states.json to remove data for Hawaii and Alaska |
13 | 13 | ||
14 | -$ topojson states.json > topology.json | 14 | + $ topojson states.json > topology.json |
15 | + | ||
16 | + | ||
17 | +The .shp file above is incomplete (USA and part of Candada.) | ||
18 | +So it may be that each region requires a bit of research to generate. | ||
19 | +Ideally a source for public domain shp files can be found that covers all geographic regions. | ||
20 | + | ||
21 | + | ||
22 | +For Canada: | ||
23 | + | ||
24 | + # wget 'http://www12.statcan.gc.ca/census-recensement/2011/geo/bound-limit/files-fichiers/gpr_000b11a_e.zip' | ||
25 | + # unzip gpr_000b11a_e.zip | ||
26 | + # ogr2ogr -f "GeoJSON" -s_srs EPSG:21781 -t_srs EPSG:4326 canada.json gpr_000b11a_e.shp | ||
27 | + # topojson --id-property CFSAUID -p name=PRNAME -p name canada.json > topology.json | ||
28 | + | ||
29 | + | ||
30 | +This produces a very large (5MB) file and draws very slowly in Chrome. | ||
31 | +So some additional processing is required to simplify the geometry. (It is not checked in.) | ||
32 | + | ||
33 | +Also, the specification of object structure within the geojson is unclear. | ||
34 | +In the US map the geojson structure is | ||
35 | + | ||
36 | + json.objects.states | ||
37 | + | ||
38 | +but in the Canadian data it's | ||
39 | + | ||
40 | + json.objects.canada | ||
41 | + | ||
42 | + | ||
43 | +Lastly, the projection that is used may be tailored to the region. | ||
44 | +The preferred projection for the US is "albers" and d3 provides a "albersUSA" which can be used to | ||
45 | + project hawaii and alaska as well | ||
46 | + | ||
47 | +For Canada, apparantly a "Lambert" projection (called conicConformal in d3) is preferred | ||
48 | + | ||
49 | +see: | ||
50 | + https://github.com/mbostock/d3/wiki/Geo-Projections | ||
51 | + http://www.statcan.gc.ca/pub/92-195-x/2011001/other-autre/mapproj-projcarte/m-c-eng.htm | ||
52 | + | ||
53 | + | ||
54 | +Summary: | ||
55 | +- some additional work is required to fully generalize maps functionality. | ||
56 | +- it may be worthwhile for ON.LAB to provide the topo files for key regions since producing these | ||
57 | + files is non-trivial | ||
58 | + | ||
59 | + | ||
60 | + | ||
15 | 61 | ... | ... |
... | @@ -128,6 +128,7 @@ | ... | @@ -128,6 +128,7 @@ |
128 | L: cycleLabels, | 128 | L: cycleLabels, |
129 | P: togglePorts, | 129 | P: togglePorts, |
130 | U: unpin, | 130 | U: unpin, |
131 | + R: resetZoomPan, | ||
131 | 132 | ||
132 | W: requestTraffic, // bag of selections | 133 | W: requestTraffic, // bag of selections |
133 | X: cancelTraffic, | 134 | X: cancelTraffic, |
... | @@ -168,6 +169,7 @@ | ... | @@ -168,6 +169,7 @@ |
168 | 169 | ||
169 | // D3 selections | 170 | // D3 selections |
170 | var svg, | 171 | var svg, |
172 | + zoomPanContainer, | ||
171 | bgImg, | 173 | bgImg, |
172 | topoG, | 174 | topoG, |
173 | nodeG, | 175 | nodeG, |
... | @@ -179,6 +181,9 @@ | ... | @@ -179,6 +181,9 @@ |
179 | // the projection for the map background | 181 | // the projection for the map background |
180 | var geoMapProjection; | 182 | var geoMapProjection; |
181 | 183 | ||
184 | + // the zoom function | ||
185 | + var zoom; | ||
186 | + | ||
182 | // ============================== | 187 | // ============================== |
183 | // For Debugging / Development | 188 | // For Debugging / Development |
184 | 189 | ||
... | @@ -1358,6 +1363,33 @@ | ... | @@ -1358,6 +1363,33 @@ |
1358 | }); | 1363 | }); |
1359 | } | 1364 | } |
1360 | 1365 | ||
1366 | + function zoomPan(scale, translate) { | ||
1367 | + zoomPanContainer.attr("transform", "translate(" + translate + ")scale(" + scale + ")"); | ||
1368 | + // keep the map lines constant width while zooming | ||
1369 | + bgImg.style("stroke-width", 2.0 / scale + "px"); | ||
1370 | + } | ||
1371 | + | ||
1372 | + function resetZoomPan() { | ||
1373 | + zoomPan(1, [0,0]); | ||
1374 | + zoom.scale(1).translate([0,0]); | ||
1375 | + } | ||
1376 | + | ||
1377 | + function setupZoomPan() { | ||
1378 | + function zoomed() { | ||
1379 | + if (!d3.event.sourceEvent.metaKey) { | ||
1380 | + zoomPan(d3.event.scale, d3.event.translate); | ||
1381 | + } | ||
1382 | + } | ||
1383 | + | ||
1384 | + zoom = d3.behavior.zoom() | ||
1385 | + .translate([0, 0]) | ||
1386 | + .scale(1) | ||
1387 | + .scaleExtent([1, 8]) | ||
1388 | + .on("zoom", zoomed); | ||
1389 | + | ||
1390 | + svg.call(zoom); | ||
1391 | + } | ||
1392 | + | ||
1361 | // ============================== | 1393 | // ============================== |
1362 | // Test harness code | 1394 | // Test harness code |
1363 | 1395 | ||
... | @@ -1438,11 +1470,15 @@ | ... | @@ -1438,11 +1470,15 @@ |
1438 | svg = view.$div.append('svg').attr('viewBox', viewBox); | 1470 | svg = view.$div.append('svg').attr('viewBox', viewBox); |
1439 | setSize(svg, view); | 1471 | setSize(svg, view); |
1440 | 1472 | ||
1473 | + zoomPanContainer = svg.append('g').attr('id', 'zoomPanContainer'); | ||
1474 | + | ||
1475 | + setupZoomPan(); | ||
1476 | + | ||
1441 | // add blue glow filter to svg layer | 1477 | // add blue glow filter to svg layer |
1442 | - d3u.appendGlow(svg); | 1478 | + d3u.appendGlow(zoomPanContainer); |
1443 | 1479 | ||
1444 | // group for the topology | 1480 | // group for the topology |
1445 | - topoG = svg.append('g') | 1481 | + topoG = zoomPanContainer.append('g') |
1446 | .attr('id', 'topo-G') | 1482 | .attr('id', 'topo-G') |
1447 | .attr('transform', fcfg.translate()); | 1483 | .attr('transform', fcfg.translate()); |
1448 | 1484 | ||
... | @@ -1501,7 +1537,7 @@ | ... | @@ -1501,7 +1537,7 @@ |
1501 | .linkStrength(lstrg) | 1537 | .linkStrength(lstrg) |
1502 | .on('tick', tick); | 1538 | .on('tick', tick); |
1503 | 1539 | ||
1504 | - network.drag = d3u.createDragBehavior(network.force, selectCb, atDragEnd); | 1540 | + network.drag = d3u.createDragBehavior(network.force, selectCb, atDragEnd, true); // true=require meta |
1505 | 1541 | ||
1506 | // create mask layer for when we lose connection to server. | 1542 | // create mask layer for when we lose connection to server. |
1507 | mask = view.$div.append('div').attr('id','topo-mask'); | 1543 | mask = view.$div.append('div').attr('id','topo-mask'); |
... | @@ -1593,15 +1629,15 @@ | ... | @@ -1593,15 +1629,15 @@ |
1593 | 1629 | ||
1594 | // [[x1,y1],[x2,y2]] | 1630 | // [[x1,y1],[x2,y2]] |
1595 | var b = path.bounds(topoData); | 1631 | var b = path.bounds(topoData); |
1596 | - // TODO: why 1.75? | 1632 | + // size map to 95% of minimum dimension to fill space |
1597 | - var s = 1.75 / Math.max((b[1][0] - b[0][0]) / config.logicalSize, (b[1][1] - b[0][1]) / config.logicalSize); | 1633 | + var s = .95 / Math.min((b[1][0] - b[0][0]) / config.logicalSize, (b[1][1] - b[0][1]) / config.logicalSize); |
1598 | var t = [(config.logicalSize - s * (b[1][0] + b[0][0])) / 2, (config.logicalSize - s * (b[1][1] + b[0][1])) / 2]; | 1634 | var t = [(config.logicalSize - s * (b[1][0] + b[0][0])) / 2, (config.logicalSize - s * (b[1][1] + b[0][1])) / 2]; |
1599 | 1635 | ||
1600 | geoMapProjection | 1636 | geoMapProjection |
1601 | .scale(s) | 1637 | .scale(s) |
1602 | .translate(t); | 1638 | .translate(t); |
1603 | 1639 | ||
1604 | - bgImg = svg.insert("g", '#topo-G'); | 1640 | + bgImg = zoomPanContainer.insert("g", '#topo-G'); |
1605 | bgImg.attr('id', 'map').selectAll('path') | 1641 | bgImg.attr('id', 'map').selectAll('path') |
1606 | .data(topoData.features) | 1642 | .data(topoData.features) |
1607 | .enter() | 1643 | .enter() | ... | ... |
-
Please register or login to post a comment