Local GPX file support, first push towards multilayer support.

This commit is contained in:
Tom MacWright
2013-03-19 18:41:16 -04:00
parent d133f63a50
commit c94ec3b4a9
7 changed files with 239 additions and 16 deletions

View File

@@ -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 File

@@ -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 File

@@ -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;

View File

@@ -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 File

@@ -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 File

@@ -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,7 +22,7 @@ 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()
@@ -29,8 +30,8 @@ iD.Map = function(context) {
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;

120
js/lib/togeojson.js Normal file
View File

@@ -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;