Permalink
Browse files

Local GPX file support, first push towards multilayer support.

  • Loading branch information...
1 parent d133f63 commit c94ec3b4a99273657ca72df803ae52bc81682bc3 @tmcw tmcw committed Mar 19, 2013
Showing with 239 additions and 16 deletions.
  1. +8 −0 css/map.css
  2. +2 −0 index.html
  3. +3 −2 js/id/id.js
  4. +3 −2 js/id/renderer/background_source.js
  5. +78 −0 js/id/renderer/localgpx.js
  6. +25 −12 js/id/renderer/map.js
  7. +120 −0 js/lib/togeojson.js
View
@@ -886,6 +886,14 @@ text.point {
pointer-events: visibleStroke;
}
+/* GPX Paths */
+path.gpx {
+ stroke:#6AFF25;
+ stroke-width:2;
+ fill:transparent;
+ pointer-events: none;
+}
+
/* Modes */
.mode-draw-line .vertex.active,
View
@@ -28,6 +28,7 @@
<script src='js/lib/d3-compat.js'></script>
<script src='js/lib/bootstrap-tooltip.js'></script>
<script src='js/lib/rtree.js'></script>
+ <script src='js/lib/togeojson.js'></script>
<script src='js/id/id.js'></script>
<script src='js/id/util.js'></script>
@@ -41,6 +42,7 @@
<script src='js/id/renderer/background.js'></script>
<script src='js/id/renderer/background_source.js'></script>
+ <script src='js/id/renderer/localgpx.js'></script>
<script src='js/id/renderer/map.js'></script>
<script src="js/id/svg.js"></script>
View
@@ -91,7 +91,8 @@ window.iD = function () {
};
/* Map */
- context.background = function() { return map.background; };
+ context.layers = function() { return map.layers; };
+ context.background = function() { return map.layers[0]; };
context.surface = function() { return map.surface; };
context.projection = map.projection;
context.tail = map.tail;
@@ -124,7 +125,7 @@ window.iD = function () {
var q = iD.util.stringQs(location.hash.substring(1)), detected = false;
if (q.layer) {
- context.background()
+ context.layers()[0]
.source(_.find(backgroundSources, function(l) {
if (l.data.sourcetag === q.layer) {
detected = true;
@@ -2,7 +2,8 @@ iD.BackgroundSource = {};
// derive the url of a 'quadkey' style tile from a coordinate object
iD.BackgroundSource.template = function(data) {
- var generator = function(coord) {
+
+ function generator(coord) {
var u = '';
for (var zoom = coord[2]; zoom > 0; zoom--) {
var b = 0;
@@ -25,7 +26,7 @@ iD.BackgroundSource.template = function(data) {
var subdomains = r.split(':')[1].split(',');
return subdomains[coord[2] % subdomains.length];
});
- };
+ }
generator.data = data;
View
@@ -0,0 +1,78 @@
+iD.LocalGpx = function() {
+ var tileSize = 256,
+ projection,
+ gj = {},
+ size = [0, 0],
+ transformProp = iD.util.prefixCSSProperty('Transform'),
+ path = d3.geo.path().projection(projection),
+ source = d3.functor('');
+
+ function render(selection) {
+
+ path.projection(projection);
+
+ var surf = selection.selectAll('svg')
+ .data([gj]);
+
+ surf.exit().remove();
+
+ surf.enter()
+ .append('svg')
+ .style('position', 'absolute');
+
+ var paths = surf
+ .selectAll('path')
+ .data(function(d) { return [d]; });
+
+ paths
+ .enter()
+ .append('path')
+ .attr('class', 'gpx');
+
+ paths
+ .attr('d', path);
+ }
+
+ function toDom(x) {
+ return (new DOMParser()).parseFromString(x, 'text/xml');
+ }
+
+ render.projection = function(_) {
+ if (!arguments.length) return projection;
+ projection = _;
+ return render;
+ };
+
+ render.size = function(_) {
+ if (!arguments.length) return size;
+ size = _;
+ return render;
+ };
+
+ function over() {
+ d3.event.stopPropagation();
+ d3.event.preventDefault();
+ d3.event.dataTransfer.dropEffect = 'copy';
+ console.log('here');
+ }
+
+ d3.select('body')
+ .attr('dropzone', 'copy')
+ .on('drop.localgpx', function() {
+ d3.event.stopPropagation();
+ d3.event.preventDefault();
+ var f = d3.event.dataTransfer.files[0],
+ reader = new FileReader();
+
+ reader.onload = function(e) {
+ gj = toGeoJSON.gpx(toDom(e.target.result));
+ };
+
+ reader.readAsText(f);
+ })
+ .on('dragenter.localgpx', over)
+ .on('dragexit.localgpx', over)
+ .on('dragover.localgpx', over);
+
+ return render;
+};
View
@@ -11,8 +11,9 @@ iD.Map = function(context) {
dblclickEnabled = true,
transformStart,
minzoom = 0,
- background = iD.Background()
- .projection(projection),
+ layers = [
+ iD.Background().projection(projection),
+ iD.LocalGpx().projection(projection)],
transformProp = iD.util.prefixCSSProperty('Transform'),
points = iD.svg.Points(roundedProjection, context),
vertices = iD.svg.Vertices(roundedProjection, context),
@@ -21,16 +22,16 @@ iD.Map = function(context) {
midpoints = iD.svg.Midpoints(roundedProjection),
labels = iD.svg.Labels(roundedProjection, context),
tail = iD.ui.Tail(),
- surface, tilegroup;
+ surface, layergroup;
function map(selection) {
context.history()
.on('change.map', redraw);
selection.call(zoom);
- tilegroup = selection.append('div')
- .attr('id', 'tile-g');
+ layergroup = selection.append('div')
+ .attr('id', 'layers-g');
var supersurface = selection.append('div')
.style('position', 'absolute');
@@ -47,10 +48,9 @@ iD.Map = function(context) {
.attr('id', 'surface')
.call(iD.svg.Surface());
-
map.size(selection.size());
map.surface = surface;
- map.tilesurface = tilegroup;
+ map.layersurface = layergroup;
supersurface
.call(tail);
@@ -131,7 +131,7 @@ iD.Map = function(context) {
'scale(' + scale + ')' +
'translate(' + tX + 'px,' + tY + 'px) ';
- tilegroup.style(transformProp, transform);
+ layergroup.style(transformProp, transform);
surface.style(transformProp, transform);
queueRedraw();
@@ -142,7 +142,7 @@ iD.Map = function(context) {
var prop = surface.node().style[transformProp];
if (!prop || prop === 'none') return false;
surface.node().style[transformProp] = '';
- tilegroup.node().style[transformProp] = '';
+ layergroup.node().style[transformProp] = '';
return true;
}
@@ -165,7 +165,18 @@ iD.Map = function(context) {
}
if (!difference) {
- tilegroup.call(background);
+ var sel = layergroup
+ .selectAll('.tile-layer-group')
+ .data(layers);
+
+ sel.exit().remove();
+
+ sel.enter().append('div')
+ .attr('class', 'tile-layer-group');
+
+ sel.each(function(layer) {
+ d3.select(this).call(layer);
+ });
}
if (map.editable()) {
@@ -260,7 +271,9 @@ iD.Map = function(context) {
var center = map.center();
dimensions = _;
surface.size(dimensions);
- background.size(dimensions);
+ layers.map(function(l) {
+ l.size(dimensions);
+ });
projection.clipExtent([[0, 0], dimensions]);
setCenter(center);
return redraw();
@@ -371,7 +384,7 @@ iD.Map = function(context) {
return map;
};
- map.background = background;
+ map.layers = layers;
map.projection = projection;
map.redraw = redraw;
View
@@ -0,0 +1,120 @@
+toGeoJSON = (function() {
+ var removeSpace = (/\s*/g), trimSpace = (/^\s*|\s*$/g), splitSpace = (/\s+/);
+ function okhash(x) {
+ if (!x || !x.length) return 0;
+ for (var i = 0, h = 0; i < x.length; i++) {
+ h = ((h << 5) - h) + x.charCodeAt(i) | 0;
+ } return h;
+ }
+ function get(x, y) { return x.getElementsByTagName(y); }
+ function attr(x, y) { return x.getAttribute(y); }
+ function attrf(x, y) { return parseFloat(attr(x, y)); }
+ function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
+ function numarray(x) {
+ for (var j = 0, o = []; j < x.length; j++) o[j] = parseFloat(x[j]);
+ return o;
+ }
+ function nodeVal(x) { return x && x.firstChild && x.firstChild.nodeValue; }
+ function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
+ function coord(v) {
+ var coords = v.replace(trimSpace, '').split(splitSpace), o = [];
+ for (var i = 0; i < coords.length; i++) o.push(coord1(coords[i]));
+ return o;
+ }
+ function fc() { return { type: 'FeatureCollection', features: [] }; }
+ t = {
+ kml: function(doc, o) {
+ o = o || {};
+ var gj = fc(), styleIndex = {},
+ geotypes = ['Polygon', 'LineString', 'Point'],
+ placemarks = get(doc, 'Placemark'), styles = get(doc, 'Style');
+
+ if (o.styles) for (var k = 0; k < styles.length; k++) {
+ styleIndex['#' + styles[k].id] = okhash(styles[k].innerHTML).toString(16);
+ }
+ for (var j = 0; j < placemarks.length; j++) {
+ gj.features = gj.features.concat(getPlacemark(placemarks[j]));
+ }
+ function getGeometry(root) {
+ var geomNode, geomNodes, i, j, k, geoms = [];
+ if (get1(root, 'MultiGeometry')) return getGeometry(get1(root, 'MultiGeometry'));
+ for (i = 0; i < geotypes.length; i++) {
+ geomNodes = get(root, geotypes[i]);
+ if (geomNodes) {
+ for (j = 0; j < geomNodes.length; j++) {
+ geomNode = geomNodes[j];
+ if (geotypes[i] == 'Point') {
+ geoms.push({ type: 'Point',
+ coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
+ });
+ } else if (geotypes[i] == 'LineString') {
+ geoms.push({ type: 'LineString',
+ coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
+ });
+ } else if (geotypes[i] == 'Polygon') {
+ var rings = get(geomNode, 'LinearRing'), coords = [];
+ for (k = 0; k < rings.length; k++) {
+ coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
+ }
+ geoms.push({ type: 'Polygon', coordinates: coords });
+ }
+ }
+ }
+ }
+ return geoms;
+ }
+ function getPlacemark(root) {
+ var geoms = getGeometry(root), i, properties = {},
+ name = nodeVal(get1(root, 'name')),
+ styleUrl = nodeVal(get1(root, 'styleUrl')),
+ description = nodeVal(get1(root, 'description')),
+ extendedData = get1(root, 'ExtendedData');
+
+ if (!geoms.length) return false;
+ if (name) properties.name = name;
+ if (styleUrl && styleIndex[styleUrl]) {
+ properties.styleUrl = styleUrl;
+ properties.styleHash = styleIndex[styleUrl];
+ }
+ if (description) properties.description = description;
+ if (extendedData) {
+ var datas = get(extendedData, 'Data'),
+ simpleDatas = get(extendedData, 'SimpleData');
+
+ for (i = 0; i < datas.length; i++) {
+ properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
+ }
+ for (i = 0; i < simpleDatas.length; i++) {
+ properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
+ }
+ }
+ return [{ type: 'Feature', geometry: (geoms.length === 1) ? geoms[0] : {
+ type: 'GeometryCollection',
+ geometries: geoms }, properties: properties }];
+ }
+ return gj;
+ },
+ gpx: function(doc, o) {
+ var i, j, tracks = get(doc, 'trk'), track, pt, gj = fc();
+ for (i = 0; i < tracks.length; i++) {
+ track = tracks[i];
+ var name = nodeVal(get1(track, 'name'));
+ var pts = get(track, 'trkpt'), line = [];
+ for (j = 0; j < pts.length; j++) {
+ line.push([attrf(pts[j], 'lon'), attrf(pts[j], 'lat')]);
+ }
+ gj.features.push({
+ type: 'Feature',
+ properties: {
+ name: name || ''
+ },
+ geometry: { type: 'LineString', coordinates: line }
+ });
+ }
+ return gj;
+ }
+ };
+ return t;
+})();
+
+if (typeof module !== 'undefined') module.exports = toGeoJSON;

0 comments on commit c94ec3b

Please sign in to comment.