wicket
Version:
A modest library for moving between Well-Known Text (WKT) and various framework geometries
434 lines (371 loc) • 15.8 kB
JavaScript
/** @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 <http://www.gnu.org/licenses/>.
*
*/
/**
* @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.
*/
(function ( root, factory ) {
if ( typeof exports === 'object' ) {
// CommonJS
module.exports = factory( require('./wicket') );
} else if ( typeof define === 'function' && define.amd ) {
// AMD. Register as an anonymous module.
define( ['wicket'], factory);
} else {
// Browser globals
factory(root.Wkt );
}
}
(this, function(Wkt) {
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, 0, this.coordsToLatLng);
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, this.coordsToLatLng);
if (L.multiPolyline) {
return L.multiPolyline(latlngs, config);
}
else {
return L.polyline(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, this.coordsToLatLng);
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, this.coordsToLatLng);
if (L.multiPolygon) {
return L.multiPolygon(latlngs, config);
}
else {
return L.polygon(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, boundary;
/**
* 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 = [];
boundary = obj.getLatLngs()[0];
// First, we deal with the boundary points
for (i = 0; i < boundary.length; i += 1) {
verts.push({ // Add the first coordinate again for closure
x: boundary[i].lng,
y: boundary[i].lat
});
}
verts.push({ // Add the first coordinate again for closure
x: boundary[0].lng,
y: boundary[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.Circle || obj.constructor === L.circle) {
console.log('Deconstruction of L.Circle objects is not yet supported');
} else {
console.log('The passed object does not have any recognizable properties.');
}
};
return Wkt;
}));