/** @license * * Copyright (C) 2012 K. Arthur Endsley (kaendsle@mtu.edu) * Michigan Tech Research Institute (MTRI) * 3600 Green Court, Suite 100, Ann Arbor, MI, 48105 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** * @augments Wkt.Wkt * A framework-dependent flag, set for each Wkt.Wkt() instance, that indicates * whether or not a closed polygon geometry should be interpreted as a rectangle. */ Wkt.Wkt.prototype.isRectangle = false; /** * @augments Wkt.Wkt * Truncates an Array of coordinates by the closing coordinate when it is * equal to the first coordinate given--this is only to be used for closed * geometries in order to provide merely an "implied" closure to Leaflet. * @param coords {Array} An Array of x,y coordinates (objects) * @return {Array} */ Wkt.Wkt.prototype.trunc = function (coords) { var i, verts = []; for (i = 0; i < coords.length; i += 1) { if (Wkt.isArray(coords[i])) { verts.push(this.trunc(coords[i])); } else { // Add the first coord, but skip the last if it is identical if (i === 0 || !this.sameCoords(coords[0], coords[i])) { verts.push(coords[i]); } } } return verts; }; /** * @augments Wkt.Wkt * An object of framework-dependent construction methods used to generate * objects belonging to the various geometry classes of the framework. */ Wkt.Wkt.prototype.construct = { /** * Creates the framework's equivalent point geometry object. * @param config {Object} An optional properties hash the object should use * @param component {Object} An optional component to build from * @return {L.marker} */ point: function (config, component) { var coord = component || this.components; if (coord instanceof Array) { coord = coord[0]; } return L.marker(this.coordsToLatLng(coord), config); }, /** * Creates the framework's equivalent multipoint geometry object. * @param config {Object} An optional properties hash the object should use * @return {L.featureGroup} */ multipoint: function (config) { var i, layers = [], coords = this.components; for (i = 0; i < coords.length; i += 1) { layers.push(this.construct.point.call(this, config, coords[i])); } return L.featureGroup(layers, config); }, /** * Creates the framework's equivalent linestring geometry object. * @param config {Object} An optional properties hash the object should use * @param component {Object} An optional component to build from * @return {L.polyline} */ linestring: function (config, component) { var coords = component || this.components, latlngs = this.coordsToLatLngs(coords); return L.polyline(latlngs, config); }, /** * Creates the framework's equivalent multilinestring geometry object. * @param config {Object} An optional properties hash the object should use * @return {L.multiPolyline} */ multilinestring: function (config) { var coords = this.components, latlngs = this.coordsToLatLngs(coords, 1); return L.multiPolyline(latlngs, config); }, /** * Creates the framework's equivalent polygon geometry object. * @param config {Object} An optional properties hash the object should use * @return {L.multiPolygon} */ polygon: function (config) { // Truncate the coordinates to remove the closing coordinate var coords = this.trunc(this.components), latlngs = this.coordsToLatLngs(coords, 1); return L.polygon(latlngs, config); }, /** * Creates the framework's equivalent multipolygon geometry object. * @param config {Object} An optional properties hash the object should use * @return {L.multiPolygon} */ multipolygon: function (config) { // Truncate the coordinates to remove the closing coordinate var coords = this.trunc(this.components), latlngs = this.coordsToLatLngs(coords, 2); return L.multiPolygon(latlngs, config); }, /** * Creates the framework's equivalent collection of geometry objects. * @param config {Object} An optional properties hash the object should use * @return {L.featureGroup} */ geometrycollection: function (config) { var comps, i, layers; comps = this.trunc(this.components); layers = []; for (i = 0; i < this.components.length; i += 1) { layers.push(this.construct[comps[i].type].call(this, comps[i])); } return L.featureGroup(layers, config); } }; L.Util.extend(Wkt.Wkt.prototype, { coordsToLatLngs: L.GeoJSON.coordsToLatLngs, // TODO Why doesn't the coordsToLatLng function in L.GeoJSON already suffice? coordsToLatLng: function (coords, reverse) { var lat = reverse ? coords.x : coords.y, lng = reverse ? coords.y : coords.x; return L.latLng(lat, lng, true); } }); /** * @augments Wkt.Wkt * A framework-dependent deconstruction method used to generate internal * geometric representations from instances of framework geometry. This method * uses object detection to attempt to classify members of framework geometry * classes into the standard WKT types. * @param obj {Object} An instance of one of the framework's geometry classes * @return {Object} A hash of the 'type' and 'components' thus derived */ Wkt.Wkt.prototype.deconstruct = function (obj) { var attr, coordsFromLatLngs, features, i, verts, rings, tmp; /** * Accepts an Array (arr) of LatLngs from which it extracts each one as a * vertex; calls itself recursively to deal with nested Arrays. */ coordsFromLatLngs = function (arr) { var i, coords; coords = []; for (i = 0; i < arr.length; i += 1) { if (Wkt.isArray(arr[i])) { coords.push(coordsFromLatLngs(arr[i])); } else { coords.push({ x: arr[i].lng, y: arr[i].lat }); } } return coords; }; // L.Marker //////////////////////////////////////////////////////////////// if (obj.constructor === L.Marker || obj.constructor === L.marker) { return { type: 'point', components: [{ x: obj.getLatLng().lng, y: obj.getLatLng().lat }] }; } // L.Rectangle ///////////////////////////////////////////////////////////// if (obj.constructor === L.Rectangle || obj.constructor === L.rectangle) { tmp = obj.getBounds(); // L.LatLngBounds instance return { type: 'polygon', isRectangle: true, components: [ [ { // NW corner x: tmp.getSouthWest().lng, y: tmp.getNorthEast().lat }, { // NE corner x: tmp.getNorthEast().lng, y: tmp.getNorthEast().lat }, { // SE corner x: tmp.getNorthEast().lng, y: tmp.getSouthWest().lat }, { // SW corner x: tmp.getSouthWest().lng, y: tmp.getSouthWest().lat }, { // NW corner (again, for closure) x: tmp.getSouthWest().lng, y: tmp.getNorthEast().lat } ] ] }; } // L.Polyline ////////////////////////////////////////////////////////////// if (obj.constructor === L.Polyline || obj.constructor === L.polyline) { verts = []; tmp = obj.getLatLngs(); if (!tmp[0].equals(tmp[tmp.length - 1])) { for (i = 0; i < tmp.length; i += 1) { verts.push({ x: tmp[i].lng, y: tmp[i].lat }); } return { type: 'linestring', components: verts }; } } // L.Polygon /////////////////////////////////////////////////////////////// if (obj.constructor === L.Polygon || obj.constructor === L.polygon) { rings = []; verts = []; tmp = obj.getLatLngs(); // First, we deal with the boundary points for (i = 0; i < obj._latlngs.length; i += 1) { verts.push({ // Add the first coordinate again for closure x: tmp[i].lng, y: tmp[i].lat }); } verts.push({ // Add the first coordinate again for closure x: tmp[0].lng, y: tmp[0].lat }); rings.push(verts); // Now, any holes if (obj._holes && obj._holes.length > 0) { // Reworked to support holes properly verts = coordsFromLatLngs(obj._holes); for (i=0; i < verts.length;i++) { verts[i].push(verts[i][0]); // Copy the beginning coords again for closure rings.push(verts[i]); } } return { type: 'polygon', components: rings }; } // L.MultiPolyline ///////////////////////////////////////////////////////// // L.MultiPolygon ////////////////////////////////////////////////////////// // L.LayerGroup //////////////////////////////////////////////////////////// // L.FeatureGroup ////////////////////////////////////////////////////////// if (obj.constructor === L.MultiPolyline || obj.constructor === L.MultiPolygon || obj.constructor === L.LayerGroup || obj.constructor === L.FeatureGroup) { features = []; tmp = obj._layers; for (attr in tmp) { if (tmp.hasOwnProperty(attr)) { if (tmp[attr].getLatLngs || tmp[attr].getLatLng) { // Recursively deconstruct each layer features.push(this.deconstruct(tmp[attr])); } } } return { type: (function () { switch (obj.constructor) { case L.MultiPolyline: return 'multilinestring'; case L.MultiPolygon: return 'multipolygon'; case L.FeatureGroup: return (function () { var i, mpgon, mpline, mpoint; // Assume that all layers are of one type (any one type) mpgon = true; mpline = true; mpoint = true; for (i in obj._layers) { if (obj._layers.hasOwnProperty(i)) { if (obj._layers[i].constructor !== L.Marker) { mpoint = false; } if (obj._layers[i].constructor !== L.Polyline) { mpline = false; } if (obj._layers[i].constructor !== L.Polygon) { mpgon = false; } } } if (mpoint) { return 'multipoint'; } if (mpline) { return 'multilinestring'; } if (mpgon) { return 'multipolygon'; } return 'geometrycollection'; }()); default: return 'geometrycollection'; } }()), components: (function () { // Pluck the components from each Wkt var i, comps; comps = []; for (i = 0; i < features.length; i += 1) { if (features[i].components) { comps.push(features[i].components); } } return comps; }()) }; } // L.Circle //////////////////////////////////////////////////////////////// if (obj.constructor === L.Rectangle || obj.constructor === L.rectangle) { console.log('Deconstruction of L.Circle objects is not yet supported'); } else { console.log('The passed object does not have any recognizable properties.'); } };