From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

// Derived from https://github.com/mapbox/togeojson/blob/master/togeojson.js (BSD-2-Clause licence)



// <nowiki>

$( function($) {



var _toGeoJSON = (function fnToGeoJSON() {

    '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 ? n0 : 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++) { oj = parseFloat(xj]); }

        return o;

    }

    // get the content of a text node, if any

    function nodeVal(x) {

        if (x) { norm(x); }

        return (x && x.textContent) || '';

    }

    // get the contents of multiple text nodes, if present

    function getMulti(x, ys) {

        var o = {}, n, k;

        for (k = 0; k < ys.length; k++) {

            n = get1(x, ysk]);

            if (n) oysk]] = nodeVal(n);

        }

        return o;

    }

    // add properties of Y to X, overwriting if present in both

    function extend(x, y) { for (var k in y) xk = yk]; }

    // 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(coordsi]));

        }

        return o;

    }

    function coordPair(x) {

        var ll = attrf(x, 'lon'), attrf(x, 'lat')],

            ele = get1(x, 'ele'),

            // handle namespaced attribute in browser

            heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'),

            time = get1(x, 'time'),

            e;

        if (ele) {

            e = parseFloat(nodeVal(ele));

            if (!isNaN(e)) {

                ll.push(e);

            }

        }

        return {

            coordinates: ll,

            time: time ? nodeVal(time) : null,

            heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null

        };

    }



    // create a new feature collection parent object

    function fc() {

        return {

            type: 'FeatureCollection',

            features: []

        };

    }



    var serializer;

    if (typeof XMLSerializer !== 'undefined') {

        /* istanbul ignore next */

        serializer = new XMLSerializer();

    } else {

        var isNodeEnv = (typeof process === 'object' && !process.browser);

        var isTitaniumEnv = (typeof Titanium === 'object');

        if (typeof exports === 'object' && (isNodeEnv || isTitaniumEnv)) {

            serializer = new (require('xmldom').XMLSerializer)();

        } else {

            throw new Error('Unable to initialize serializer');

        }

    }

    function xml2str(str) {

        // IE9 will create a new XMLSerializer but it'll crash immediately.

        // This line is ignored because we don't run coverage tests in IE9

        /* istanbul ignore next */

        if (str.xml !== undefined) return str.xml;

        return serializer.serializeToString(str);

    }



    var t = {

        kml: function(doc) {



            var gj = fc(),

                // styleindex keeps track of hashed styles in order to match features

                styleIndex = {}, styleByHash = {},

                // stylemapindex keeps track of style maps to expose in properties

                styleMapIndex = {},

                // atomic geospatial types supported by KML - MultiGeometry is

                // handled separately

                geotypes = 'Polygon', 'LineString', 'Point', 'Track', 'gx:Track'],

                // all root placemarks in the file

                placemarks = get(doc, 'Placemark'),

                styles = get(doc, 'Style'),

                styleMaps = get(doc, 'StyleMap');



            for (var k = 0; k < styles.length; k++) {

                var hash = okhash(xml2str(stylesk])).toString(16);

                styleIndex'#' + attr(stylesk], 'id')] = hash;

                styleByHashhash = stylesk];

            }

            for (var l = 0; l < styleMaps.length; l++) {

                styleIndex'#' + attr(styleMapsl], 'id')] = okhash(xml2str(styleMapsl])).toString(16);

                var pairs = get(styleMapsl], 'Pair');

                var pairsMap = {};

                for (var m = 0; m < pairs.length; m++) {

                    pairsMapnodeVal(get1(pairsm], 'key'))] = nodeVal(get1(pairsm], 'styleUrl'));

                }

                styleMapIndex'#' + attr(styleMapsl], 'id')] = pairsMap;



            }

            for (var j = 0; j < placemarks.length; j++) {

                gj.features = gj.features.concat(getPlacemark(placemarksj]));

            }

            function kmlColor(v) {

                var color, opacity;

                v = v || '';

                if (v.substr(0, 1) === '#') { v = v.substr(1); }

                if (v.length === 6 || v.length === 3) { color = v; }

                if (v.length === 8) {

                    opacity = parseInt(v.substr(0, 2), 16) / 255;

                    color = '#' + v.substr(6, 2) +

                        v.substr(4, 2) +

                        v.substr(2, 2);

                }

                return color, isNaN(opacity) ? undefined : opacity];

            }

            function gxCoord(v) { return numarray(v.split(' ')); }

            function gxCoords(root) {

                var elems = get(root, 'coord', 'gx'), coords = [], times = [];

                if (elems.length === 0) elems = get(root, 'gx:coord');

                for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elemsi])));

                var timeElems = get(root, 'when');

                for (var j = 0; j < timeElems.length; j++) times.push(nodeVal(timeElemsj]));

                return {

                    coords: coords,

                    times: times

                };

            }

            function getGeometry(root) {

                var geomNode, geomNodes, i, j, k, geoms = [], coordTimes = [];

                if (get1(root, 'MultiGeometry')) { return getGeometry(get1(root, 'MultiGeometry')); }

                if (get1(root, 'MultiTrack')) { return getGeometry(get1(root, 'MultiTrack')); }

                if (get1(root, 'gx:MultiTrack')) { return getGeometry(get1(root, 'gx:MultiTrack')); }

                for (i = 0; i < geotypes.length; i++) {

                    geomNodes = get(root, geotypesi]);

                    if (geomNodes) {

                        for (j = 0; j < geomNodes.length; j++) {

                            geomNode = geomNodesj];

                            if (geotypesi === 'Point') {

                                geoms.push({

                                    type: 'Point',

                                    coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))

                                });

                            } else if (geotypesi === 'LineString') {

                                geoms.push({

                                    type: 'LineString',

                                    coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))

                                });

                            } else if (geotypesi === 'Polygon') {

                                var rings = get(geomNode, 'LinearRing'),

                                    coords = [];

                                for (k = 0; k < rings.length; k++) {

                                    coords.push(coord(nodeVal(get1(ringsk], 'coordinates'))));

                                }

                                geoms.push({

                                    type: 'Polygon',

                                    coordinates: coords

                                });

                            } else if (geotypesi === 'Track' ||

                                geotypesi === 'gx:Track') {

                                var track = gxCoords(geomNode);

                                geoms.push({

                                    type: 'LineString',

                                    coordinates: track.coords

                                });

                                if (track.times.length) coordTimes.push(track.times);

                            }

                        }

                    }

                }

                return {

                    geoms: geoms,

                    coordTimes: coordTimes

                };

            }

            function getPlacemark(root) {

                var geomsAndTimes = getGeometry(root), i, properties = {},

                    name = nodeVal(get1(root, 'name')),

                    address = nodeVal(get1(root, 'address')),

                    styleUrl = nodeVal(get1(root, 'styleUrl')),

                    description = nodeVal(get1(root, 'description')),

                    timeSpan = get1(root, 'TimeSpan'),

                    timeStamp = get1(root, 'TimeStamp'),

                    extendedData = get1(root, 'ExtendedData'),

                    lineStyle = get1(root, 'LineStyle'),

                    polyStyle = get1(root, 'PolyStyle'),

                    visibility = get1(root, 'visibility');



                if (!geomsAndTimes.geoms.length) return [];

                if (name) properties.name = name;

                if (address) properties.address = address;

                if (styleUrl) {

                    if (styleUrl0 !== '#') {

                        styleUrl = '#' + styleUrl;

                    }



                    properties.styleUrl = styleUrl;

                    if (styleIndexstyleUrl]) {

                        properties.styleHash = styleIndexstyleUrl];

                    }

                    if (styleMapIndexstyleUrl]) {

                        properties.styleMapHash = styleMapIndexstyleUrl];

                        properties.styleHash = styleIndexstyleMapIndexstyleUrl].normal];

                    }

                    // Try to populate the lineStyle or polyStyle since we got the style hash

                    var style = styleByHashproperties.styleHash];

                    if (style) {

                        if (!lineStyle) lineStyle = get1(style, 'LineStyle');

                        if (!polyStyle) polyStyle = get1(style, 'PolyStyle');

                        var iconStyle = get1(style, 'IconStyle');

                        if (iconStyle) {

                            var icon = get1(iconStyle, 'Icon');

                            if (icon) {

                                var href = nodeVal(get1(icon, 'href'));

                                if (href) properties.icon = href;

                            }

                        }

                    }

                }

                if (description) properties.description = description;

                if (timeSpan) {

                    var begin = nodeVal(get1(timeSpan, 'begin'));

                    var end = nodeVal(get1(timeSpan, 'end'));

                    properties.timespan = { begin: begin, end: end };

                }

                if (timeStamp) {

                    properties.timestamp = nodeVal(get1(timeStamp, 'when'));

                }

                if (lineStyle) {

                    var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),

                        color = linestyles0],

                        opacity = linestyles1],

                        width = parseFloat(nodeVal(get1(lineStyle, 'width')));

                    if (color) properties.stroke = color;

                    if (!isNaN(opacity)) properties'stroke-opacity' = opacity;

                    if (!isNaN(width)) properties'stroke-width' = width;

                }

                if (polyStyle) {

                    var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),

                        pcolor = polystyles0],

                        popacity = polystyles1],

                        fill = nodeVal(get1(polyStyle, 'fill')),

                        outline = nodeVal(get1(polyStyle, 'outline'));

                    if (pcolor) properties.fill = pcolor;

                    if (!isNaN(popacity)) properties'fill-opacity' = popacity;

                    if (fill) properties'fill-opacity' = fill === '1' ? properties'fill-opacity' || 1 : 0;

                    if (outline) properties'stroke-opacity' = outline === '1' ? properties'stroke-opacity' || 1 : 0;

                }

                if (extendedData) {

                    var datas = get(extendedData, 'Data'),

                        simpleDatas = get(extendedData, 'SimpleData');



                    for (i = 0; i < datas.length; i++) {

                        propertiesdatasi].getAttribute('name')] = nodeVal(get1(datasi], 'value'));

                    }

                    for (i = 0; i < simpleDatas.length; i++) {

                        propertiessimpleDatasi].getAttribute('name')] = nodeVal(simpleDatasi]);

                    }

                }

                if (visibility) {

                    properties.visibility = nodeVal(visibility);

                }

                if (geomsAndTimes.coordTimes.length) {

                    properties.coordTimes = (geomsAndTimes.coordTimes.length === 1) ?

                        geomsAndTimes.coordTimes0 : geomsAndTimes.coordTimes;

                }

                var feature = {

                    type: 'Feature',

                    geometry: (geomsAndTimes.geoms.length === 1) ? geomsAndTimes.geoms0 : {

                        type: 'GeometryCollection',

                        geometries: geomsAndTimes.geoms

                    },

                    properties: properties

                };

                if (attr(root, 'id')) feature.id = attr(root, 'id');

                return feature];

            }

            return gj;

        },

        gpx: function(doc) {

            var i,

                tracks = get(doc, 'trk'),

                routes = get(doc, 'rte'),

                waypoints = get(doc, 'wpt'),

                // a feature collection

                gj = fc(),

                feature;

            for (i = 0; i < tracks.length; i++) {

                feature = getTrack(tracksi]);

                if (feature) gj.features.push(feature);

            }

            for (i = 0; i < routes.length; i++) {

                feature = getRoute(routesi]);

                if (feature) gj.features.push(feature);

            }

            for (i = 0; i < waypoints.length; i++) {

                gj.features.push(getPoint(waypointsi]));

            }

            function initializeArray(arr, size) {

                for (var h = 0; h < size; h++) {

                    arr.push(null);

                }

                return arr;

            }

            function getPoints(node, pointname) {

                var pts = get(node, pointname),

                    line = [],

                    times = [],

                    heartRates = [],

                    l = pts.length;

                if (l < 2) return {};  // Invalid line in GeoJSON

                for (var i = 0; i < l; i++) {

                    var c = coordPair(ptsi]);

                    line.push(c.coordinates);

                    if (c.time) times.push(c.time);

                    if (c.heartRate || heartRates.length) {

                        if (!heartRates.length) initializeArray(heartRates, i);

                        heartRates.push(c.heartRate || null);

                    }

                }

                return {

                    line: line,

                    times: times,

                    heartRates: heartRates

                };

            }

            function getTrack(node) {

                var segments = get(node, 'trkseg'),

                    track = [],

                    times = [],

                    heartRates = [],

                    line;

                for (var i = 0; i < segments.length; i++) {

                    line = getPoints(segmentsi], 'trkpt');

                    if (line) {

                        if (line.line) track.push(line.line);

                        if (line.times && line.times.length) times.push(line.times);

                        if (heartRates.length || (line.heartRates && line.heartRates.length)) {

                            if (!heartRates.length) {

                                for (var s = 0; s < i; s++) {

                                    heartRates.push(initializeArray([], tracks].length));

                                }

                            }

                            if (line.heartRates && line.heartRates.length) {

                                heartRates.push(line.heartRates);

                            } else {

                                heartRates.push(initializeArray([], line.line.length || 0));

                            }

                        }

                    }

                }

                if (track.length === 0) return;

                var properties = getProperties(node);

                extend(properties, getLineStyle(get1(node, 'extensions')));

                if (times.length) properties.coordTimes = track.length === 1 ? times0 : times;

                if (heartRates.length) properties.heartRates = track.length === 1 ? heartRates0 : heartRates;

                return {

                    type: 'Feature',

                    properties: properties,

                    geometry: {

                        type: track.length === 1 ? 'LineString' : 'MultiLineString',

                        coordinates: track.length === 1 ? track0 : track

                    }

                };

            }

            function getRoute(node) {

                var line = getPoints(node, 'rtept');

                if (!line.line) return;

                var prop = getProperties(node);

                extend(prop, getLineStyle(get1(node, 'extensions')));

                var routeObj = {

                    type: 'Feature',

                    properties: prop,

                    geometry: {

                        type: 'LineString',

                        coordinates: line.line

                    }

                };

                return routeObj;

            }

            function getPoint(node) {

                var prop = getProperties(node);

                extend(prop, getMulti(node, 'sym']));

                return {

                    type: 'Feature',

                    properties: prop,

                    geometry: {

                        type: 'Point',

                        coordinates: coordPair(node).coordinates

                    }

                };

            }

            function getLineStyle(extensions) {

                var style = {};

                if (extensions) {

                    var lineStyle = get1(extensions, 'line');

                    if (lineStyle) {

                        var color = nodeVal(get1(lineStyle, 'color')),

                            opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))),

                            width = parseFloat(nodeVal(get1(lineStyle, 'width')));

                        if (color) style.stroke = color;

                        if (!isNaN(opacity)) style'stroke-opacity' = opacity;

                        // GPX width is in mm, convert to px with 96 px per inch

                        if (!isNaN(width)) style'stroke-width' = width * 96 / 25.4;

                    }

                }

                return style;

            }

            function getProperties(node) {

                var prop = getMulti(node, 'name', 'cmt', 'desc', 'type', 'time', 'keywords']),

                    links = get(node, 'link');

                if (links.length) prop.links = [];

                for (var i = 0, link; i < links.length; i++) {

                    link = { href: attr(linksi], 'href') };

                    extend(link, getMulti(linksi], 'text', 'type']));

                    prop.links.push(link);

                }

                return prop;

            }

            return gj;

        }

    };

    return t;

})();



var toGeoJson = _toGeoJSON.kml;



var getKML = function fnGetKML() {

	var url = 'https:' + mw.config.get('wgServer') + mw.util.getUrl(null, {action: 'raw'});

	return $.ajax(url);

};



var toDOM = function fnToDOM(xmlStr) {

	return (new DOMParser()).parseFromString(xmlStr, 'text/xml');

};



var parseOutput = function(geoJSON) {

	return JSON.stringify(geoJSON);

};



var showOutput = function fnShowOutput(output) {

	mw.util.$content.empty();

	$('<textarea>')

		.attr('disabled', 'true')

		.css({'background':'#ddd', 'height':'350px'})

		.val(output)

		.appendTo(mw.util.$content);

};



var doConverion = function fnConvert(pagename) {

	// Clear current content

	mw.util.$content.empty().append('Working...');

	

	getKML(pagename)

	.then(toDOM)

	.then(toGeoJson)

	.then(parseOutput)

	.then(showOutput);

};



var setup = function fnSetup() {

	var config = mw.config.get(['wgPageName', 'wgServer']);

	if ( config.wgPageName.indexOf('Template:Attached_KML/') == -1 ) {

		return;

	}



	var portletLink = mw.util.addPortletLink(

		'p-cactions',

		'#',

		'GeoJSON',

		'ca-tojson',

		'Convert to geoJSON',

		'5'

	);

	$('#ca-tojson').click(function(e) {

		e.preventDefault();

		doConverion(config.wgServer);

	});

};	



mw.loader.using( 'mediawiki.util'], setup);



});

// </nowiki>