Merge pull request #579 from nylonee/master
Plugin to add a KML, GPX or GeoJSON overlay over the map
This commit is contained in:
commit
6833c02ff4
@ -10,5 +10,8 @@ This project is licensed under the permissive [ISC license](http://www.isc.org/d
|
|||||||
- [taphold.js by Rich Adams; unknown](https://github.com/richadams/jquery-taphold)
|
- [taphold.js by Rich Adams; unknown](https://github.com/richadams/jquery-taphold)
|
||||||
- [L.Control.Pan.js by Kartena AB; same as Leaflet](https://github.com/kartena/Leaflet.Pancontrol)
|
- [L.Control.Pan.js by Kartena AB; same as Leaflet](https://github.com/kartena/Leaflet.Pancontrol)
|
||||||
- [L.Control.Zoomslider.js by Kartena AB; same as Leaflet](https://github.com/kartena/Leaflet.zoomslider)
|
- [L.Control.Zoomslider.js by Kartena AB; same as Leaflet](https://github.com/kartena/Leaflet.zoomslider)
|
||||||
|
- [KML.js by shramov; same as Leaflet](https://github.com/shramov/leaflet-plugins)
|
||||||
|
- [leaflet.filelayer.js by shramov; same as Leaflet](https://github.com/shramov/leaflet-plugins)
|
||||||
|
- [togeojson.js by shramov; same as Leaflet](https://github.com/shramov/leaflet-plugins)
|
||||||
- StackOverflow-CopyPasta is attributed in the source; [CC-Wiki](https://creativecommons.org/licenses/by-sa/3.0/)
|
- StackOverflow-CopyPasta is attributed in the source; [CC-Wiki](https://creativecommons.org/licenses/by-sa/3.0/)
|
||||||
- all Ingress/Niantic related stuff obviously remains non-free and is still copyrighted by Niantic/Google
|
- all Ingress/Niantic related stuff obviously remains non-free and is still copyrighted by Niantic/Google
|
||||||
|
336
external/KML.js
vendored
Executable file
336
external/KML.js
vendored
Executable file
@ -0,0 +1,336 @@
|
|||||||
|
/*global L: true */
|
||||||
|
|
||||||
|
L.KML = L.FeatureGroup.extend({
|
||||||
|
options: {
|
||||||
|
async: true
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function(kml, options) {
|
||||||
|
L.Util.setOptions(this, options);
|
||||||
|
this._kml = kml;
|
||||||
|
this._layers = {};
|
||||||
|
|
||||||
|
if (kml) {
|
||||||
|
this.addKML(kml, options, this.options.async);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
loadXML: function(url, cb, options, async) {
|
||||||
|
if (async == undefined) async = this.options.async;
|
||||||
|
if (options == undefined) options = this.options;
|
||||||
|
|
||||||
|
var req = new window.XMLHttpRequest();
|
||||||
|
req.open('GET', url, async);
|
||||||
|
try {
|
||||||
|
req.overrideMimeType('text/xml'); // unsupported by IE
|
||||||
|
} catch(e) {}
|
||||||
|
req.onreadystatechange = function() {
|
||||||
|
if (req.readyState != 4) return;
|
||||||
|
if(req.status == 200) cb(req.responseXML, options);
|
||||||
|
};
|
||||||
|
req.send(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
addKML: function(url, options, async) {
|
||||||
|
var _this = this;
|
||||||
|
var cb = function(gpx, options) { _this._addKML(gpx, options) };
|
||||||
|
this.loadXML(url, cb, options, async);
|
||||||
|
},
|
||||||
|
|
||||||
|
_addKML: function(xml, options) {
|
||||||
|
var layers = L.KML.parseKML(xml);
|
||||||
|
if (!layers || !layers.length) return;
|
||||||
|
for (var i = 0; i < layers.length; i++)
|
||||||
|
{
|
||||||
|
this.fire('addlayer', {
|
||||||
|
layer: layers[i]
|
||||||
|
});
|
||||||
|
this.addLayer(layers[i]);
|
||||||
|
}
|
||||||
|
this.latLngs = L.KML.getLatLngs(xml);
|
||||||
|
this.fire("loaded");
|
||||||
|
},
|
||||||
|
|
||||||
|
latLngs: []
|
||||||
|
});
|
||||||
|
|
||||||
|
L.Util.extend(L.KML, {
|
||||||
|
|
||||||
|
parseKML: function (xml) {
|
||||||
|
var style = this.parseStyle(xml);
|
||||||
|
var el = xml.getElementsByTagName("Folder");
|
||||||
|
var layers = [], l;
|
||||||
|
for (var i = 0; i < el.length; i++) {
|
||||||
|
if (!this._check_folder(el[i])) { continue; }
|
||||||
|
l = this.parseFolder(el[i], style);
|
||||||
|
if (l) { layers.push(l); }
|
||||||
|
}
|
||||||
|
el = xml.getElementsByTagName('Placemark');
|
||||||
|
for (var j = 0; j < el.length; j++) {
|
||||||
|
if (!this._check_folder(el[j])) { continue; }
|
||||||
|
l = this.parsePlacemark(el[j], xml, style);
|
||||||
|
if (l) { layers.push(l); }
|
||||||
|
}
|
||||||
|
return layers;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Return false if e's first parent Folder is not [folder]
|
||||||
|
// - returns true if no parent Folders
|
||||||
|
_check_folder: function (e, folder) {
|
||||||
|
e = e.parentElement;
|
||||||
|
while (e && e.tagName !== "Folder")
|
||||||
|
{
|
||||||
|
e = e.parentElement;
|
||||||
|
}
|
||||||
|
return !e || e === folder;
|
||||||
|
},
|
||||||
|
|
||||||
|
parseStyle: function (xml) {
|
||||||
|
var style = {};
|
||||||
|
var sl = xml.getElementsByTagName("Style");
|
||||||
|
|
||||||
|
//for (var i = 0; i < sl.length; i++) {
|
||||||
|
var attributes = {color: true, width: true, Icon: true, href: true,
|
||||||
|
hotSpot: true};
|
||||||
|
|
||||||
|
function _parse(xml) {
|
||||||
|
var options = {};
|
||||||
|
for (var i = 0; i < xml.childNodes.length; i++) {
|
||||||
|
var e = xml.childNodes[i];
|
||||||
|
var key = e.tagName;
|
||||||
|
if (!attributes[key]) { continue; }
|
||||||
|
if (key === 'hotSpot')
|
||||||
|
{
|
||||||
|
for (var j = 0; j < e.attributes.length; j++) {
|
||||||
|
options[e.attributes[j].name] = e.attributes[j].nodeValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var value = e.childNodes[0].nodeValue;
|
||||||
|
if (key === 'color') {
|
||||||
|
options.opacity = parseInt(value.substring(0, 2), 16) / 255.0;
|
||||||
|
options.color = "#" + value.substring(6, 8) + value.substring(4, 6) + value.substring(2, 4);
|
||||||
|
} else if (key === 'width') {
|
||||||
|
options.weight = value;
|
||||||
|
} else if (key === 'Icon') {
|
||||||
|
ioptions = _parse(e);
|
||||||
|
if (ioptions.href) { options.href = ioptions.href; }
|
||||||
|
} else if (key === 'href') {
|
||||||
|
options.href = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < sl.length; i++) {
|
||||||
|
var e = sl[i], el;
|
||||||
|
var options = {}, poptions = {}, ioptions = {};
|
||||||
|
el = e.getElementsByTagName("LineStyle");
|
||||||
|
if (el && el[0]) { options = _parse(el[0]); }
|
||||||
|
el = e.getElementsByTagName("PolyStyle");
|
||||||
|
if (el && el[0]) { poptions = _parse(el[0]); }
|
||||||
|
if (poptions.color) { options.fillColor = poptions.color; }
|
||||||
|
if (poptions.opacity) { options.fillOpacity = poptions.opacity; }
|
||||||
|
el = e.getElementsByTagName("IconStyle");
|
||||||
|
if (el && el[0]) { ioptions = _parse(el[0]); }
|
||||||
|
if (ioptions.href) {
|
||||||
|
// save anchor info until the image is loaded
|
||||||
|
options.icon = new L.KMLIcon({
|
||||||
|
iconUrl: ioptions.href,
|
||||||
|
shadowUrl: null,
|
||||||
|
iconAnchorRef: {x: ioptions.x, y: ioptions.y},
|
||||||
|
iconAnchorType: {x: ioptions.xunits, y: ioptions.yunits}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
style['#' + e.getAttribute('id')] = options;
|
||||||
|
}
|
||||||
|
return style;
|
||||||
|
},
|
||||||
|
|
||||||
|
parseFolder: function (xml, style) {
|
||||||
|
var el, layers = [], l;
|
||||||
|
el = xml.getElementsByTagName('Folder');
|
||||||
|
for (var i = 0; i < el.length; i++) {
|
||||||
|
if (!this._check_folder(el[i], xml)) { continue; }
|
||||||
|
l = this.parseFolder(el[i], style);
|
||||||
|
if (l) { layers.push(l); }
|
||||||
|
}
|
||||||
|
el = xml.getElementsByTagName('Placemark');
|
||||||
|
for (var j = 0; j < el.length; j++) {
|
||||||
|
if (!this._check_folder(el[j], xml)) { continue; }
|
||||||
|
l = this.parsePlacemark(el[j], xml, style);
|
||||||
|
if (l) { layers.push(l); }
|
||||||
|
}
|
||||||
|
if (!layers.length) { return; }
|
||||||
|
if (layers.length === 1) { return layers[0]; }
|
||||||
|
return new L.FeatureGroup(layers);
|
||||||
|
},
|
||||||
|
|
||||||
|
parsePlacemark: function (place, xml, style) {
|
||||||
|
var i, j, el, options = {};
|
||||||
|
el = place.getElementsByTagName('styleUrl');
|
||||||
|
for (i = 0; i < el.length; i++) {
|
||||||
|
var url = el[i].childNodes[0].nodeValue;
|
||||||
|
for (var a in style[url])
|
||||||
|
{
|
||||||
|
// for jshint
|
||||||
|
if (true)
|
||||||
|
{
|
||||||
|
options[a] = style[url][a];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var layers = [];
|
||||||
|
|
||||||
|
var parse = ['LineString', 'Polygon', 'Point'];
|
||||||
|
for (j in parse) {
|
||||||
|
// for jshint
|
||||||
|
if (true)
|
||||||
|
{
|
||||||
|
var tag = parse[j];
|
||||||
|
el = place.getElementsByTagName(tag);
|
||||||
|
for (i = 0; i < el.length; i++) {
|
||||||
|
var l = this["parse" + tag](el[i], xml, options);
|
||||||
|
if (l) { layers.push(l); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!layers.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var layer = layers[0];
|
||||||
|
if (layers.length > 1) {
|
||||||
|
layer = new L.FeatureGroup(layers);
|
||||||
|
}
|
||||||
|
|
||||||
|
var name, descr = "";
|
||||||
|
el = place.getElementsByTagName('name');
|
||||||
|
if (el.length) {
|
||||||
|
name = el[0].childNodes[0].nodeValue;
|
||||||
|
}
|
||||||
|
el = place.getElementsByTagName('description');
|
||||||
|
for (i = 0; i < el.length; i++) {
|
||||||
|
for (j = 0; j < el[i].childNodes.length; j++) {
|
||||||
|
descr = descr + el[i].childNodes[j].nodeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
layer.bindPopup("<h2>" + name + "</h2>" + descr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return layer;
|
||||||
|
},
|
||||||
|
|
||||||
|
parseCoords: function (xml) {
|
||||||
|
var el = xml.getElementsByTagName('coordinates');
|
||||||
|
return this._read_coords(el[0]);
|
||||||
|
},
|
||||||
|
|
||||||
|
parseLineString: function (line, xml, options) {
|
||||||
|
var coords = this.parseCoords(line);
|
||||||
|
if (!coords.length) { return; }
|
||||||
|
return new L.Polyline(coords, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
parsePoint: function (line, xml, options) {
|
||||||
|
var el = line.getElementsByTagName('coordinates');
|
||||||
|
if (!el.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var ll = el[0].childNodes[0].nodeValue.split(',');
|
||||||
|
return new L.KMLMarker(new L.LatLng(ll[1], ll[0]), options);
|
||||||
|
},
|
||||||
|
|
||||||
|
parsePolygon: function (line, xml, options) {
|
||||||
|
var el, polys = [], inner = [], i, coords;
|
||||||
|
el = line.getElementsByTagName('outerBoundaryIs');
|
||||||
|
for (i = 0; i < el.length; i++) {
|
||||||
|
coords = this.parseCoords(el[i]);
|
||||||
|
if (coords) {
|
||||||
|
polys.push(coords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
el = line.getElementsByTagName('innerBoundaryIs');
|
||||||
|
for (i = 0; i < el.length; i++) {
|
||||||
|
coords = this.parseCoords(el[i]);
|
||||||
|
if (coords) {
|
||||||
|
inner.push(coords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!polys.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (options.fillColor) {
|
||||||
|
options.fill = true;
|
||||||
|
}
|
||||||
|
if (polys.length === 1) {
|
||||||
|
return new L.Polygon(polys.concat(inner), options);
|
||||||
|
}
|
||||||
|
return new L.MultiPolygon(polys, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
getLatLngs: function (xml) {
|
||||||
|
var el = xml.getElementsByTagName('coordinates');
|
||||||
|
var coords = [];
|
||||||
|
for (var j = 0; j < el.length; j++) {
|
||||||
|
// text might span many childnodes
|
||||||
|
coords = coords.concat(this._read_coords(el[j]));
|
||||||
|
}
|
||||||
|
return coords;
|
||||||
|
},
|
||||||
|
|
||||||
|
_read_coords: function (el) {
|
||||||
|
var text = "", coords = [], i;
|
||||||
|
for (i = 0; i < el.childNodes.length; i++) {
|
||||||
|
text = text + el.childNodes[i].nodeValue;
|
||||||
|
}
|
||||||
|
text = text.split(/[\s\n]+/);
|
||||||
|
for (i = 0; i < text.length; i++) {
|
||||||
|
var ll = text[i].split(',');
|
||||||
|
if (ll.length < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
coords.push(new L.LatLng(ll[1], ll[0]));
|
||||||
|
}
|
||||||
|
return coords;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
L.KMLIcon = L.Icon.extend({
|
||||||
|
|
||||||
|
createIcon: function () {
|
||||||
|
var img = this._createIcon('icon');
|
||||||
|
img.onload = function () {
|
||||||
|
var i = new Image();
|
||||||
|
i.src = this.src;
|
||||||
|
this.style.width = i.width + 'px';
|
||||||
|
this.style.height = i.height + 'px';
|
||||||
|
|
||||||
|
if (this.anchorType.x === 'UNITS_FRACTION' || this.anchorType.x === 'fraction') {
|
||||||
|
img.style.marginLeft = (-this.anchor.x * i.width) + 'px';
|
||||||
|
}
|
||||||
|
if (this.anchorType.y === 'UNITS_FRACTION' || this.anchorType.x === 'fraction') {
|
||||||
|
img.style.marginTop = (-(1 - this.anchor.y) * i.height) + 'px';
|
||||||
|
}
|
||||||
|
this.style.display = "";
|
||||||
|
};
|
||||||
|
return img;
|
||||||
|
},
|
||||||
|
|
||||||
|
_setIconStyles: function (img, name) {
|
||||||
|
L.Icon.prototype._setIconStyles.apply(this, [img, name])
|
||||||
|
// save anchor information to the image
|
||||||
|
img.anchor = this.options.iconAnchorRef;
|
||||||
|
img.anchorType = this.options.iconAnchorType;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
L.KMLMarker = L.Marker.extend({
|
||||||
|
options: {
|
||||||
|
icon: new L.KMLIcon.Default()
|
||||||
|
}
|
||||||
|
});
|
168
external/leaflet.filelayer.js
vendored
Executable file
168
external/leaflet.filelayer.js
vendored
Executable file
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* Load files *locally* (GeoJSON, KML, GPX) into the map
|
||||||
|
* using the HTML5 File API.
|
||||||
|
*
|
||||||
|
* Requires Pavel Shramov's GPX.js
|
||||||
|
* https://github.com/shramov/leaflet-plugins/blob/d74d67/layer/vector/GPX.js
|
||||||
|
*/
|
||||||
|
var FileLoader = L.Class.extend({
|
||||||
|
includes: L.Mixin.Events,
|
||||||
|
options: {
|
||||||
|
layerOptions: {}
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function (map, options) {
|
||||||
|
this._map = map;
|
||||||
|
L.Util.setOptions(this, options);
|
||||||
|
|
||||||
|
this._parsers = {
|
||||||
|
'geojson': this._loadGeoJSON,
|
||||||
|
'gpx': this._convertToGeoJSON,
|
||||||
|
'kml': this._convertToGeoJSON
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
load: function (file /* File */) {
|
||||||
|
// Check file extension
|
||||||
|
var ext = file.name.split('.').pop(),
|
||||||
|
parser = this._parsers[ext];
|
||||||
|
if (!parser) {
|
||||||
|
window.alert("Unsupported file type " + file.type + '(' + ext + ')');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Read selected file using HTML5 File API
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = L.Util.bind(function (e) {
|
||||||
|
this.fire('data:loading', {filename: file.name, format: ext});
|
||||||
|
var layer = parser.call(this, e.target.result, ext);
|
||||||
|
this.fire('data:loaded', {layer: layer, filename: file.name, format: ext});
|
||||||
|
}, this);
|
||||||
|
reader.readAsText(file);
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadGeoJSON: function (content) {
|
||||||
|
if (typeof content == 'string') {
|
||||||
|
content = JSON.parse(content);
|
||||||
|
}
|
||||||
|
return L.geoJson(content, this.options.layerOptions).addTo(this._map);
|
||||||
|
},
|
||||||
|
|
||||||
|
_convertToGeoJSON: function (content, format) {
|
||||||
|
// Format is either 'gpx' or 'kml'
|
||||||
|
if (typeof content == 'string') {
|
||||||
|
content = ( new window.DOMParser() ).parseFromString(content, "text/xml");
|
||||||
|
}
|
||||||
|
var geojson = toGeoJSON[format](content);
|
||||||
|
return this._loadGeoJSON(geojson);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
L.Control.FileLayerLoad = L.Control.extend({
|
||||||
|
statics: {
|
||||||
|
TITLE: 'Load local file (GPX, KML, GeoJSON)',
|
||||||
|
LABEL: '⌅'
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
position: 'topleft',
|
||||||
|
fitBounds: true,
|
||||||
|
layerOptions: {}
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function (options) {
|
||||||
|
L.Util.setOptions(this, options);
|
||||||
|
this.loader = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
onAdd: function (map) {
|
||||||
|
this.loader = new FileLoader(map, {layerOptions: this.options.layerOptions});
|
||||||
|
|
||||||
|
this.loader.on('data:loaded', function (e) {
|
||||||
|
// Fit bounds after loading
|
||||||
|
if (this.options.fitBounds) {
|
||||||
|
window.setTimeout(function () {
|
||||||
|
map.fitBounds(e.layer.getBounds()).zoomOut();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
// Initialize Drag-and-drop
|
||||||
|
this._initDragAndDrop(map);
|
||||||
|
|
||||||
|
// Initialize map control
|
||||||
|
return this._initContainer();
|
||||||
|
},
|
||||||
|
|
||||||
|
_initDragAndDrop: function (map) {
|
||||||
|
var fileLoader = this.loader,
|
||||||
|
dropbox = map._container;
|
||||||
|
|
||||||
|
var callbacks = {
|
||||||
|
dragenter: function () {
|
||||||
|
map.scrollWheelZoom.disable();
|
||||||
|
},
|
||||||
|
dragleave: function () {
|
||||||
|
map.scrollWheelZoom.enable();
|
||||||
|
},
|
||||||
|
dragover: function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
},
|
||||||
|
drop: function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var files = Array.prototype.slice.apply(e.dataTransfer.files),
|
||||||
|
i = files.length;
|
||||||
|
setTimeout(function(){
|
||||||
|
fileLoader.load(files.shift());
|
||||||
|
if (files.length > 0) {
|
||||||
|
setTimeout(arguments.callee, 25);
|
||||||
|
}
|
||||||
|
}, 25);
|
||||||
|
map.scrollWheelZoom.enable();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (var name in callbacks)
|
||||||
|
dropbox.addEventListener(name, callbacks[name], false);
|
||||||
|
},
|
||||||
|
|
||||||
|
_initContainer: function () {
|
||||||
|
// Create an invisible file input
|
||||||
|
var fileInput = L.DomUtil.create('input', 'hidden', container);
|
||||||
|
fileInput.type = 'file';
|
||||||
|
fileInput.accept = '.gpx,.kml,.geojson';
|
||||||
|
fileInput.style.display = 'none';
|
||||||
|
// Load on file change
|
||||||
|
var fileLoader = this.loader;
|
||||||
|
fileInput.addEventListener("change", function (e) {
|
||||||
|
fileLoader.load(this.files[0]);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
// Create a button, and bind click on hidden file input
|
||||||
|
var zoomName = 'leaflet-control-filelayer leaflet-control-zoom',
|
||||||
|
barName = 'leaflet-bar',
|
||||||
|
partName = barName + '-part',
|
||||||
|
container = L.DomUtil.create('div', zoomName + ' ' + barName);
|
||||||
|
var link = L.DomUtil.create('a', zoomName + '-in ' + partName, container);
|
||||||
|
link.innerHTML = L.Control.FileLayerLoad.LABEL;
|
||||||
|
link.href = '#';
|
||||||
|
link.title = L.Control.FileLayerLoad.TITLE;
|
||||||
|
|
||||||
|
var stop = L.DomEvent.stopPropagation;
|
||||||
|
L.DomEvent
|
||||||
|
.on(link, 'click', stop)
|
||||||
|
.on(link, 'mousedown', stop)
|
||||||
|
.on(link, 'dblclick', stop)
|
||||||
|
.on(link, 'click', L.DomEvent.preventDefault)
|
||||||
|
.on(link, 'click', function (e) {
|
||||||
|
fileInput.click();
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
L.Control.fileLayerLoad = function (options) {
|
||||||
|
return new L.Control.FileLayerLoad(options);
|
||||||
|
};
|
224
external/togeojson.js
vendored
Executable file
224
external/togeojson.js
vendored
Executable file
@ -0,0 +1,224 @@
|
|||||||
|
toGeoJSON = (function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var removeSpace = (/\s*/g),
|
||||||
|
trimSpace = (/^\s*|\s*$/g),
|
||||||
|
splitSpace = (/\s+/);
|
||||||
|
// generate a short, numeric hash of a string
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
// all Y children of X
|
||||||
|
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)); }
|
||||||
|
// one Y child of X, if any, otherwise null
|
||||||
|
function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
|
||||||
|
function norm(el) { if (el.normalize) { el.normalize(); } return el; }
|
||||||
|
// cast array x into numbers
|
||||||
|
function numarray(x) {
|
||||||
|
for (var j = 0, o = []; j < x.length; j++) o[j] = parseFloat(x[j]);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
function clean(x) {
|
||||||
|
var o = {};
|
||||||
|
for (var i in x) if (x[i]) o[i] = x[i];
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
// get the content of a text node, if any
|
||||||
|
function nodeVal(x) { if (x) {norm(x);} return x && x.firstChild && x.firstChild.nodeValue; }
|
||||||
|
// get one coordinate from a coordinate array, if any
|
||||||
|
function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
|
||||||
|
// get all coordinates from a coordinate array as [[],[]]
|
||||||
|
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 coordPair(x) { return [attrf(x, 'lon'), attrf(x, 'lat')]; }
|
||||||
|
|
||||||
|
// create a new feature collection parent object
|
||||||
|
function fc() {
|
||||||
|
return {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var serializer;
|
||||||
|
if (typeof XMLSerializer !== 'undefined') {
|
||||||
|
serializer = new XMLSerializer();
|
||||||
|
} else if (typeof require !== 'undefined') {
|
||||||
|
serializer = new (require('xmldom').XMLSerializer)();
|
||||||
|
}
|
||||||
|
function xml2str(str) { return serializer.serializeToString(str); }
|
||||||
|
|
||||||
|
var t = {
|
||||||
|
kml: function(doc, o) {
|
||||||
|
o = o || {};
|
||||||
|
|
||||||
|
var gj = fc(),
|
||||||
|
// styleindex keeps track of hashed styles in order to match features
|
||||||
|
styleIndex = {},
|
||||||
|
// atomic geospatial types supported by KML - MultiGeometry is
|
||||||
|
// handled separately
|
||||||
|
geotypes = ['Polygon', 'LineString', 'Point', 'Track'],
|
||||||
|
// all root placemarks in the file
|
||||||
|
placemarks = get(doc, 'Placemark'),
|
||||||
|
styles = get(doc, 'Style');
|
||||||
|
|
||||||
|
for (var k = 0; k < styles.length; k++) {
|
||||||
|
styleIndex['#' + attr(styles[k], 'id')] = okhash(xml2str(styles[k])).toString(16);
|
||||||
|
}
|
||||||
|
for (var j = 0; j < placemarks.length; j++) {
|
||||||
|
gj.features = gj.features.concat(getPlacemark(placemarks[j]));
|
||||||
|
}
|
||||||
|
function gxCoord(v) { return numarray(v.split(' ')); }
|
||||||
|
function gxCoords(root) {
|
||||||
|
var elems = get(root, 'coord', 'gx'), coords = [];
|
||||||
|
for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
|
||||||
|
return coords;
|
||||||
|
}
|
||||||
|
function getGeometry(root) {
|
||||||
|
var geomNode, geomNodes, i, j, k, geoms = [];
|
||||||
|
if (get1(root, 'MultiGeometry')) return getGeometry(get1(root, 'MultiGeometry'));
|
||||||
|
if (get1(root, 'MultiTrack')) return getGeometry(get1(root, 'MultiTrack'));
|
||||||
|
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
|
||||||
|
});
|
||||||
|
} else if (geotypes[i] == 'Track') {
|
||||||
|
geoms.push({
|
||||||
|
type: 'LineString',
|
||||||
|
coordinates: gxCoords(geomNode)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 [];
|
||||||
|
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,
|
||||||
|
tracks = get(doc, 'trk'),
|
||||||
|
routes = get(doc, 'rte'),
|
||||||
|
waypoints = get(doc, 'wpt'),
|
||||||
|
// a feature collection
|
||||||
|
gj = fc();
|
||||||
|
for (i = 0; i < tracks.length; i++) {
|
||||||
|
gj.features.push(getLinestring(tracks[i], 'trkpt'));
|
||||||
|
}
|
||||||
|
for (i = 0; i < routes.length; i++) {
|
||||||
|
gj.features.push(getLinestring(routes[i], 'rtept'));
|
||||||
|
}
|
||||||
|
for (i = 0; i < waypoints.length; i++) {
|
||||||
|
gj.features.push(getPoint(waypoints[i]));
|
||||||
|
}
|
||||||
|
function getLinestring(node, pointname) {
|
||||||
|
var j, pts = get(node, pointname), line = [];
|
||||||
|
for (j = 0; j < pts.length; j++) {
|
||||||
|
line.push(coordPair(pts[j]));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: 'Feature',
|
||||||
|
properties: getProperties(node),
|
||||||
|
geometry: {
|
||||||
|
type: 'LineString',
|
||||||
|
coordinates: line
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function getPoint(node) {
|
||||||
|
var prop = getProperties(node);
|
||||||
|
prop.ele = nodeVal(get1(node, 'ele'));
|
||||||
|
prop.sym = nodeVal(get1(node, 'sym'));
|
||||||
|
return {
|
||||||
|
type: 'Feature',
|
||||||
|
properties: prop,
|
||||||
|
geometry: {
|
||||||
|
type: 'Point',
|
||||||
|
coordinates: coordPair(node)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function getProperties(node) {
|
||||||
|
var meta = ['name', 'desc', 'author', 'copyright', 'link',
|
||||||
|
'time', 'keywords'],
|
||||||
|
prop = {},
|
||||||
|
k;
|
||||||
|
for (k = 0; k < meta.length; k++) {
|
||||||
|
prop[meta[k]] = nodeVal(get1(node, meta[k]));
|
||||||
|
}
|
||||||
|
return clean(prop);
|
||||||
|
}
|
||||||
|
return gj;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return t;
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (typeof module !== 'undefined') module.exports = toGeoJSON;
|
BIN
images/open-folder-icon_sml.png
Normal file
BIN
images/open-folder-icon_sml.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 345 B |
68
plugins/add-kml.user.js
Executable file
68
plugins/add-kml.user.js
Executable file
@ -0,0 +1,68 @@
|
|||||||
|
// ==UserScript==
|
||||||
|
// @id overlay-kml@danielatkins
|
||||||
|
// @name IITC plugin: overlay KML
|
||||||
|
// @category Info
|
||||||
|
// @version 0.1.@@DATETIMEVERSION@@
|
||||||
|
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||||
|
// @updateURL @@UPDATEURL@@
|
||||||
|
// @downloadURL @@DOWNLOADURL@@
|
||||||
|
// @description [@@BUILDNAME@@-@@BUILDDATE@@] Allows users to overlay their own KML / GPX files on top of IITC
|
||||||
|
// @include https://www.ingress.com/intel*
|
||||||
|
// @include http://www.ingress.com/intel*
|
||||||
|
// @match https://www.ingress.com/intel*
|
||||||
|
// @match http://www.ingress.com/intel*
|
||||||
|
// @grant none
|
||||||
|
// ==/UserScript==
|
||||||
|
|
||||||
|
@@PLUGINSTART@@
|
||||||
|
|
||||||
|
// PLUGIN START ////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// use own namespace for plugin
|
||||||
|
window.plugin.overlayKML = function() {};
|
||||||
|
|
||||||
|
window.plugin.overlayKML.loadExternals = function() {
|
||||||
|
try { console.log('Loading leaflet.filelayer JS now'); } catch(e) {}
|
||||||
|
@@INCLUDERAW:external/leaflet.filelayer.js@@
|
||||||
|
try { console.log('done loading leaflet.filelayer JS'); } catch(e) {}
|
||||||
|
|
||||||
|
try { console.log('Loading KML JS now'); } catch(e) {}
|
||||||
|
@@INCLUDERAW:external/KML.js@@
|
||||||
|
try { console.log('done loading KML JS'); } catch(e) {}
|
||||||
|
|
||||||
|
try { console.log('Loading togeojson JS now'); } catch(e) {}
|
||||||
|
@@INCLUDERAW:external/togeojson.js@@
|
||||||
|
try { console.log('done loading togeojson JS'); } catch(e) {}
|
||||||
|
|
||||||
|
window.plugin.overlayKML.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.plugin.overlayKML.load = function() {
|
||||||
|
// Provide popup window allow user to select KML to overlay
|
||||||
|
|
||||||
|
L.Icon.Default.imagePath = '@@INCLUDEIMAGE:images/marker-icon.png@@';
|
||||||
|
var KMLIcon = L.icon({
|
||||||
|
iconUrl: '@@INCLUDEIMAGE:images/marker-icon.png@@',
|
||||||
|
|
||||||
|
iconSize: [16, 24], // size of the icon
|
||||||
|
iconAnchor: [8, 24], // point of the icon which will correspond to marker's location
|
||||||
|
popupAnchor: [-3, 16] // point from which the popup should open relative to the iconAnchor
|
||||||
|
});
|
||||||
|
|
||||||
|
L.Control.FileLayerLoad.LABEL = '<img src="@@INCLUDEIMAGE:images/open-folder-icon_sml.png@@" alt="Open" />';
|
||||||
|
L.Control.fileLayerLoad({
|
||||||
|
fitBounds: true,
|
||||||
|
layerOptions: {
|
||||||
|
pointToLayer: function (data, latlng) {
|
||||||
|
return L.marker(latlng, {icon: KMLIcon});
|
||||||
|
}},
|
||||||
|
}).addTo(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
var setup = function() {
|
||||||
|
window.plugin.overlayKML.loadExternals();
|
||||||
|
}
|
||||||
|
|
||||||
|
// PLUGIN END //////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@@PLUGINEND@@
|
Loading…
x
Reference in New Issue
Block a user