Subversion Repositories eFlore/Applications.moissonnage

Rev

Rev 28 | Go to most recent revision | View as "text/plain" | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * 
 * Copyright (c) 2011-2012, Pavel Shramov
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, are 
 * permitted provided that the following conditions are met: 
 * 
 *    1. Redistributions of source code must retain the above copyright notice, this list of 
 *       conditions and the following disclaimer. 
 *        
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list 
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 */

// Adaptation Alexandre GALIBERT :
//   - Define fill opacity for polygons at 0 for transparent rendering
//   - Disable creation of icons for KML elements
//   - Specific beahaviour to add an array of files by their urls. A variable will define
//     the status of the processing (BUSY or READY) and managed with a counter for the number
//     of files remaining to parse. Once all files parsed, the map zooms on KML spatial elements

const KML_READY = 0;
const KML_BUSY = 1;


L.KML = L.FeatureGroup.extend({
        options: {
                async: true
        },

        initialize: function(kml, options) {
                L.Util.setOptions(this, options);
                this._kml = kml;
                this._status = KML_READY;
                this._onWaiting = 0;
                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);
        },

        addKMLFiles: function(urlList, options, async) {
                this._status = KML_BUSY;
                this._onWaiting = urlList.length;
                for (var index = 0; index < urlList.length; index ++) {
                        this.addKML(urlList[index], options, async);
                }
        },
        
        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");
                this._onWaiting --;
                if (this._onWaiting == 0) {
                        this._status = KML_READY;
                        this._map.fitBounds(this.getBounds());
                }
        },
        
        getStatus: function() {
                return this._status;
        },

        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(2, 8);
                                        } 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;
                                        }
                                }
                        }
                        options.fillOpacity = 0;
                        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()
        }
});