745 lines
25 KiB
JavaScript
745 lines
25 KiB
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/>.
|
|
*
|
|
*/
|
|
(function ( root, factory ) {
|
|
if ( typeof exports === 'object' ) {
|
|
// CommonJS
|
|
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) {
|
|
|
|
/**
|
|
* @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
|
|
* 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 {google.maps.Marker}
|
|
*/
|
|
point: function (config, component) {
|
|
var c = component || this.components;
|
|
|
|
config = config || {};
|
|
|
|
config.position = new google.maps.LatLng(c[0].y, c[0].x);
|
|
|
|
return new google.maps.Marker(config);
|
|
},
|
|
|
|
/**
|
|
* Creates the framework's equivalent multipoint geometry object.
|
|
* @param config {Object} An optional properties hash the object should use
|
|
* @return {Array} Array containing multiple google.maps.Marker
|
|
*/
|
|
multipoint: function (config) {
|
|
var i, c, arr;
|
|
|
|
c = this.components;
|
|
|
|
config = config || {};
|
|
|
|
arr = [];
|
|
|
|
for (i = 0; i < c.length; i += 1) {
|
|
arr.push(this.construct.point(config, c[i]));
|
|
}
|
|
|
|
return arr;
|
|
},
|
|
|
|
/**
|
|
* 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 {google.maps.Polyline}
|
|
*/
|
|
linestring: function (config, component) {
|
|
var i, c;
|
|
|
|
c = component || this.components;
|
|
|
|
config = config || {
|
|
editable: false
|
|
};
|
|
|
|
config.path = [];
|
|
|
|
for (i = 0; i < c.length; i += 1) {
|
|
config.path.push(new google.maps.LatLng(c[i].y, c[i].x));
|
|
}
|
|
|
|
return new google.maps.Polyline(config);
|
|
},
|
|
|
|
/**
|
|
* Creates the framework's equivalent multilinestring geometry object.
|
|
* @param config {Object} An optional properties hash the object should use
|
|
* @return {Array} Array containing multiple google.maps.Polyline instances
|
|
*/
|
|
multilinestring: function (config) {
|
|
var i, c, arr;
|
|
|
|
c = this.components;
|
|
|
|
config = config || {
|
|
editable: false
|
|
};
|
|
|
|
config.path = [];
|
|
|
|
arr = [];
|
|
|
|
for (i = 0; i < c.length; i += 1) {
|
|
arr.push(this.construct.linestring(config, c[i]));
|
|
}
|
|
|
|
return arr;
|
|
},
|
|
|
|
/**
|
|
* Creates the framework's equivalent Box or Rectangle geometry object.
|
|
* @param config {Object} An optional properties hash the object should use
|
|
* @param component {Object} An optional component to build from
|
|
* @return {google.maps.Rectangle}
|
|
*/
|
|
box: function (config, component) {
|
|
var c = component || this.components;
|
|
|
|
config = config || {};
|
|
|
|
config.bounds = new google.maps.LatLngBounds(
|
|
new google.maps.LatLng(c[0].y, c[0].x),
|
|
new google.maps.LatLng(c[1].y, c[1].x));
|
|
|
|
return new google.maps.Rectangle(config);
|
|
},
|
|
|
|
/**
|
|
* Creates the framework's equivalent polygon geometry object.
|
|
* @param config {Object} An optional properties hash the object should use
|
|
* @param component {Object} An optional component to build from
|
|
* @return {google.maps.Polygon}
|
|
*/
|
|
polygon: function (config, component) {
|
|
var j, k, c, rings, verts;
|
|
|
|
var polygonIsClockwise = function (coords) {
|
|
var area = 0,
|
|
j = null,
|
|
i = 0;
|
|
|
|
for (i = 0; i < coords.length; i++) {
|
|
j = (i + 1) % coords.length;
|
|
area += coords[i].x * coords[j].x;
|
|
area -= coords[j].y * coords[i].y;
|
|
}
|
|
|
|
return area > 0;
|
|
};
|
|
|
|
c = component || this.components;
|
|
|
|
config = config || {
|
|
editable: false // Editable geometry off by default
|
|
};
|
|
|
|
config.paths = [];
|
|
|
|
rings = [];
|
|
for (j = 0; j < c.length; j += 1) { // For each ring...
|
|
|
|
verts = [];
|
|
// NOTE: We iterate to one (1) less than the Array length to skip the last vertex
|
|
for (k = 0; k < c[j].length - 1; k += 1) { // For each vertex...
|
|
verts.push(new google.maps.LatLng(c[j][k].y, c[j][k].x));
|
|
|
|
} // eo for each vertex
|
|
|
|
if (j !== 0) {
|
|
// Orient inner rings correctly
|
|
if (polygonIsClockwise(c[j]) && this.type == 'polygon') {
|
|
verts.reverse();
|
|
}
|
|
}
|
|
|
|
rings.push(verts);
|
|
} // eo for each ring
|
|
|
|
config.paths = config.paths.concat(rings);
|
|
|
|
if (this.isRectangle) {
|
|
return (function () {
|
|
var bounds, v;
|
|
|
|
bounds = new google.maps.LatLngBounds();
|
|
|
|
for (v in rings[0]) { // Ought to be only 1 ring in a Rectangle
|
|
if (rings[0].hasOwnProperty(v)) {
|
|
bounds.extend(rings[0][v]);
|
|
}
|
|
}
|
|
|
|
return new google.maps.Rectangle({
|
|
bounds: bounds
|
|
});
|
|
}());
|
|
} else {
|
|
return new google.maps.Polygon(config);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Creates the framework's equivalent multipolygon geometry object.
|
|
* @param config {Object} An optional properties hash the object should use
|
|
* @return {Array} Array containing multiple google.maps.Polygon
|
|
*/
|
|
multipolygon: function (config) {
|
|
var i, c, arr;
|
|
|
|
c = this.components;
|
|
|
|
config = config || {
|
|
editable: false
|
|
};
|
|
|
|
config.path = [];
|
|
|
|
arr = [];
|
|
|
|
for (i = 0; i < c.length; i += 1) {
|
|
arr.push(this.construct.polygon(config, c[i]));
|
|
}
|
|
|
|
return arr;
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* @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
|
|
* @param multiFlag {Boolean} If true, then the deconstructor will be forced to return a MultiGeometry (multipoint, multilinestring or multipolygon)
|
|
* @return {Object} A hash of the 'type' and 'components' thus derived, plus the WKT string of the feature.
|
|
*/
|
|
Wkt.Wkt.prototype.deconstruct = function (obj, multiFlag) {
|
|
var features, i, j, multiFlag, verts, rings, sign, tmp, response, lat, lng, vertex, ring, linestrings, k;
|
|
|
|
// Shortcut to signed area function (determines clockwise vs counter-clock)
|
|
if (google.maps.geometry) {
|
|
sign = google.maps.geometry.spherical.computeSignedArea;
|
|
};
|
|
|
|
// google.maps.LatLng //////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.LatLng) {
|
|
|
|
response = {
|
|
type: 'point',
|
|
components: [{
|
|
x: obj.lng(),
|
|
y: obj.lat()
|
|
}]
|
|
};
|
|
return response;
|
|
}
|
|
|
|
// google.maps.Point //////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Point) {
|
|
response = {
|
|
type: 'point',
|
|
components: [{
|
|
x: obj.x,
|
|
y: obj.y
|
|
}]
|
|
};
|
|
return response;
|
|
}
|
|
|
|
// google.maps.Marker //////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Marker) {
|
|
response = {
|
|
type: 'point',
|
|
components: [{
|
|
x: obj.getPosition().lng(),
|
|
y: obj.getPosition().lat()
|
|
}]
|
|
};
|
|
return response;
|
|
}
|
|
|
|
// google.maps.Polyline ////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Polyline) {
|
|
|
|
verts = [];
|
|
for (i = 0; i < obj.getPath().length; i += 1) {
|
|
tmp = obj.getPath().getAt(i);
|
|
verts.push({
|
|
x: tmp.lng(),
|
|
y: tmp.lat()
|
|
});
|
|
}
|
|
response = {
|
|
type: 'linestring',
|
|
components: verts
|
|
};
|
|
return response;
|
|
|
|
}
|
|
|
|
// google.maps.Polygon /////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Polygon) {
|
|
|
|
rings = [];
|
|
|
|
if (multiFlag === undefined) {
|
|
multiFlag = (function () {
|
|
var areas, i, l;
|
|
|
|
l = obj.getPaths().length;
|
|
if (l <= 1) { // Trivial; this is a single polygon
|
|
return false;
|
|
}
|
|
|
|
if (l === 2) {
|
|
// If clockwise*clockwise or counter*counter, i.e.
|
|
// (-1)*(-1) or (1)*(1), then result would be positive
|
|
if (sign(obj.getPaths().getAt(0)) * sign(obj.getPaths().getAt(1)) < 0) {
|
|
return false; // Most likely single polygon with 1 hole
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Must be longer than 3 polygons at this point...
|
|
areas = obj.getPaths().getArray().map(function (k) {
|
|
return sign(k) / Math.abs(sign(k)); // Unit normalization (outputs 1 or -1)
|
|
});
|
|
|
|
// If two clockwise or two counter-clockwise rings are found
|
|
// (at different indices)...
|
|
if (areas.indexOf(areas[0]) !== areas.lastIndexOf(areas[0])) {
|
|
multiFlag = true; // Flag for holes in one or more polygons
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
|
|
}());
|
|
}
|
|
|
|
for (i = 0; i < obj.getPaths().length; i += 1) { // For each polygon (ring)...
|
|
tmp = obj.getPaths().getAt(i);
|
|
verts = [];
|
|
|
|
for (j = 0; j < obj.getPaths().getAt(i).length; j += 1) { // For each vertex...
|
|
verts.push({
|
|
x: tmp.getAt(j).lng(),
|
|
y: tmp.getAt(j).lat()
|
|
});
|
|
|
|
}
|
|
|
|
if (!tmp.getAt(tmp.length - 1).equals(tmp.getAt(0))) {
|
|
verts.push({ // Add the first coordinate again for closure
|
|
x: tmp.getAt(0).lng(),
|
|
y: tmp.getAt(0).lat()
|
|
});
|
|
|
|
if (i % 2 !== 0) { // In inner rings, coordinates are reversed...
|
|
verts.reverse();
|
|
}
|
|
}
|
|
|
|
if (obj.getPaths().length > 1 && i > 0) {
|
|
// If this and the last ring have the same signs...
|
|
if (sign(obj.getPaths().getAt(i)) > 0 && sign(obj.getPaths().getAt(i - 1)) > 0 ||
|
|
sign(obj.getPaths().getAt(i)) < 0 && sign(obj.getPaths().getAt(i - 1)) < 0 /*&& !multiFlag*/ ) {
|
|
// ...They must both be inner rings (or both be outer rings, in a multipolygon)
|
|
verts = [verts]; // Wrap multipolygons once more (collection)
|
|
} else {
|
|
verts.reverse();
|
|
}
|
|
|
|
}
|
|
|
|
rings.push(verts);
|
|
}
|
|
|
|
response = {
|
|
type: (multiFlag) ? 'multipolygon' : 'polygon',
|
|
components: rings
|
|
};
|
|
return response;
|
|
|
|
}
|
|
|
|
// google.maps.Circle //////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Circle) {
|
|
var point = obj.getCenter();
|
|
var radius = obj.getRadius();
|
|
verts = [];
|
|
var d2r = Math.PI / 180; // degrees to radians
|
|
var r2d = 180 / Math.PI; // radians to degrees
|
|
radius = radius / 1609; // meters to miles
|
|
var earthsradius = 3963; // 3963 is the radius of the earth in miles
|
|
var num_seg = 32; // number of segments used to approximate a circle
|
|
var rlat = (radius / earthsradius) * r2d;
|
|
var rlng = rlat / Math.cos(point.lat() * d2r);
|
|
|
|
for (var n = 0; n <= num_seg; n++) {
|
|
var theta = Math.PI * (n / (num_seg / 2));
|
|
lng = point.lng() + (rlng * Math.cos(theta)); // center a + radius x * cos(theta)
|
|
lat = point.lat() + (rlat * Math.sin(theta)); // center b + radius y * sin(theta)
|
|
verts.push({
|
|
x: lng,
|
|
y: lat
|
|
});
|
|
}
|
|
|
|
response = {
|
|
type: 'polygon',
|
|
components: [verts]
|
|
};
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
// google.maps.LatLngBounds ///////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.LatLngBounds) {
|
|
|
|
tmp = obj;
|
|
verts = [];
|
|
verts.push({ // NW corner
|
|
x: tmp.getSouthWest().lng(),
|
|
y: tmp.getNorthEast().lat()
|
|
});
|
|
|
|
verts.push({ // NE corner
|
|
x: tmp.getNorthEast().lng(),
|
|
y: tmp.getNorthEast().lat()
|
|
});
|
|
|
|
verts.push({ // SE corner
|
|
x: tmp.getNorthEast().lng(),
|
|
y: tmp.getSouthWest().lat()
|
|
});
|
|
|
|
verts.push({ // SW corner
|
|
x: tmp.getSouthWest().lng(),
|
|
y: tmp.getSouthWest().lat()
|
|
});
|
|
|
|
verts.push({ // NW corner (again, for closure)
|
|
x: tmp.getSouthWest().lng(),
|
|
y: tmp.getNorthEast().lat()
|
|
});
|
|
|
|
response = {
|
|
type: 'polygon',
|
|
isRectangle: true,
|
|
components: [verts]
|
|
};
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
// google.maps.Rectangle ///////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Rectangle) {
|
|
|
|
tmp = obj.getBounds();
|
|
verts = [];
|
|
verts.push({ // NW corner
|
|
x: tmp.getSouthWest().lng(),
|
|
y: tmp.getNorthEast().lat()
|
|
});
|
|
|
|
verts.push({ // NE corner
|
|
x: tmp.getNorthEast().lng(),
|
|
y: tmp.getNorthEast().lat()
|
|
});
|
|
|
|
verts.push({ // SE corner
|
|
x: tmp.getNorthEast().lng(),
|
|
y: tmp.getSouthWest().lat()
|
|
});
|
|
|
|
verts.push({ // SW corner
|
|
x: tmp.getSouthWest().lng(),
|
|
y: tmp.getSouthWest().lat()
|
|
});
|
|
|
|
verts.push({ // NW corner (again, for closure)
|
|
x: tmp.getSouthWest().lng(),
|
|
y: tmp.getNorthEast().lat()
|
|
});
|
|
|
|
response = {
|
|
type: 'polygon',
|
|
isRectangle: true,
|
|
components: [verts]
|
|
};
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
// google.maps.Data Geometry Types /////////////////////////////////////////////////////
|
|
|
|
// google.maps.Data.Feature /////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Data.Feature) {
|
|
return this.deconstruct.call(this, obj.getGeometry());
|
|
}
|
|
|
|
// google.maps.Data.Point /////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Data.Point) {
|
|
//console.log('It is a google.maps.Data.Point');
|
|
response = {
|
|
type: 'point',
|
|
components: [{
|
|
x: obj.get().lng(),
|
|
y: obj.get().lat()
|
|
}]
|
|
};
|
|
return response;
|
|
}
|
|
|
|
// google.maps.Data.LineString /////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Data.LineString) {
|
|
verts = [];
|
|
//console.log('It is a google.maps.Data.LineString');
|
|
for (i = 0; i < obj.getLength(); i += 1) {
|
|
vertex = obj.getAt(i);
|
|
verts.push({
|
|
x: vertex.lng(),
|
|
y: vertex.lat()
|
|
});
|
|
}
|
|
response = {
|
|
type: 'linestring',
|
|
components: verts
|
|
};
|
|
return response;
|
|
}
|
|
|
|
// google.maps.Data.Polygon /////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Data.Polygon) {
|
|
var rings = [];
|
|
//console.log('It is a google.maps.Data.Polygon');
|
|
for (i = 0; i < obj.getLength(); i += 1) { // For each ring...
|
|
ring = obj.getAt(i);
|
|
var verts = [];
|
|
for (j = 0; j < ring.getLength(); j += 1) { // For each vertex...
|
|
vertex = ring.getAt(j);
|
|
verts.push({
|
|
x: vertex.lng(),
|
|
y: vertex.lat()
|
|
});
|
|
}
|
|
verts.push({
|
|
x: ring.getAt(0).lng(),
|
|
y: ring.getAt(0).lat()
|
|
});
|
|
|
|
rings.push(verts);
|
|
}
|
|
response = {
|
|
type: 'polygon',
|
|
components: rings
|
|
};
|
|
|
|
return response;
|
|
}
|
|
|
|
// google.maps.Data.MultiPoint /////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Data.MultiPoint) {
|
|
verts = [];
|
|
for (i = 0; i < obj.getLength(); i += 1) {
|
|
vertex = obj.getAt(i);
|
|
verts.push([{
|
|
x: vertex.lng(),
|
|
y: vertex.lat()
|
|
}]);
|
|
}
|
|
response = {
|
|
type: 'multipoint',
|
|
components: verts
|
|
};
|
|
return response;
|
|
}
|
|
|
|
// google.maps.Data.MultiLineString /////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Data.MultiLineString) {
|
|
linestrings = []
|
|
for (i = 0; i < obj.getLength(); i += 1) {
|
|
verts = [];
|
|
var linestring = obj.getAt(i);
|
|
for (j = 0; j < linestring.getLength(); j += 1) {
|
|
vertex = linestring.getAt(j);
|
|
verts.push({
|
|
x: vertex.lng(),
|
|
y: vertex.lat()
|
|
});
|
|
}
|
|
linestrings.push(verts);
|
|
}
|
|
response = {
|
|
type: 'multilinestring',
|
|
components: linestrings
|
|
};
|
|
return response;
|
|
}
|
|
|
|
// google.maps.Data.MultiPolygon /////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Data.MultiPolygon) {
|
|
|
|
var polygons = [];
|
|
|
|
//console.log('It is a google.maps.Data.MultiPolygon');
|
|
for (k = 0; k < obj.getLength(); k += 1) { // For each multipolygon
|
|
var polygon = obj.getAt(k);
|
|
var rings = [];
|
|
for (i = 0; i < polygon.getLength(); i += 1) { // For each ring...
|
|
ring = polygon.getAt(i);
|
|
var verts = [];
|
|
for (j = 0; j < ring.getLength(); j += 1) { // For each vertex...
|
|
vertex = ring.getAt(j);
|
|
verts.push({
|
|
x: vertex.lng(),
|
|
y: vertex.lat()
|
|
});
|
|
}
|
|
verts.push({
|
|
x: ring.getAt(0).lng(),
|
|
y: ring.getAt(0).lat()
|
|
});
|
|
|
|
rings.push(verts);
|
|
}
|
|
polygons.push(rings);
|
|
}
|
|
|
|
response = {
|
|
type: 'multipolygon',
|
|
components: polygons
|
|
};
|
|
return response;
|
|
}
|
|
|
|
// google.maps.Data.GeometryCollection /////////////////////////////////////////////////////
|
|
if (obj.constructor === google.maps.Data.GeometryCollection) {
|
|
|
|
var objects = [];
|
|
for (k = 0; k < obj.getLength(); k += 1) { // For each multipolygon
|
|
var object = obj.getAt(k);
|
|
objects.push(this.deconstruct.call(this, object));
|
|
}
|
|
//console.log('It is a google.maps.Data.GeometryCollection', objects);
|
|
response = {
|
|
type: 'geometrycollection',
|
|
components: objects
|
|
};
|
|
return response;
|
|
}
|
|
|
|
// Array ///////////////////////////////////////////////////////////////////
|
|
if (Wkt.isArray(obj)) {
|
|
features = [];
|
|
|
|
for (i = 0; i < obj.length; i += 1) {
|
|
features.push(this.deconstruct.call(this, obj[i], true));
|
|
}
|
|
|
|
response = {
|
|
|
|
type: (function () {
|
|
var k, type = obj[0].constructor;
|
|
|
|
for (k = 0; k < obj.length; k += 1) {
|
|
// Check that all items have the same constructor as the first item
|
|
if (obj[k].constructor !== type) {
|
|
// If they don't, type is heterogeneous geometry collection
|
|
return 'geometrycollection';
|
|
}
|
|
}
|
|
|
|
switch (type) {
|
|
case google.maps.Marker:
|
|
return 'multipoint';
|
|
case google.maps.Polyline:
|
|
return 'multilinestring';
|
|
case google.maps.Polygon:
|
|
return 'multipolygon';
|
|
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: comps
|
|
};
|
|
}())
|
|
|
|
};
|
|
response.components = response.components.comps;
|
|
return response;
|
|
|
|
}
|
|
|
|
console.log('The passed object does not have any recognizable properties.');
|
|
|
|
};
|
|
|
|
return Wkt;
|
|
}));
|