/** * Author : René-Luc D'Hont * * Copyright 2010 3liz SARL. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /** * Copyright 2005-2010 OpenLayers Contributors, released under the Clear BSD * license. Please see http://svn.openlayers.org/trunk/openlayers/license.txt * for the full text of the license. */ /** * Class: ZOO */ ZOO = { /** * Constant: SERVICE_ACCEPTED * {Integer} used for */ SERVICE_ACCEPTED: 0, /** * Constant: SERVICE_STARTED * {Integer} used for */ SERVICE_STARTED: 1, /** * Constant: SERVICE_PAUSED * {Integer} used for */ SERVICE_PAUSED: 2, /** * Constant: SERVICE_SUCCEEDED * {Integer} used for */ SERVICE_SUCCEEDED: 3, /** * Constant: SERVICE_FAILED * {Integer} used for */ SERVICE_FAILED: 4, /** * Function: removeItem * Remove an object from an array. Iterates through the array * to find the item, then removes it. * * Parameters: * array - {Array} * item - {Object} * * Return * {Array} A reference to the array */ removeItem: function(array, item) { for(var i = array.length - 1; i >= 0; i--) { if(array[i] == item) { array.splice(i,1); } } return array; }, /** * Function: indexOf * * Parameters: * array - {Array} * obj - {Object} * * Returns: * {Integer} The index at, which the first object was found in the array. * If not found, returns -1. */ indexOf: function(array, obj) { for(var i=0, len=array.length; i} (or any object with both .x, .y properties) * p2 - {} (or any object with both .x, .y properties) * * Returns: * {Float} The distance (in km) between the two input points as measured on an * ellipsoid. Note that the input point objects must be in geographic * coordinates (decimal degrees) and the return distance is in kilometers. */ distVincenty: function(p1, p2) { var a = 6378137, b = 6356752.3142, f = 1/298.257223563; var L = ZOO.rad(p2.x - p1.y); var U1 = Math.atan((1-f) * Math.tan(ZOO.rad(p1.y))); var U2 = Math.atan((1-f) * Math.tan(ZOO.rad(p2.y))); var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1); var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2); var lambda = L, lambdaP = 2*Math.PI; var iterLimit = 20; while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) { var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda); var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) + (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda)); if (sinSigma==0) { return 0; // co-incident points } var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda; var sigma = Math.atan2(sinSigma, cosSigma); var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma); var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha); var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha; var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha)); lambdaP = lambda; lambda = L + (1-C) * f * Math.sin(alpha) * (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM))); } if (iterLimit==0) { return NaN; // formula failed to converge } var uSq = cosSqAlpha * (a*a - b*b) / (b*b); var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq))); var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq))); var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)- B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM))); var s = b*A*(sigma-deltaSigma); var d = s.toFixed(3)/1000; // round to 1mm precision return d; }, /** * Function: Class * Method used to create ZOO classes. Includes support for * multiple inheritance. */ Class: function() { var Class = function() { this.initialize.apply(this, arguments); }; var extended = {}; var parent; for(var i=0; i 1) { var newArgs = [C, P].concat( Array.prototype.slice.call(arguments).slice(1, len-1), F); ZOO.inherit.apply(null, newArgs); } else { C.prototype = F; } return C; }; /** * Function: create * Function for creating CLASS */ ZOO.Class.create = function() { return function() { if (arguments && arguments[0] != ZOO.Class.isPrototype) { this.initialize.apply(this, arguments); } }; }; /** * Function: inherit * Function for inheriting CLASS */ ZOO.Class.inherit = function (P) { var C = function() { P.call(this); }; var newArgs = [C].concat(Array.prototype.slice.call(arguments)); ZOO.inherit.apply(null, newArgs); return C.prototype; }; /** * Function: inherit * Function for inheriting CLASS */ ZOO.inherit = function(C, P) { var F = function() {}; F.prototype = P.prototype; C.prototype = new F; var i, l, o; for(i=2, l=arguments.length; i 0) { var separator = (url.indexOf('?') > -1) ? '&' : '?'; url += separator + paramString; } return ZOORequest('GET',url); }, /** * Function: POST * Send an HTTP POST request. * * Parameters: * url - {String} The URL to request. * body - {String} The request's body to send. * headers - {Object} A key-value object of headers to push to * the request's head * * Returns: * {String} Request result. */ Post: function(url,body,headers) { if(!(headers instanceof Array)) { var headersArray = []; for (var name in headers) { headersArray.push(name+': '+headers[name]); } headers = headersArray; } return ZOORequest('POST',url,body,headers); } }; /** * Class: ZOO.Bounds * Instances of this class represent bounding boxes. Data stored as left, * bottom, right, top floats. All values are initialized to null, * however, you should make sure you set them before using the bounds * for anything. */ ZOO.Bounds = ZOO.Class({ /** * Property: left * {Number} Minimum horizontal coordinate. */ left: null, /** * Property: bottom * {Number} Minimum vertical coordinate. */ bottom: null, /** * Property: right * {Number} Maximum horizontal coordinate. */ right: null, /** * Property: top * {Number} Maximum vertical coordinate. */ top: null, /** * Constructor: ZOO.Bounds * Construct a new bounds object. * * Parameters: * left - {Number} The left bounds of the box. Note that for width * calculations, this is assumed to be less than the right value. * bottom - {Number} The bottom bounds of the box. Note that for height * calculations, this is assumed to be more than the top value. * right - {Number} The right bounds. * top - {Number} The top bounds. */ initialize: function(left, bottom, right, top) { if (left != null) this.left = parseFloat(left); if (bottom != null) this.bottom = parseFloat(bottom); if (right != null) this.right = parseFloat(right); if (top != null) this.top = parseFloat(top); }, /** * Method: clone * Create a cloned instance of this bounds. * * Returns: * {} A fresh copy of the bounds */ clone:function() { return new ZOO.Bounds(this.left, this.bottom, this.right, this.top); }, /** * Method: equals * Test a two bounds for equivalence. * * Parameters: * bounds - {} * * Returns: * {Boolean} The passed-in bounds object has the same left, * right, top, bottom components as this. Note that if bounds * passed in is null, returns false. */ equals:function(bounds) { var equals = false; if (bounds != null) equals = ((this.left == bounds.left) && (this.right == bounds.right) && (this.top == bounds.top) && (this.bottom == bounds.bottom)); return equals; }, /** * Method: toString * * Returns: * {String} String representation of bounds object. * (ex."left-bottom=(5,42) right-top=(10,45)") */ toString:function() { return ( "left-bottom=(" + this.left + "," + this.bottom + ")" + " right-top=(" + this.right + "," + this.top + ")" ); }, /** * APIMethod: toArray * * Returns: * {Array} array of left, bottom, right, top */ toArray: function() { return [this.left, this.bottom, this.right, this.top]; }, /** * Method: toBBOX * * Parameters: * decimal - {Integer} How many significant digits in the bbox coords? * Default is 6 * * Returns: * {String} Simple String representation of bounds object. * (ex. "5,42,10,45") */ toBBOX:function(decimal) { if (decimal== null) decimal = 6; var mult = Math.pow(10, decimal); var bbox = Math.round(this.left * mult) / mult + "," + Math.round(this.bottom * mult) / mult + "," + Math.round(this.right * mult) / mult + "," + Math.round(this.top * mult) / mult; return bbox; }, /** * Method: toGeometry * Create a new polygon geometry based on this bounds. * * Returns: * {} A new polygon with the coordinates * of this bounds. */ toGeometry: function() { return new ZOO.Geometry.Polygon([ new ZOO.Geometry.LinearRing([ new ZOO.Geometry.Point(this.left, this.bottom), new ZOO.Geometry.Point(this.right, this.bottom), new ZOO.Geometry.Point(this.right, this.top), new ZOO.Geometry.Point(this.left, this.top) ]) ]); }, /** * Method: getWidth * * Returns: * {Float} The width of the bounds */ getWidth:function() { return (this.right - this.left); }, /** * Method: getHeight * * Returns: * {Float} The height of the bounds (top minus bottom). */ getHeight:function() { return (this.top - this.bottom); }, /** * Method: add * * Parameters: * x - {Float} * y - {Float} * * Returns: * {} A new bounds whose coordinates are the same as * this, but shifted by the passed-in x and y values. */ add:function(x, y) { if ( (x == null) || (y == null) ) return null; return new ZOO.Bounds(this.left + x, this.bottom + y, this.right + x, this.top + y); }, /** * Method: extend * Extend the bounds to include the point, lonlat, or bounds specified. * Note, this function assumes that left < right and bottom < top. * * Parameters: * object - {Object} Can be Point, or Bounds */ extend:function(object) { var bounds = null; if (object) { // clear cached center location switch(object.CLASS_NAME) { case "ZOO.Geometry.Point": bounds = new ZOO.Bounds(object.x, object.y, object.x, object.y); break; case "ZOO.Bounds": bounds = object; break; } if (bounds) { if ( (this.left == null) || (bounds.left < this.left)) this.left = bounds.left; if ( (this.bottom == null) || (bounds.bottom < this.bottom) ) this.bottom = bounds.bottom; if ( (this.right == null) || (bounds.right > this.right) ) this.right = bounds.right; if ( (this.top == null) || (bounds.top > this.top) ) this.top = bounds.top; } } }, /** * APIMethod: contains * * Parameters: * x - {Float} * y - {Float} * inclusive - {Boolean} Whether or not to include the border. * Default is true. * * Returns: * {Boolean} Whether or not the passed-in coordinates are within this * bounds. */ contains:function(x, y, inclusive) { //set default if (inclusive == null) inclusive = true; if (x == null || y == null) return false; x = parseFloat(x); y = parseFloat(y); var contains = false; if (inclusive) contains = ((x >= this.left) && (x <= this.right) && (y >= this.bottom) && (y <= this.top)); else contains = ((x > this.left) && (x < this.right) && (y > this.bottom) && (y < this.top)); return contains; }, /** * Method: intersectsBounds * Determine whether the target bounds intersects this bounds. Bounds are * considered intersecting if any of their edges intersect or if one * bounds contains the other. * * Parameters: * bounds - {} The target bounds. * inclusive - {Boolean} Treat coincident borders as intersecting. Default * is true. If false, bounds that do not overlap but only touch at the * border will not be considered as intersecting. * * Returns: * {Boolean} The passed-in bounds object intersects this bounds. */ intersectsBounds:function(bounds, inclusive) { if (inclusive == null) inclusive = true; var intersects = false; var mightTouch = ( this.left == bounds.right || this.right == bounds.left || this.top == bounds.bottom || this.bottom == bounds.top ); if (inclusive || !mightTouch) { var inBottom = ( ((bounds.bottom >= this.bottom) && (bounds.bottom <= this.top)) || ((this.bottom >= bounds.bottom) && (this.bottom <= bounds.top)) ); var inTop = ( ((bounds.top >= this.bottom) && (bounds.top <= this.top)) || ((this.top > bounds.bottom) && (this.top < bounds.top)) ); var inLeft = ( ((bounds.left >= this.left) && (bounds.left <= this.right)) || ((this.left >= bounds.left) && (this.left <= bounds.right)) ); var inRight = ( ((bounds.right >= this.left) && (bounds.right <= this.right)) || ((this.right >= bounds.left) && (this.right <= bounds.right)) ); intersects = ((inBottom || inTop) && (inLeft || inRight)); } return intersects; }, /** * Method: containsBounds * Determine whether the target bounds is contained within this bounds. * * bounds - {} The target bounds. * partial - {Boolean} If any of the target corners is within this bounds * consider the bounds contained. Default is false. If true, the * entire target bounds must be contained within this bounds. * inclusive - {Boolean} Treat shared edges as contained. Default is * true. * * Returns: * {Boolean} The passed-in bounds object is contained within this bounds. */ containsBounds:function(bounds, partial, inclusive) { if (partial == null) partial = false; if (inclusive == null) inclusive = true; var bottomLeft = this.contains(bounds.left, bounds.bottom, inclusive); var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive); var topLeft = this.contains(bounds.left, bounds.top, inclusive); var topRight = this.contains(bounds.right, bounds.top, inclusive); return (partial) ? (bottomLeft || bottomRight || topLeft || topRight) : (bottomLeft && bottomRight && topLeft && topRight); }, CLASS_NAME: 'ZOO.Bounds' }); /** * Class: ZOO.Projection * Class for coordinate transforms between coordinate systems. * Depends on the zoo-proj4js library. zoo-proj4js library * is loaded by the ZOO Kernel with zoo-api. */ ZOO.Projection = ZOO.Class({ /** * Property: proj * {Object} Proj4js.Proj instance. */ proj: null, /** * Property: projCode * {String} */ projCode: null, /** * Constructor: ZOO.Projection * This class offers several methods for interacting with a wrapped * zoo-pro4js projection object. * * Parameters: * projCode - {String} A string identifying the Well Known Identifier for * the projection. * options - {Object} An optional object to set additional properties. * * Returns: * {} A projection object. */ initialize: function(projCode, options) { ZOO.extend(this, options); this.projCode = projCode; if (Proj4js) { this.proj = new Proj4js.Proj(projCode); } }, /** * Method: getCode * Get the string SRS code. * * Returns: * {String} The SRS code. */ getCode: function() { return this.proj ? this.proj.srsCode : this.projCode; }, /** * Method: getUnits * Get the units string for the projection -- returns null if * zoo-proj4js is not available. * * Returns: * {String} The units abbreviation. */ getUnits: function() { return this.proj ? this.proj.units : null; }, /** * Method: toString * Convert projection to string (getCode wrapper). * * Returns: * {String} The projection code. */ toString: function() { return this.getCode(); }, /** * Method: equals * Test equality of two projection instances. Determines equality based * soley on the projection code. * * Returns: * {Boolean} The two projections are equivalent. */ equals: function(projection) { if (projection && projection.getCode) return this.getCode() == projection.getCode(); else return false; }, /* Method: destroy * Destroy projection object. */ destroy: function() { this.proj = null; this.projCode = null; }, CLASS_NAME: 'ZOO.Projection' }); /** * Method: transform * Transform a point coordinate from one projection to another. Note that * the input point is transformed in place. * * Parameters: * point - {{ZOO.Geometry.Point> | Object} An object with x and y * properties representing coordinates in those dimensions. * sourceProj - {ZOO.Projection} Source map coordinate system * destProj - {ZOO.Projection} Destination map coordinate system * * Returns: * point - {object} A transformed coordinate. The original point is modified. */ ZOO.Projection.transform = function(point, source, dest) { if (source.proj && dest.proj) point = Proj4js.transform(source.proj, dest.proj, point); return point; }; /** * Class: ZOO.Format * Base class for format reading/writing a variety of formats. Subclasses * of ZOO.Format are expected to have read and write methods. */ ZOO.Format = ZOO.Class({ /** * Property: options * {Object} A reference to options passed to the constructor. */ options:null, /** * Property: externalProjection * {} When passed a externalProjection and * internalProjection, the format will reproject the geometries it * reads or writes. The externalProjection is the projection used by * the content which is passed into read or which comes out of write. * In order to reproject, a projection transformation function for the * specified projections must be available. This support is provided * via zoo-proj4js. */ externalProjection: null, /** * Property: internalProjection * {} When passed a externalProjection and * internalProjection, the format will reproject the geometries it * reads or writes. The internalProjection is the projection used by * the geometries which are returned by read or which are passed into * write. In order to reproject, a projection transformation function * for the specified projections must be available. This support is * provided via zoo-proj4js. */ internalProjection: null, /** * Property: data * {Object} When is true, this is the parsed string sent to * . */ data: null, /** * Property: keepData * {Object} Maintain a reference () to the most recently read data. * Default is false. */ keepData: false, /** * Constructor: ZOO.Format * Instances of this class are not useful. See one of the subclasses. * * Parameters: * options - {Object} An optional object with properties to set on the * format * * Valid options: * keepData - {Boolean} If true, upon , the data property will be * set to the parsed object (e.g. the json or xml object). * * Returns: * An instance of ZOO.Format */ initialize: function(options) { ZOO.extend(this, options); this.options = options; }, /** * Method: destroy * Clean up. */ destroy: function() { }, /** * Method: read * Read data from a string, and return an object whose type depends on the * subclass. * * Parameters: * data - {string} Data to read/parse. * * Returns: * Depends on the subclass */ read: function(data) { }, /** * Method: write * Accept an object, and return a string. * * Parameters: * object - {Object} Object to be serialized * * Returns: * {String} A string representation of the object. */ write: function(data) { }, CLASS_NAME: 'ZOO.Format' }); /** * Class: ZOO.Format.WKT * Class for reading and writing Well-Known Text. Create a new instance * with the constructor. * * Inherits from: * - */ ZOO.Format.WKT = ZOO.Class(ZOO.Format, { /** * Constructor: ZOO.Format.WKT * Create a new parser for WKT * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance * * Returns: * {} A new WKT parser. */ initialize: function(options) { this.regExes = { 'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/, 'spaces': /\s+/, 'parenComma': /\)\s*,\s*\(/, 'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here 'trimParens': /^\s*\(?(.*?)\)?\s*$/ }; ZOO.Format.prototype.initialize.apply(this, [options]); }, /** * Method: read * Deserialize a WKT string and return a vector feature or an * array of vector features. Supports WKT for POINT, * MULTIPOINT, LINESTRING, MULTILINESTRING, POLYGON, * MULTIPOLYGON, and GEOMETRYCOLLECTION. * * Parameters: * wkt - {String} A WKT string * * Returns: * {|Array} A feature or array of features for * GEOMETRYCOLLECTION WKT. */ read: function(wkt) { var features, type, str; var matches = this.regExes.typeStr.exec(wkt); if(matches) { type = matches[1].toLowerCase(); str = matches[2]; if(this.parse[type]) { features = this.parse[type].apply(this, [str]); } if (this.internalProjection && this.externalProjection) { if (features && features.CLASS_NAME == "ZOO.Feature") { features.geometry.transform(this.externalProjection, this.internalProjection); } else if (features && type != "geometrycollection" && typeof features == "object") { for (var i=0, len=features.length; i|Array} A feature or array of * features * * Returns: * {String} The WKT string representation of the input geometries */ write: function(features) { var collection, geometry, type, data, isCollection; if(features.constructor == Array) { collection = features; isCollection = true; } else { collection = [features]; isCollection = false; } var pieces = []; if(isCollection) pieces.push('GEOMETRYCOLLECTION('); for(var i=0, len=collection.length; i0) pieces.push(','); geometry = collection[i].geometry; type = geometry.CLASS_NAME.split('.')[2].toLowerCase(); if(!this.extract[type]) return null; if (this.internalProjection && this.externalProjection) { geometry = geometry.clone(); geometry.transform(this.internalProjection, this.externalProjection); } data = this.extract[type].apply(this, [geometry]); pieces.push(type.toUpperCase() + '(' + data + ')'); } if(isCollection) pieces.push(')'); return pieces.join(''); }, /** * Property: extract * Object with properties corresponding to the geometry types. * Property values are functions that do the actual data extraction. */ extract: { /** * Return a space delimited string of point coordinates. * @param {} point * @returns {String} A string of coordinates representing the point */ 'point': function(point) { return point.x + ' ' + point.y; }, /** * Return a comma delimited string of point coordinates from a multipoint. * @param {} multipoint * @returns {String} A string of point coordinate strings representing * the multipoint */ 'multipoint': function(multipoint) { var array = []; for(var i=0, len=multipoint.components.length; i} linestring * @returns {String} A string of point coordinate strings representing * the linestring */ 'linestring': function(linestring) { var array = []; for(var i=0, len=linestring.components.length; i} multilinestring * @returns {String} A string of of linestring strings representing * the multilinestring */ 'multilinestring': function(multilinestring) { var array = []; for(var i=0, len=multilinestring.components.length; i} polygon * @returns {String} An array of linear ring arrays representing the polygon */ 'polygon': function(polygon) { var array = []; for(var i=0, len=polygon.components.length; i} multipolygon * @returns {Array} An array of polygon arrays representing * the multipolygon */ 'multipolygon': function(multipolygon) { var array = []; for(var i=0, len=multipolygon.components.length; i} A point feature */ 'point': function(str) { var coords = ZOO.String.trim(str).split(this.regExes.spaces); return new ZOO.Feature( new ZOO.Geometry.Point(coords[0], coords[1]) ); }, /** * Method: parse.multipoint * Return a multipoint feature given a multipoint WKT fragment. * * Parameters: * str - {String} A WKT fragment representing the multipoint * * Returns: * {} A multipoint feature */ 'multipoint': function(str) { var points = ZOO.String.trim(str).split(','); var components = []; for(var i=0, len=points.length; i} A linestring feature */ 'linestring': function(str) { var points = ZOO.String.trim(str).split(','); var components = []; for(var i=0, len=points.length; i} A multilinestring feature */ 'multilinestring': function(str) { var line; var lines = ZOO.String.trim(str).split(this.regExes.parenComma); var components = []; for(var i=0, len=lines.length; i} A polygon feature */ 'polygon': function(str) { var ring, linestring, linearring; var rings = ZOO.String.trim(str).split(this.regExes.parenComma); var components = []; for(var i=0, len=rings.length; i} A multipolygon feature */ 'multipolygon': function(str) { var polygon; var polygons = ZOO.String.trim(str).split(this.regExes.doubleParenComma); var components = []; for(var i=0, len=polygons.length; i constructor. * * Inherits from: * - */ ZOO.Format.JSON = ZOO.Class(ZOO.Format, { /** * Property: indent * {String} For "pretty" printing, the indent string will be used once for * each indentation level. */ indent: " ", /** * Property: space * {String} For "pretty" printing, the space string will be used after * the ":" separating a name/value pair. */ space: " ", /** * Property: newline * {String} For "pretty" printing, the newline string will be used at the * end of each name/value pair or array item. */ newline: "\n", /** * Property: level * {Integer} For "pretty" printing, this is incremented/decremented during * serialization. */ level: 0, /** * Property: pretty * {Boolean} Serialize with extra whitespace for structure. This is set * by the method. */ pretty: false, /** * Constructor: ZOO.Format.JSON * Create a new parser for JSON. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { ZOO.Format.prototype.initialize.apply(this, [options]); }, /** * Method: read * Deserialize a json string. * * Parameters: * json - {String} A JSON string * filter - {Function} A function which will be called for every key and * value at every level of the final result. Each value will be * replaced by the result of the filter function. This can be used to * reform generic objects into instances of classes, or to transform * date strings into Date objects. * * Returns: * {Object} An object, array, string, or number . */ read: function(json, filter) { /** * Parsing happens in three stages. In the first stage, we run the text * against a regular expression which looks for non-JSON * characters. We are especially concerned with '()' and 'new' * because they can cause invocation, and '=' because it can cause * mutation. But just to be safe, we will reject all unexpected * characters. */ try { if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@'). replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { /** * In the second stage we use the eval function to compile the * text into a JavaScript structure. The '{' operator is * subject to a syntactic ambiguity in JavaScript - it can * begin a block or an object literal. We wrap the text in * parens to eliminate the ambiguity. */ var object = eval('(' + json + ')'); /** * In the optional third stage, we recursively walk the new * structure, passing each name/value pair to a filter * function for possible transformation. */ if(typeof filter === 'function') { function walk(k, v) { if(v && typeof v === 'object') { for(var i in v) { if(v.hasOwnProperty(i)) { v[i] = walk(i, v[i]); } } } return filter(k, v); } object = walk('', object); } if(this.keepData) { this.data = object; } return object; } } catch(e) { // Fall through if the regexp test fails. } return null; }, /** * Method: write * Serialize an object into a JSON string. * * Parameters: * value - {String} The object, array, string, number, boolean or date * to be serialized. * pretty - {Boolean} Structure the output with newlines and indentation. * Default is false. * * Returns: * {String} The JSON string representation of the input value. */ write: function(value, pretty) { this.pretty = !!pretty; var json = null; var type = typeof value; if(this.serialize[type]) { try { json = this.serialize[type].apply(this, [value]); } catch(err) { //OpenLayers.Console.error("Trouble serializing: " + err); } } return json; }, /** * Method: writeIndent * Output an indentation string depending on the indentation level. * * Returns: * {String} An appropriate indentation string. */ writeIndent: function() { var pieces = []; if(this.pretty) { for(var i=0; i 0) pieces.push(','); pieces.push(this.writeNewline(), this.writeIndent(), json); } } this.level -= 1; pieces.push(this.writeNewline(), this.writeIndent(), ']'); return pieces.join(''); }, /** * Method: serialize.string * Transform a string into a JSON string. * * Parameters: * string - {String} The string to be serialized * * Returns: * {String} A JSON string representing the string. */ 'string': function(string) { var m = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' }; if(/["\\\x00-\x1f]/.test(string)) { return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) { var c = m[b]; if(c) return c; c = b.charCodeAt(); return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16); }) + '"'; } return '"' + string + '"'; }, /** * Method: serialize.number * Transform a number into a JSON string. * * Parameters: * number - {Number} The number to be serialized. * * Returns: * {String} A JSON string representing the number. */ 'number': function(number) { return isFinite(number) ? String(number) : "null"; }, /** * Method: serialize.boolean * Transform a boolean into a JSON string. * * Parameters: * bool - {Boolean} The boolean to be serialized. * * Returns: * {String} A JSON string representing the boolean. */ 'boolean': function(bool) { return String(bool); }, /** * Method: serialize.date * Transform a date into a JSON string. * * Parameters: * date - {Date} The date to be serialized. * * Returns: * {String} A JSON string representing the date. */ 'date': function(date) { function format(number) { // Format integers to have at least two digits. return (number < 10) ? '0' + number : number; } return '"' + date.getFullYear() + '-' + format(date.getMonth() + 1) + '-' + format(date.getDate()) + 'T' + format(date.getHours()) + ':' + format(date.getMinutes()) + ':' + format(date.getSeconds()) + '"'; } }, CLASS_NAME: 'ZOO.Format.JSON' }); /** * Class: ZOO.Format.GeoJSON * Read and write GeoJSON. Create a new parser with the * constructor. * * Inherits from: * - */ ZOO.Format.GeoJSON = ZOO.Class(ZOO.Format.JSON, { /** * Constructor: ZOO.Format.GeoJSON * Create a new parser for GeoJSON. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { ZOO.Format.JSON.prototype.initialize.apply(this, [options]); }, /** * Method: read * Deserialize a GeoJSON string. * * Parameters: * json - {String} A GeoJSON string * type - {String} Optional string that determines the structure of * the output. Supported values are "Geometry", "Feature", and * "FeatureCollection". If absent or null, a default of * "FeatureCollection" is assumed. * filter - {Function} A function which will be called for every key and * value at every level of the final result. Each value will be * replaced by the result of the filter function. This can be used to * reform generic objects into instances of classes, or to transform * date strings into Date objects. * * Returns: * {Object} The return depends on the value of the type argument. If type * is "FeatureCollection" (the default), the return will be an array * of . If type is "Geometry", the input json * must represent a single geometry, and the return will be an * . If type is "Feature", the input json must * represent a single feature, and the return will be an * . */ read: function(json, type, filter) { type = (type) ? type : "FeatureCollection"; var results = null; var obj = null; if (typeof json == "string") obj = ZOO.Format.JSON.prototype.read.apply(this,[json, filter]); else obj = json; if(!obj) { //ZOO.Console.error("Bad JSON: " + json); } else if(typeof(obj.type) != "string") { //ZOO.Console.error("Bad GeoJSON - no type: " + json); } else if(this.isValidType(obj, type)) { switch(type) { case "Geometry": try { results = this.parseGeometry(obj); } catch(err) { //ZOO.Console.error(err); } break; case "Feature": try { results = this.parseFeature(obj); results.type = "Feature"; } catch(err) { //ZOO.Console.error(err); } break; case "FeatureCollection": // for type FeatureCollection, we allow input to be any type results = []; switch(obj.type) { case "Feature": try { results.push(this.parseFeature(obj)); } catch(err) { results = null; //ZOO.Console.error(err); } break; case "FeatureCollection": for(var i=0, len=obj.features.length; i. * * Parameters: * obj - {Object} An object created from a GeoJSON object * * Returns: * {} A feature. */ parseFeature: function(obj) { var feature, geometry, attributes, bbox; attributes = (obj.properties) ? obj.properties : {}; bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox; try { geometry = this.parseGeometry(obj.geometry); } catch(err) { // deal with bad geometries throw err; } feature = new ZOO.Feature(geometry, attributes); if(bbox) feature.bounds = ZOO.Bounds.fromArray(bbox); if(obj.id) feature.fid = obj.id; return feature; }, /** * Method: parseGeometry * Convert a geometry object from GeoJSON into an . * * Parameters: * obj - {Object} An object created from a GeoJSON object * * Returns: * {} A geometry. */ parseGeometry: function(obj) { if (obj == null) return null; var geometry, collection = false; if(obj.type == "GeometryCollection") { if(!(obj.geometries instanceof Array)) { throw "GeometryCollection must have geometries array: " + obj; } var numGeom = obj.geometries.length; var components = new Array(numGeom); for(var i=0; i. * * Parameters: * array - {Object} The coordinates array from the GeoJSON fragment. * * Returns: * {} A geometry. */ "point": function(array) { if(array.length != 2) { throw "Only 2D points are supported: " + array; } return new ZOO.Geometry.Point(array[0], array[1]); }, /** * Method: parseCoords.multipoint * Convert a coordinate array from GeoJSON into an * . * * Parameters: * array - {Object} The coordinates array from the GeoJSON fragment. * * Returns: * {} A geometry. */ "multipoint": function(array) { var points = []; var p = null; for(var i=0, len=array.length; i. * * Parameters: * array - {Object} The coordinates array from the GeoJSON fragment. * * Returns: * {} A geometry. */ "linestring": function(array) { var points = []; var p = null; for(var i=0, len=array.length; i. * * Parameters: * array - {Object} The coordinates array from the GeoJSON fragment. * * Returns: * {} A geometry. */ "multilinestring": function(array) { var lines = []; var l = null; for(var i=0, len=array.length; i. * * Parameters: * array - {Object} The coordinates array from the GeoJSON fragment. * * Returns: * {} A geometry. */ "polygon": function(array) { var rings = []; var r, l; for(var i=0, len=array.length; i. * * Parameters: * array - {Object} The coordinates array from the GeoJSON fragment. * * Returns: * {} A geometry. */ "multipolygon": function(array) { var polys = []; var p = null; for(var i=0, len=array.length; i. * * Parameters: * array - {Object} The coordinates array from the GeoJSON fragment. * * Returns: * {} A geometry. */ "box": function(array) { if(array.length != 2) { throw "GeoJSON box coordinates must have 2 elements"; } return new ZOO.Geometry.Polygon([ new ZOO.Geometry.LinearRing([ new ZOO.Geometry.Point(array[0][0], array[0][1]), new ZOO.Geometry.Point(array[1][0], array[0][1]), new ZOO.Geometry.Point(array[1][0], array[1][1]), new ZOO.Geometry.Point(array[0][0], array[1][1]), new Z0O.Geometry.Point(array[0][0], array[0][1]) ]) ]); } }, /** * Method: write * Serialize a feature, geometry, array of features into a GeoJSON string. * * Parameters: * obj - {Object} An , , * or an array of features. * pretty - {Boolean} Structure the output with newlines and indentation. * Default is false. * * Returns: * {String} The GeoJSON string representation of the input geometry, * features, or array of features. */ write: function(obj, pretty) { var geojson = { "type": null }; if(obj instanceof Array) { geojson.type = "FeatureCollection"; var numFeatures = obj.length; geojson.features = new Array(numFeatures); for(var i=0; i} * * Returns: * {Object} An object which can be assigned to the crs property * of a GeoJSON object. */ createCRSObject: function(object) { //var proj = object.layer.projection.toString(); var proj = object.projection.toString(); var crs = {}; if (proj.match(/epsg:/i)) { var code = parseInt(proj.substring(proj.indexOf(":") + 1)); if (code == 4326) { crs = { "type": "OGC", "properties": { "urn": "urn:ogc:def:crs:OGC:1.3:CRS84" } }; } else { crs = { "type": "EPSG", "properties": { "code": code } }; } } return crs; }, /** * Property: extract * Object with properties corresponding to the GeoJSON types. * Property values are functions that do the actual value extraction. */ extract: { /** * Method: extract.feature * Return a partial GeoJSON object representing a single feature. * * Parameters: * feature - {} * * Returns: * {Object} An object representing the point. */ 'feature': function(feature) { var geom = this.extract.geometry.apply(this, [feature.geometry]); return { "type": "Feature", "id": feature.fid == null ? feature.id : feature.fid, "properties": feature.attributes, "geometry": geom }; }, /** * Method: extract.geometry * Return a GeoJSON object representing a single geometry. * * Parameters: * geometry - {} * * Returns: * {Object} An object representing the geometry. */ 'geometry': function(geometry) { if (geometry == null) return null; if (this.internalProjection && this.externalProjection) { geometry = geometry.clone(); geometry.transform(this.internalProjection, this.externalProjection); } var geometryType = geometry.CLASS_NAME.split('.')[2]; var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]); var json; if(geometryType == "Collection") json = { "type": "GeometryCollection", "geometries": data }; else json = { "type": geometryType, "coordinates": data }; return json; }, /** * Method: extract.point * Return an array of coordinates from a point. * * Parameters: * point - {} * * Returns: * {Array} An array of coordinates representing the point. */ 'point': function(point) { return [point.x, point.y]; }, /** * Method: extract.multipoint * Return an array of coordinates from a multipoint. * * Parameters: * multipoint - {} * * Returns: * {Array} An array of point coordinate arrays representing * the multipoint. */ 'multipoint': function(multipoint) { var array = []; for(var i=0, len=multipoint.components.length; i} * * Returns: * {Array} An array of coordinate arrays representing * the linestring. */ 'linestring': function(linestring) { var array = []; for(var i=0, len=linestring.components.length; i} * * Returns: * {Array} An array of linestring arrays representing * the multilinestring. */ 'multilinestring': function(multilinestring) { var array = []; for(var i=0, len=multilinestring.components.length; i} * * Returns: * {Array} An array of linear ring arrays representing the polygon. */ 'polygon': function(polygon) { var array = []; for(var i=0, len=polygon.components.length; i} * * Returns: * {Array} An array of polygon arrays representing * the multipolygon */ 'multipolygon': function(multipolygon) { var array = []; for(var i=0, len=multipolygon.components.length; i} * * Returns: * {Array} An array of geometry objects representing the geometry * collection. */ 'collection': function(collection) { var len = collection.components.length; var array = new Array(len); for(var i=0; i * constructor. * * Inherits from: * - */ ZOO.Format.KML = ZOO.Class(ZOO.Format, { /** * Property: kmlns * {String} KML Namespace to use. Defaults to 2.2 namespace. */ kmlns: "http://www.opengis.net/kml/2.2", /** * Property: foldersName * {String} Name of the folders. Default is "ZOO export". * If set to null, no name element will be created. */ foldersName: "ZOO export", /** * Property: foldersDesc * {String} Description of the folders. Default is "Exported on [date]." * If set to null, no description element will be created. */ foldersDesc: "Created on " + new Date(), /** * Property: placemarksDesc * {String} Name of the placemarks. Default is "No description available". */ placemarksDesc: "No description available", /** * Property: extractAttributes * {Boolean} Extract attributes from KML. Default is true. * Extracting styleUrls requires this to be set to true */ extractAttributes: true, /** * Constructor: ZOO.Format.KML * Create a new parser for KML. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { // compile regular expressions once instead of every time they are used this.regExes = { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g), kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/), kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/), straightBracket: (/\$\[(.*?)\]/g) }; // KML coordinates are always in longlat WGS84 this.externalProjection = new ZOO.Projection("EPSG:4326"); ZOO.Format.prototype.initialize.apply(this, [options]); }, /** * APIMethod: read * Read data from a string, and return a list of features. * * Parameters: * data - {String} data to read/parse. * * Returns: * {Array()} List of features. */ read: function(data) { this.features = []; data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, ""); data = new XML(data); var placemarks = data..*::Placemark; this.parseFeatures(placemarks); return this.features; }, /** * Method: parseFeatures * Loop through all Placemark nodes and parse them. * Will create a list of features * * Parameters: * nodes - {Array} of {E4XElement} data to read/parse. * options - {Object} Hash of options * */ parseFeatures: function(nodes) { var features = new Array(nodes.length()); for(var i=0, len=nodes.length(); i} A vector feature. */ parseFeature: function(node) { // only accept one geometry per feature - look for highest "order" var order = ["MultiGeometry", "Polygon", "LineString", "Point"]; var type, nodeList, geometry, parser; for(var i=0, len=order.length; i 0) { var parser = this.parseGeometry[type.toLowerCase()]; if(parser) { geometry = parser.apply(this, [nodeList[0]]); if (this.internalProjection && this.externalProjection) { geometry.transform(this.externalProjection, this.internalProjection); } } // stop looking for different geometry types break; } } // construct feature (optionally with attributes) var attributes; if(this.extractAttributes) { attributes = this.parseAttributes(node); } var feature = new ZOO.Feature(geometry, attributes); var fid = node.@id || node.@name; if(fid != null) feature.fid = fid; return feature; }, /** * Property: parseGeometry * Properties of this object are the functions that parse geometries based * on their type. */ parseGeometry: { /** * Method: parseGeometry.point * Given a KML node representing a point geometry, create a ZOO * point geometry. * * Parameters: * node - {E4XElement} A KML Point node. * * Returns: * {} A point geometry. */ 'point': function(node) { var coordString = node.*::coordinates.toString(); coordString = coordString.replace(this.regExes.removeSpace, ""); coords = coordString.split(","); var point = null; if(coords.length > 1) { // preserve third dimension if(coords.length == 2) { coords[2] = null; } point = new ZOO.Geometry.Point(coords[0], coords[1], coords[2]); } return point; }, /** * Method: parseGeometry.linestring * Given a KML node representing a linestring geometry, create a * ZOO linestring geometry. * * Parameters: * node - {E4XElement} A KML LineString node. * * Returns: * {} A linestring geometry. */ 'linestring': function(node, ring) { var line = null; var coordString = node.*::coordinates.toString(); coordString = coordString.replace(this.regExes.trimSpace, ""); coordString = coordString.replace(this.regExes.trimComma, ","); var pointList = coordString.split(this.regExes.splitSpace); var numPoints = pointList.length; var points = new Array(numPoints); var coords, numCoords; for(var i=0; i 1) { if(coords.length == 2) { coords[2] = null; } points[i] = new ZOO.Geometry.Point(coords[0], coords[1], coords[2]); } } if(numPoints) { if(ring) { line = new ZOO.Geometry.LinearRing(points); } else { line = new ZOO.Geometry.LineString(points); } } else { throw "Bad LineString coordinates: " + coordString; } return line; }, /** * Method: parseGeometry.polygon * Given a KML node representing a polygon geometry, create a * ZOO polygon geometry. * * Parameters: * node - {E4XElement} A KML Polygon node. * * Returns: * {} A polygon geometry. */ 'polygon': function(node) { var nodeList = node..*::LinearRing; var numRings = nodeList.length(); var components = new Array(numRings); if(numRings > 0) { // this assumes exterior ring first, inner rings after var ring; for(var i=0, len=nodeList.length(); i} A geometry collection. */ 'multigeometry': function(node) { var child, parser; var parts = []; var children = node.*::*; for(var i=0, len=children.length(); i 0) { attributes = this.parseExtendedData(edNodes[0]) } var child, grandchildren; var children = node.*::*; for(var i=0, len=children.length(); i 0) ed['value'] = valueNode[0].toString(); var nameNode = data.*::displayName; if (nameNode.length() > 0) ed['displayName'] = valueNode[0].toString(); attributes[key] = ed; } return attributes; }, /** * Method: write * Accept Feature Collection, and return a string. * * Parameters: * features - {Array(} An array of features. * * Returns: * {String} A KML string. */ write: function(features) { if(!(features instanceof Array)) features = [features]; var kml = new XML(''); var folder = kml.Document.Folder; folder.name = this.foldersName; folder.description = this.foldersDesc; for(var i=0, len=features.length; i} * * Returns: * {E4XElement} */ createPlacemark: function(feature) { var placemark = new XML(''); placemark.name = (feature.attributes.name) ? feature.attributes.name : feature.id; placemark.description = (feature.attributes.description) ? feature.attributes.description : this.placemarksDesc; if(feature.fid != null) placemark.@id = feature.fid; placemark.*[2] = this.buildGeometryNode(feature.geometry); return placemark; }, /** * Method: buildGeometryNode * Builds and returns a KML geometry node with the given geometry. * * Parameters: * geometry - {} * * Returns: * {E4XElement} */ buildGeometryNode: function(geometry) { if (this.internalProjection && this.externalProjection) { geometry = geometry.clone(); geometry.transform(this.internalProjection, this.externalProjection); } var className = geometry.CLASS_NAME; var type = className.substring(className.lastIndexOf(".") + 1); var builder = this.buildGeometry[type.toLowerCase()]; var node = null; if(builder) { node = builder.apply(this, [geometry]); } return node; }, /** * Property: buildGeometry * Object containing methods to do the actual geometry node building * based on geometry type. */ buildGeometry: { /** * Method: buildGeometry.point * Given a ZOO point geometry, create a KML point. * * Parameters: * geometry - {} A point geometry. * * Returns: * {E4XElement} A KML point node. */ 'point': function(geometry) { var kml = new XML(''); kml.coordinates = this.buildCoordinatesNode(geometry); return kml; }, /** * Method: buildGeometry.multipoint * Given a ZOO multipoint geometry, create a KML * GeometryCollection. * * Parameters: * geometry - {} A multipoint geometry. * * Returns: * {E4XElement} A KML GeometryCollection node. */ 'multipoint': function(geometry) { return this.buildGeometry.collection.apply(this, [geometry]); }, /** * Method: buildGeometry.linestring * Given a ZOO linestring geometry, create a KML linestring. * * Parameters: * geometry - {} A linestring geometry. * * Returns: * {E4XElement} A KML linestring node. */ 'linestring': function(geometry) { var kml = new XML(''); kml.coordinates = this.buildCoordinatesNode(geometry); return kml; }, /** * Method: buildGeometry.multilinestring * Given a ZOO multilinestring geometry, create a KML * GeometryCollection. * * Parameters: * geometry - {} A multilinestring geometry. * * Returns: * {E4XElement} A KML GeometryCollection node. */ 'multilinestring': function(geometry) { return this.buildGeometry.collection.apply(this, [geometry]); }, /** * Method: buildGeometry.linearring * Given a ZOO linearring geometry, create a KML linearring. * * Parameters: * geometry - {} A linearring geometry. * * Returns: * {E4XElement} A KML linearring node. */ 'linearring': function(geometry) { var kml = new XML(''); kml.coordinates = this.buildCoordinatesNode(geometry); return kml; }, /** * Method: buildGeometry.polygon * Given a ZOO polygon geometry, create a KML polygon. * * Parameters: * geometry - {} A polygon geometry. * * Returns: * {E4XElement} A KML polygon node. */ 'polygon': function(geometry) { var kml = new XML(''); var rings = geometry.components; var ringMember, ringGeom, type; for(var i=0, len=rings.length; i'); ringMember.LinearRing = this.buildGeometry.linearring.apply(this,[rings[i]]); kml.*[i] = ringMember; } return kml; }, /** * Method: buildGeometry.multipolygon * Given a ZOO multipolygon geometry, create a KML * GeometryCollection. * * Parameters: * geometry - {} A multipolygon geometry. * * Returns: * {E4XElement} A KML GeometryCollection node. */ 'multipolygon': function(geometry) { return this.buildGeometry.collection.apply(this, [geometry]); }, /** * Method: buildGeometry.collection * Given a ZOO geometry collection, create a KML MultiGeometry. * * Parameters: * geometry - {} A geometry collection. * * Returns: * {E4XElement} A KML MultiGeometry node. */ 'collection': function(geometry) { var kml = new XML(''); var child; for(var i=0, len=geometry.components.length; i... * * Parameters: * geometry - {} * * Return: * {E4XElement} */ buildCoordinatesNode: function(geometry) { var cooridnates = new XML(''); var points = geometry.components; if(points) { // LineString or LinearRing var point; var numPoints = points.length; var parts = new Array(numPoints); for(var i=0; i * constructor. Supports the GML simple features profile. * * Inherits from: * - */ ZOO.Format.GML = ZOO.Class(ZOO.Format, { /** * Property: schemaLocation * {String} Schema location for a particular minor version. */ schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd", /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { ogr: "http://ogr.maptools.org/", gml: "http://www.opengis.net/gml", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance", wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection }, /** * Property: defaultPrefix */ defaultPrefix: 'ogr', /** * Property: collectionName * {String} Name of featureCollection element. */ collectionName: "FeatureCollection", /* * Property: featureName * {String} Element name for features. Default is "sql_statement". */ featureName: "sql_statement", /** * Property: geometryName * {String} Name of geometry element. Defaults to "geometryProperty". */ geometryName: "geometryProperty", /** * Property: xy * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x) * Changing is not recommended, a new Format should be instantiated. */ xy: true, /** * Property: extractAttributes * {Boolean} Could we extract attributes */ extractAttributes: true, /** * Constructor: ZOO.Format.GML * Create a new parser for GML. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { // compile regular expressions once instead of every time they are used this.regExes = { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }; ZOO.Format.prototype.initialize.apply(this, [options]); }, /** * Method: read * Read data from a string, and return a list of features. * * Parameters: * data - {String} data to read/parse. * * Returns: * {Array()} An array of features. */ read: function(data) { this.features = []; data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, ""); data = new XML(data); var gmlns = Namespace(this.namespaces['gml']); var featureNodes = data..gmlns::featureMember; if (data.localName() == 'featureMember') featureNodes = data; var features = []; for(var i=0,len=featureNodes.length(); i 0) { var parser = this.parseGeometry[type.toLowerCase()]; if(parser) { geometry = parser.apply(this, [nodeList[0]]); if (this.internalProjection && this.externalProjection) { geometry.transform(this.externalProjection, this.internalProjection); } } // stop looking for different geometry types break; } } var attributes; if(this.extractAttributes) { attributes = this.parseAttributes(node); } var feature = new ZOO.Feature(geometry, attributes); return feature; }, /** * Property: parseGeometry * Properties of this object are the functions that parse geometries based * on their type. */ parseGeometry: { /** * Method: parseGeometry.point * Given a GML node representing a point geometry, create a ZOO * point geometry. * * Parameters: * node - {E4XElement} A GML node. * * Returns: * {} A point geometry. */ 'point': function(node) { /** * Three coordinate variations to consider: * 1) x y z * 2) x, y, z * 3) xy */ var nodeList, coordString; var coords = []; // look for var nodeList = node..*::pos; if(nodeList.length() > 0) { coordString = nodeList[0].toString(); coordString = coordString.replace(this.regExes.trimSpace, ""); coords = coordString.split(this.regExes.splitSpace); } // look for if(coords.length == 0) { nodeList = node..*::coordinates; if(nodeList.length() > 0) { coordString = nodeList[0].toString(); coordString = coordString.replace(this.regExes.removeSpace,""); coords = coordString.split(","); } } // look for if(coords.length == 0) { nodeList = node..*::coord; if(nodeList.length() > 0) { var xList = nodeList[0].*::X; var yList = nodeList[0].*::Y; if(xList.length() > 0 && yList.length() > 0) coords = [xList[0].toString(), yList[0].toString()]; } } // preserve third dimension if(coords.length == 2) coords[2] = null; if (this.xy) return new ZOO.Geometry.Point(coords[0],coords[1],coords[2]); else return new ZOO.Geometry.Point(coords[1],coords[0],coords[2]); }, /** * Method: parseGeometry.multipoint * Given a GML node representing a multipoint geometry, create a * ZOO multipoint geometry. * * Parameters: * node - {E4XElement} A GML node. * * Returns: * {} A multipoint geometry. */ 'multipoint': function(node) { var nodeList = node..*::Point; var components = []; if(nodeList.length() > 0) { var point; for(var i=0, len=nodeList.length(); i} A linestring geometry. */ 'linestring': function(node, ring) { /** * Two coordinate variations to consider: * 1) x0 y0 z0 x1 y1 z1 * 2) x0, y0, z0 x1, y1, z1 */ var nodeList, coordString; var coords = []; var points = []; // look for nodeList = node..*::posList; if(nodeList.length() > 0) { coordString = nodeList[0].toString(); coordString = coordString.replace(this.regExes.trimSpace, ""); coords = coordString.split(this.regExes.splitSpace); var dim = parseInt(nodeList[0].@dimension); var j, x, y, z; for(var i=0; i if(coords.length == 0) { nodeList = node..*::coordinates; if(nodeList.length() > 0) { coordString = nodeList[0].toString(); coordString = coordString.replace(this.regExes.trimSpace,""); coordString = coordString.replace(this.regExes.trimComma,","); var pointList = coordString.split(this.regExes.splitSpace); for(var i=0; i} A multilinestring geometry. */ 'multilinestring': function(node) { var nodeList = node..*::LineString; var components = []; if(nodeList.length() > 0) { var line; for(var i=0, len=nodeList.length(); i} A polygon geometry. */ 'polygon': function(node) { nodeList = node..*::LinearRing; var components = []; if(nodeList.length() > 0) { // this assumes exterior ring first, inner rings after var ring; for(var i=0, len = nodeList.length(); i} A multipolygon geometry. */ 'multipolygon': function(node) { var nodeList = node..*::Polygon; var components = []; if(nodeList.length() > 0) { var polygon; for(var i=0, len=nodeList.length(); i} A polygon geometry. */ 'envelope': function(node) { var components = []; var coordString; var envelope; var lpoint = node..*::lowerCorner; if (lpoint.length() > 0) { var coords = []; if(lpoint.length() > 0) { coordString = lpoint[0].toString(); coordString = coordString.replace(this.regExes.trimSpace, ""); coords = coordString.split(this.regExes.splitSpace); } if(coords.length == 2) coords[2] = null; if (this.xy) var lowerPoint = new ZOO.Geometry.Point(coords[0], coords[1],coords[2]); else var lowerPoint = new ZOO.Geometry.Point(coords[1], coords[0],coords[2]); } var upoint = node..*::upperCorner; if (upoint.length() > 0) { var coords = []; if(upoint.length > 0) { coordString = upoint[0].toString(); coordString = coordString.replace(this.regExes.trimSpace, ""); coords = coordString.split(this.regExes.splitSpace); } if(coords.length == 2) coords[2] = null; if (this.xy) var upperPoint = new ZOO.Geometry.Point(coords[0], coords[1],coords[2]); else var upperPoint = new ZOO.Geometry.Point(coords[1], coords[0],coords[2]); } if (lowerPoint && upperPoint) { components.push(new ZOO.Geometry.Point(lowerPoint.x, lowerPoint.y)); components.push(new ZOO.Geometry.Point(upperPoint.x, lowerPoint.y)); components.push(new ZOO.Geometry.Point(upperPoint.x, upperPoint.y)); components.push(new ZOO.Geometry.Point(lowerPoint.x, upperPoint.y)); components.push(new ZOO.Geometry.Point(lowerPoint.x, lowerPoint.y)); var ring = new ZOO.Geometry.LinearRing(components); envelope = new ZOO.Geometry.Polygon([ring]); } return envelope; } }, /** * Method: parseAttributes * * Parameters: * node - {} * * Returns: * {Object} An attributes object. */ parseAttributes: function(node) { var attributes = {}; // assume attributes are children of the first type 1 child var childNode = node.*::*[0]; var child, grandchildren; var children = childNode.*::*; for(var i=0, len=children.length(); i)} List of features to * serialize into a string. * * Returns: * {String} A string representing the GML document. */ write: function(features) { if(!(features instanceof Array)) { features = [features]; } var pfx = this.defaultPrefix; var name = pfx+':'+this.collectionName; var gml = new XML('<'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'" xmlns:gml="'+this.namespaces['gml']+'" xmlns:xsi="'+this.namespaces['xsi']+'" xsi:schemaLocation="'+this.schemaLocation+'">'); for(var i=0; i} The feature to be built as GML. * * Returns: * {E4XElement} A node reprensting the feature in GML. */ createFeature: function(feature) { var pfx = this.defaultPrefix; var name = pfx+':'+this.featureName; var fid = feature.fid || feature.id; var gml = new XML('<'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'" fid="'+fid+'">'); var geometry = feature.geometry; gml.*::*[0].*::* = this.buildGeometryNode(geometry); for(var attr in feature.attributes) { var attrNode = new XML('<'+pfx+':'+attr+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'">'+feature.attributes[attr]+''); gml.*::*[0].appendChild(attrNode); } return gml; }, /** * Method: buildGeometryNode * * Parameters: * geometry - {} The geometry to be built as GML. * * Returns: * {E4XElement} A node reprensting the geometry in GML. */ buildGeometryNode: function(geometry) { if (this.externalProjection && this.internalProjection) { geometry = geometry.clone(); geometry.transform(this.internalProjection, this.externalProjection); } var className = geometry.CLASS_NAME; var type = className.substring(className.lastIndexOf(".") + 1); var builder = this.buildGeometry[type.toLowerCase()]; var pfx = this.defaultPrefix; var name = pfx+':'+this.geometryName; var gml = new XML('<'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'">'); if (builder) gml.*::* = builder.apply(this, [geometry]); return gml; }, /** * Property: buildGeometry * Object containing methods to do the actual geometry node building * based on geometry type. */ buildGeometry: { /** * Method: buildGeometry.point * Given a ZOO point geometry, create a GML point. * * Parameters: * geometry - {} A point geometry. * * Returns: * {E4XElement} A GML point node. */ 'point': function(geometry) { var gml = new XML(''); gml.*::*[0] = this.buildCoordinatesNode(geometry); return gml; }, /** * Method: buildGeometry.multipoint * Given a ZOO multipoint geometry, create a GML multipoint. * * Parameters: * geometry - {} A multipoint geometry. * * Returns: * {E4XElement} A GML multipoint node. */ 'multipoint': function(geometry) { var gml = new XML(''); var points = geometry.components; var pointMember; for(var i=0; i'); pointMember.*::* = this.buildGeometry.point.apply(this,[points[i]]); gml.*::*[i] = pointMember; } return gml; }, /** * Method: buildGeometry.linestring * Given a ZOO linestring geometry, create a GML linestring. * * Parameters: * geometry - {} A linestring geometry. * * Returns: * {E4XElement} A GML linestring node. */ 'linestring': function(geometry) { var gml = new XML(''); gml.*::*[0] = this.buildCoordinatesNode(geometry); return gml; }, /** * Method: buildGeometry.multilinestring * Given a ZOO multilinestring geometry, create a GML * multilinestring. * * Parameters: * geometry - {} A multilinestring * geometry. * * Returns: * {E4XElement} A GML multilinestring node. */ 'multilinestring': function(geometry) { var gml = new XML(''); var lines = geometry.components; var lineMember; for(var i=0; i'); lineMember.*::* = this.buildGeometry.linestring.apply(this,[lines[i]]); gml.*::*[i] = lineMember; } return gml; }, /** * Method: buildGeometry.linearring * Given a ZOO linearring geometry, create a GML linearring. * * Parameters: * geometry - {} A linearring geometry. * * Returns: * {E4XElement} A GML linearring node. */ 'linearring': function(geometry) { var gml = new XML(''); gml.*::*[0] = this.buildCoordinatesNode(geometry); return gml; }, /** * Method: buildGeometry.polygon * Given an ZOO polygon geometry, create a GML polygon. * * Parameters: * geometry - {} A polygon geometry. * * Returns: * {E4XElement} A GML polygon node. */ 'polygon': function(geometry) { var gml = new XML(''); var rings = geometry.components; var ringMember, type; for(var i=0; i'); ringMember.*::* = this.buildGeometry.linearring.apply(this,[rings[i]]); gml.*::*[i] = ringMember; } return gml; }, /** * Method: buildGeometry.multipolygon * Given a ZOO multipolygon geometry, create a GML multipolygon. * * Parameters: * geometry - {} A multipolygon * geometry. * * Returns: * {E4XElement} A GML multipolygon node. */ 'multipolygon': function(geometry) { var gml = new XML(''); var polys = geometry.components; var polyMember; for(var i=0; i'); polyMember.*::* = this.buildGeometry.polygon.apply(this,[polys[i]]); gml.*::*[i] = polyMember; } return gml; } }, /** * Method: buildCoordinatesNode * builds the coordinates XmlNode * (code) * ... * (end) * Parameters: * geometry - {} * * Returns: * {E4XElement} created E4XElement */ buildCoordinatesNode: function(geometry) { var parts = []; if(geometry instanceof ZOO.Bounds){ parts.push(geometry.left + "," + geometry.bottom); parts.push(geometry.right + "," + geometry.top); } else { var points = (geometry.components) ? geometry.components : [geometry]; for(var i=0; i'+parts.join(" ")+''); }, CLASS_NAME: 'ZOO.Format.GML' }); /** * Class: ZOO.Format.WPS * Read/Write WPS. Create a new instance with the * constructor. Supports only parseExecuteResponse. * * Inherits from: * - */ ZOO.Format.WPS = ZOO.Class(ZOO.Format, { /** * Property: schemaLocation * {String} Schema location for a particular minor version. */ schemaLocation: "http://www.opengis.net/wps/1.0.0/../wpsExecute_request.xsd", /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { ows: "http://www.opengis.net/ows/1.1", wps: "http://www.opengis.net/wps/1.0.0", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance", }, /** * Method: read * * Parameters: * data - {String} A WPS xml document * * Returns: * {Object} Execute response. */ read:function(data) { data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, ""); data = new XML(data); switch (data.localName()) { case 'ExecuteResponse': return this.parseExecuteResponse(data); default: return null; } }, /** * Method: parseExecuteResponse * * Parameters: * node - {E4XElement} A WPS ExecuteResponse document * * Returns: * {Object} Execute response. */ parseExecuteResponse: function(node) { var outputs = node.*::ProcessOutputs.*::Output; if (outputs.length() > 0) { var data = outputs[0].*::Data.*::*[0]; var builder = this.parseData[data.localName().toLowerCase()]; if (builder) return builder.apply(this,[data]); else return null; } else return null; }, /** * Property: parseData * Object containing methods to analyse data response. */ parseData: { /** * Method: parseData.complexdata * Given an Object representing the WPS complex data response. * * Parameters: * node - {E4XElement} A WPS node. * * Returns: * {Object} A WPS complex data response. */ 'complexdata': function(node) { var result = {value:node.toString()}; if (node.@mimeType.length()>0) result.mimeType = node.@mimeType; if (node.@encoding.length()>0) result.encoding = node.@encoding; if (node.@schema.length()>0) result.schema = node.@schema; return result; }, /** * Method: parseData.literaldata * Given an Object representing the WPS literal data response. * * Parameters: * node - {E4XElement} A WPS node. * * Returns: * {Object} A WPS literal data response. */ 'literaldata': function(node) { var result = {value:node.toString()}; if (node.@dataType.length()>0) result.dataType = node.@dataType; if (node.@uom.length()>0) result.uom = node.@uom; return result; }, /** * Method: parseData.reference * Given an Object representing the WPS reference response. * * Parameters: * node - {E4XElement} A WPS node. * * Returns: * {Object} A WPS reference response. */ 'reference': function(node) { var result = {type:'reference',value:node.*::href}; return result; } }, CLASS_NAME: 'ZOO.Format.WPS' }); /** * Class: ZOO.Feature * Vector features use the ZOO.Geometry classes as geometry description. * They have an 'attributes' property, which is the data object */ ZOO.Feature = ZOO.Class({ /** * Property: fid * {String} */ fid: null, /** * Property: geometry * {} */ geometry: null, /** * Property: attributes * {Object} This object holds arbitrary properties that describe the * feature. */ attributes: null, /** * Property: bounds * {} The box bounding that feature's geometry, that * property can be set by an object when * deserializing the feature, so in most cases it represents an * information set by the server. */ bounds: null, /** * Constructor: ZOO.Feature * Create a vector feature. * * Parameters: * geometry - {} The geometry that this feature * represents. * attributes - {Object} An optional object that will be mapped to the * property. */ initialize: function(geometry, attributes) { this.geometry = geometry ? geometry : null; this.attributes = {}; if (attributes) this.attributes = ZOO.extend(this.attributes,attributes); }, /** * Method: destroy * nullify references to prevent circular references and memory leaks */ destroy: function() { this.geometry = null; }, /** * Method: clone * Create a clone of this vector feature. Does not set any non-standard * properties. * * Returns: * {} An exact clone of this vector feature. */ clone: function () { return new ZOO.Feature(this.geometry ? this.geometry.clone() : null, this.attributes); }, /** * Method: move * Moves the feature and redraws it at its new location * * Parameters: * x - {Float} * y - {Float} */ move: function(x, y) { if(!this.geometry.move) return; this.geometry.move(x,y); return this.geometry; }, CLASS_NAME: 'ZOO.Feature' }); /** * Class: ZOO.Geometry * A Geometry is a description of a geographic object. Create an instance * of this class with the constructor. This is a base class, * typical geometry types are described by subclasses of this class. */ ZOO.Geometry = ZOO.Class({ /** * Property: id * {String} A unique identifier for this geometry. */ id: null, /** * Property: parent * {}This is set when a Geometry is added as component * of another geometry */ parent: null, /** * Property: bounds * {} The bounds of this geometry */ bounds: null, /** * Constructor: ZOO.Geometry * Creates a geometry object. */ initialize: function() { //generate unique id }, /** * Method: destroy * Destroy this geometry. */ destroy: function() { this.id = null; this.bounds = null; }, /** * Method: clone * Create a clone of this geometry. Does not set any non-standard * properties of the cloned geometry. * * Returns: * {} An exact clone of this geometry. */ clone: function() { return new ZOO.Geometry(); }, /** * Method: extendBounds * Extend the existing bounds to include the new bounds. * If geometry's bounds is not yet set, then set a new Bounds. * * Parameters: * newBounds - {} */ extendBounds: function(newBounds){ var bounds = this.getBounds(); if (!bounds) this.setBounds(newBounds); else this.bounds.extend(newBounds); }, /** * Set the bounds for this Geometry. * * Parameters: * bounds - {} */ setBounds: function(bounds) { if (bounds) this.bounds = bounds.clone(); }, /** * Method: clearBounds * Nullify this components bounds and that of its parent as well. */ clearBounds: function() { this.bounds = null; if (this.parent) this.parent.clearBounds(); }, /** * Method: getBounds * Get the bounds for this Geometry. If bounds is not set, it * is calculated again, this makes queries faster. * * Returns: * {} */ getBounds: function() { if (this.bounds == null) { this.calculateBounds(); } return this.bounds; }, /** * Method: calculateBounds * Recalculate the bounds for the geometry. */ calculateBounds: function() { // This should be overridden by subclasses. return this.bounds; }, distanceTo: function(geometry, options) { }, getVertices: function(nodes) { }, getLength: function() { return 0.0; }, getArea: function() { return 0.0; }, getCentroid: function() { return null; }, /** * Method: toString * Returns the Well-Known Text representation of a geometry * * Returns: * {String} Well-Known Text */ toString: function() { return ZOO.Format.WKT.prototype.write( new ZOO.Feature(this) ); }, CLASS_NAME: 'ZOO.Geometry' }); /** * Function: ZOO.Geometry.fromWKT * Generate a geometry given a Well-Known Text string. * * Parameters: * wkt - {String} A string representing the geometry in Well-Known Text. * * Returns: * {} A geometry of the appropriate class. */ ZOO.Geometry.fromWKT = function(wkt) { var format = arguments.callee.format; if(!format) { format = new ZOO.Format.WKT(); arguments.callee.format = format; } var geom; var result = format.read(wkt); if(result instanceof ZOO.Feature) { geom = result.geometry; } else if(result instanceof Array) { var len = result.length; var components = new Array(len); for(var i=0; i= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) { // intersect if(!point) { intersection = true; } else { // calculate the intersection point var x = seg1.x1 + (along1 * x12_11); var y = seg1.y1 + (along1 * y12_11); intersection = new ZOO.Geometry.Point(x, y); } } } if(tolerance) { var dist; if(intersection) { if(point) { var segs = [seg1, seg2]; var seg, x, y; // check segment endpoints for proximity to intersection // set intersection to first endpoint within the tolerance outer: for(var i=0; i<2; ++i) { seg = segs[i]; for(var j=1; j<3; ++j) { x = seg["x" + j]; y = seg["y" + j]; dist = Math.sqrt( Math.pow(x - intersection.x, 2) + Math.pow(y - intersection.y, 2) ); if(dist < tolerance) { intersection.x = x; intersection.y = y; break outer; } } } } } else { // no calculated intersection, but segments could be within // the tolerance of one another var segs = [seg1, seg2]; var source, target, x, y, p, result; // check segment endpoints for proximity to intersection // set intersection to first endpoint within the tolerance outer: for(var i=0; i<2; ++i) { source = segs[i]; target = segs[(i+1)%2]; for(var j=1; j<3; ++j) { p = {x: source["x"+j], y: source["y"+j]}; result = ZOO.Geometry.distanceToSegment(p, target); if(result.distance < tolerance) { if(point) { intersection = new ZOO.Geometry.Point(p.x, p.y); } else { intersection = true; } break outer; } } } } } return intersection; }; ZOO.Geometry.distanceToSegment = function(point, segment) { var x0 = point.x; var y0 = point.y; var x1 = segment.x1; var y1 = segment.y1; var x2 = segment.x2; var y2 = segment.y2; var dx = x2 - x1; var dy = y2 - y1; var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) / (Math.pow(dx, 2) + Math.pow(dy, 2)); var x, y; if(along <= 0.0) { x = x1; y = y1; } else if(along >= 1.0) { x = x2; y = y2; } else { x = x1 + along * dx; y = y1 + along * dy; } return { distance: Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)), x: x, y: y }; }; /** * Class: ZOO.Geometry.Collection * A Collection is exactly what it sounds like: A collection of different * Geometries. These are stored in the local parameter (which * can be passed as a parameter to the constructor). * * As new geometries are added to the collection, they are NOT cloned. * When removing geometries, they need to be specified by reference (ie you * have to pass in the *exact* geometry to be removed). * * The and functions here merely iterate through * the components, summing their respective areas and lengths. * * Create a new instance with the constructor. * * Inerhits from: * - */ ZOO.Geometry.Collection = ZOO.Class(ZOO.Geometry, { /** * Property: components * {Array()} The component parts of this geometry */ components: null, /** * Property: componentTypes * {Array(String)} An array of class names representing the types of * components that the collection can include. A null value means the * component types are not restricted. */ componentTypes: null, /** * Constructor: ZOO.Geometry.Collection * Creates a Geometry Collection -- a list of geoms. * * Parameters: * components - {Array()} Optional array of geometries * */ initialize: function (components) { ZOO.Geometry.prototype.initialize.apply(this, arguments); this.components = []; if (components != null) { this.addComponents(components); } }, /** * Method: destroy * Destroy this geometry. */ destroy: function () { this.components.length = 0; this.components = null; }, /** * Method: clone * Clone this geometry. * * Returns: * {} An exact clone of this collection */ clone: function() { var geometry = eval("new " + this.CLASS_NAME + "()"); for(var i=0, len=this.components.length; i 0) { this.setBounds(this.components[0].getBounds()); for (var i=1, len=this.components.length; i)} An array of geometries to add */ addComponents: function(components){ if(!(components instanceof Array)) components = [components]; for(var i=0, len=components.length; i} A geometry to add * index - {int} Optional index into the array to insert the component * * Returns: * {Boolean} The component geometry was successfully added */ addComponent: function(component, index) { var added = false; if(component) { if(this.componentTypes == null || (ZOO.indexOf(this.componentTypes, component.CLASS_NAME) > -1)) { if(index != null && (index < this.components.length)) { var components1 = this.components.slice(0, index); var components2 = this.components.slice(index, this.components.length); components1.push(component); this.components = components1.concat(components2); } else { this.components.push(component); } component.parent = this; this.clearBounds(); added = true; } } return added; }, /** * Method: removeComponents * Remove components from this geometry. * * Parameters: * components - {Array()} The components to be removed */ removeComponents: function(components) { if(!(components instanceof Array)) components = [components]; for(var i=components.length-1; i>=0; --i) { this.removeComponent(components[i]); } }, /** * Method: removeComponent * Remove a component from this geometry. * * Parameters: * component - {} */ removeComponent: function(component) { ZOO.removeItem(this.components, component); // clearBounds() so that it gets recalculated on the next call // to this.getBounds(); this.clearBounds(); }, /** * Method: getLength * Calculate the length of this geometry * * Returns: * {Float} The length of the geometry */ getLength: function() { var length = 0.0; for (var i=0, len=this.components.length; i. * * Returns: * {Float} The area of the collection by summing its parts */ getArea: function() { var area = 0.0; for (var i=0, len=this.components.length; i} The spatial reference system * for the geometry coordinates. If not provided, Geographic/WGS84 is * assumed. * * Reference: * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 * * Returns: * {float} The approximate geodesic area of the geometry in square meters. */ getGeodesicArea: function(projection) { var area = 0.0; for(var i=0, len=this.components.length; i} The centroid of the collection */ getCentroid: function() { return this.components.length && this.components[0].getCentroid(); }, /** * Method: getGeodesicLength * Calculate the approximate length of the geometry were it projected onto * the earth. * * Parameters: * projection - {} The spatial reference system * for the geometry coordinates. If not provided, Geographic/WGS84 is * assumed. * * Returns: * {Float} The appoximate geodesic length of the geometry in meters. */ getGeodesicLength: function(projection) { var length = 0.0; for(var i=0, len=this.components.length; i} Center point for the rotation */ rotate: function(angle, origin) { for(var i=0, len=this.components.length; i} Point of origin for resizing * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. * * Returns: * {ZOO.Geometry} - The current geometry. */ resize: function(scale, origin, ratio) { for(var i=0; i} The geometry to test. * * Returns: * {Boolean} The supplied geometry is equivalent to this geometry. */ equals: function(geometry) { var equivalent = true; if(!geometry || !geometry.CLASS_NAME || (this.CLASS_NAME != geometry.CLASS_NAME)) equivalent = false; else if(!(geometry.components instanceof Array) || (geometry.components.length != this.components.length)) equivalent = false; else for(var i=0, len=this.components.length; i} * dest - {} * * Returns: * {} */ transform: function(source, dest) { if (source && dest) { for (var i=0, len=this.components.length; i} Any type of geometry. * * Returns: * {Boolean} The input geometry intersects this one. */ intersects: function(geometry) { var intersect = false; for(var i=0, len=this.components.length; i */ ZOO.Geometry.Point = ZOO.Class(ZOO.Geometry, { /** * Property: x * {float} */ x: null, /** * Property: y * {float} */ y: null, /** * Constructor: ZOO.Geometry.Point * Construct a point geometry. * * Parameters: * x - {float} * y - {float} * */ initialize: function(x, y) { ZOO.Geometry.prototype.initialize.apply(this, arguments); this.x = parseFloat(x); this.y = parseFloat(y); }, /** * Method: clone * * Returns: * {} An exact clone of this ZOO.Geometry.Point */ clone: function(obj) { if (obj == null) obj = new ZOO.Geometry.Point(this.x, this.y); // catch any randomly tagged-on properties // ZOO.Util.applyDefaults(obj, this); return obj; }, /** * Method: calculateBounds * Create a new Bounds based on the x/y */ calculateBounds: function () { this.bounds = new ZOO.Bounds(this.x, this.y, this.x, this.y); }, distanceTo: function(geometry, options) { var edge = !(options && options.edge === false); var details = edge && options && options.details; var distance, x0, y0, x1, y1, result; if(geometry instanceof ZOO.Geometry.Point) { x0 = this.x; y0 = this.y; x1 = geometry.x; y1 = geometry.y; distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2)); result = !details ? distance : {x0: x0, y0: y0, x1: x1, y1: y1, distance: distance}; } else { result = geometry.distanceTo(this, options); if(details) { // switch coord order since this geom is target result = { x0: result.x1, y0: result.y1, x1: result.x0, y1: result.y0, distance: result.distance }; } } return result; }, /** * Method: equals * Determine whether another geometry is equivalent to this one. Geometries * are considered equivalent if all components have the same coordinates. * * Parameters: * geom - {} The geometry to test. * * Returns: * {Boolean} The supplied geometry is equivalent to this geometry. */ equals: function(geom) { var equals = false; if (geom != null) equals = ((this.x == geom.x && this.y == geom.y) || (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y))); return equals; }, /** * Method: toShortString * * Returns: * {String} Shortened String representation of Point object. * (ex. "5, 42") */ toShortString: function() { return (this.x + ", " + this.y); }, /** * Method: move * Moves a geometry by the given displacement along positive x and y axes. * This modifies the position of the geometry and clears the cached * bounds. * * Parameters: * x - {Float} Distance to move geometry in positive x direction. * y - {Float} Distance to move geometry in positive y direction. */ move: function(x, y) { this.x = this.x + x; this.y = this.y + y; this.clearBounds(); }, /** * Method: rotate * Rotate a point around another. * * Parameters: * angle - {Float} Rotation angle in degrees (measured counterclockwise * from the positive x-axis) * origin - {} Center point for the rotation */ rotate: function(angle, origin) { angle *= Math.PI / 180; var radius = this.distanceTo(origin); var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x); this.x = origin.x + (radius * Math.cos(theta)); this.y = origin.y + (radius * Math.sin(theta)); this.clearBounds(); }, /** * Method: getCentroid * * Returns: * {} The centroid of the collection */ getCentroid: function() { return new ZOO.Geometry.Point(this.x, this.y); }, /** * Method: resize * Resize a point relative to some origin. For points, this has the effect * of scaling a vector (from the origin to the point). This method is * more useful on geometry collection subclasses. * * Parameters: * scale - {Float} Ratio of the new distance from the origin to the old * distance from the origin. A scale of 2 doubles the * distance between the point and origin. * origin - {} Point of origin for resizing * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. * * Returns: * {ZOO.Geometry} - The current geometry. */ resize: function(scale, origin, ratio) { ratio = (ratio == undefined) ? 1 : ratio; this.x = origin.x + (scale * ratio * (this.x - origin.x)); this.y = origin.y + (scale * (this.y - origin.y)); this.clearBounds(); return this; }, /** * Method: intersects * Determine if the input geometry intersects this one. * * Parameters: * geometry - {} Any type of geometry. * * Returns: * {Boolean} The input geometry intersects this one. */ intersects: function(geometry) { var intersect = false; if(geometry.CLASS_NAME == "ZOO.Geometry.Point") { intersect = this.equals(geometry); } else { intersect = geometry.intersects(this); } return intersect; }, /** * Method: transform * Translate the x,y properties of the point from source to dest. * * Parameters: * source - {} * dest - {} * * Returns: * {} */ transform: function(source, dest) { if ((source && dest)) { ZOO.Projection.transform( this, source, dest); this.bounds = null; } return this; }, /** * Method: getVertices * Return a list of all points in this geometry. * * Parameters: * nodes - {Boolean} For lines, only return vertices that are * endpoints. If false, for lines, only vertices that are not * endpoints will be returned. If not provided, all vertices will * be returned. * * Returns: * {Array} A list of all vertices in the geometry. */ getVertices: function(nodes) { return [this]; }, CLASS_NAME: 'ZOO.Geometry.Point' }); /** * Class: ZOO.Geometry.Surface * Surface geometry class. * * Inherits from: * - */ ZOO.Geometry.Surface = ZOO.Class(ZOO.Geometry, { initialize: function() { ZOO.Geometry.prototype.initialize.apply(this, arguments); }, CLASS_NAME: "ZOO.Geometry.Surface" }); /** * Class: ZOO.Geometry.MultiPoint * MultiPoint is a collection of Points. Create a new instance with the * constructor. * * Inherits from: * - */ ZOO.Geometry.MultiPoint = ZOO.Class( ZOO.Geometry.Collection, { /** * Property: componentTypes * {Array(String)} An array of class names representing the types of * components that the collection can include. A null value means the * component types are not restricted. */ componentTypes: ["ZOO.Geometry.Point"], /** * Constructor: ZOO.Geometry.MultiPoint * Create a new MultiPoint Geometry * * Parameters: * components - {Array()} * * Returns: * {} */ initialize: function(components) { ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments); }, /** * Method: addPoint * Wrapper for * * Parameters: * point - {} Point to be added * index - {Integer} Optional index */ addPoint: function(point, index) { this.addComponent(point, index); }, /** * Method: removePoint * Wrapper for * * Parameters: * point - {} Point to be removed */ removePoint: function(point){ this.removeComponent(point); }, CLASS_NAME: "ZOO.Geometry.MultiPoint" }); /** * Class: ZOO.Geometry.Curve * A Curve is a MultiPoint, whose points are assumed to be connected. To * this end, we provide a "getLength()" function, which iterates through * the points, summing the distances between them. * * Inherits: * - */ ZOO.Geometry.Curve = ZOO.Class(ZOO.Geometry.MultiPoint, { /** * Property: componentTypes * {Array(String)} An array of class names representing the types of * components that the collection can include. A null * value means the component types are not restricted. */ componentTypes: ["ZOO.Geometry.Point"], /** * Constructor: ZOO.Geometry.Curve * * Parameters: * point - {Array()} */ initialize: function(points) { ZOO.Geometry.MultiPoint.prototype.initialize.apply(this,arguments); }, /** * Method: getLength * * Returns: * {Float} The length of the curve */ getLength: function() { var length = 0.0; if ( this.components && (this.components.length > 1)) { for(var i=1, len=this.components.length; i} The spatial reference system * for the geometry coordinates. If not provided, Geographic/WGS84 is * assumed. * * Returns: * {Float} The appoximate geodesic length of the geometry in meters. */ getGeodesicLength: function(projection) { var geom = this; // so we can work with a clone if needed if(projection) { var gg = new ZOO.Projection("EPSG:4326"); if(!gg.equals(projection)) { geom = this.clone().transform(projection, gg); } } var length = 0.0; if(geom.components && (geom.components.length > 1)) { var p1, p2; for(var i=1, len=geom.components.length; i */ ZOO.Geometry.LineString = ZOO.Class(ZOO.Geometry.Curve, { /** * Constructor: ZOO.Geometry.LineString * Create a new LineString geometry * * Parameters: * points - {Array()} An array of points used to * generate the linestring * */ initialize: function(points) { ZOO.Geometry.Curve.prototype.initialize.apply(this, arguments); }, /** * Method: removeComponent * Only allows removal of a point if there are three or more points in * the linestring. (otherwise the result would be just a single point) * * Parameters: * point - {} The point to be removed */ removeComponent: function(point) { if ( this.components && (this.components.length > 2)) ZOO.Geometry.Collection.prototype.removeComponent.apply(this,arguments); }, /** * Method: intersects * Test for instersection between two geometries. This is a cheapo * implementation of the Bently-Ottmann algorigithm. It doesn't * really keep track of a sweep line data structure. It is closer * to the brute force method, except that segments are sorted and * potential intersections are only calculated when bounding boxes * intersect. * * Parameters: * geometry - {} * * Returns: * {Boolean} The input geometry intersects this geometry. */ intersects: function(geometry) { var intersect = false; var type = geometry.CLASS_NAME; if(type == "ZOO.Geometry.LineString" || type == "ZOO.Geometry.LinearRing" || type == "ZOO.Geometry.Point") { var segs1 = this.getSortedSegments(); var segs2; if(type == "ZOO.Geometry.Point") segs2 = [{ x1: geometry.x, y1: geometry.y, x2: geometry.x, y2: geometry.y }]; else segs2 = geometry.getSortedSegments(); var seg1, seg1x1, seg1x2, seg1y1, seg1y2, seg2, seg2y1, seg2y2; // sweep right outer: for(var i=0, len=segs1.length; i seg1x2) break; if(seg2.x2 < seg1x1) continue; seg2y1 = seg2.y1; seg2y2 = seg2.y2; if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) continue; if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) continue; if(ZOO.Geometry.segmentsIntersect(seg1, seg2)) { intersect = true; break outer; } } } } else { intersect = geometry.intersects(this); } return intersect; }, /** * Method: getSortedSegments * * Returns: * {Array} An array of segment objects. Segment objects have properties * x1, y1, x2, and y2. The start point is represented by x1 and y1. * The end point is represented by x2 and y2. Start and end are * ordered so that x1 < x2. */ getSortedSegments: function() { var numSeg = this.components.length - 1; var segments = new Array(numSeg); for(var i=0; i 0) { // sort intersections along segment var xDir = seg.x1 < seg.x2 ? 1 : -1; var yDir = seg.y1 < seg.y2 ? 1 : -1; result = { lines: lines, points: intersections.sort(function(p1, p2) { return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y); }) }; } return result; }, /** * Method: split * Use this geometry (the source) to attempt to split a target geometry. * * Parameters: * target - {} The target geometry. * options - {Object} Properties of this object will be used to determine * how the split is conducted. * * Valid options: * mutual - {Boolean} Split the source geometry in addition to the target * geometry. Default is false. * edge - {Boolean} Allow splitting when only edges intersect. Default is * true. If false, a vertex on the source must be within the tolerance * distance of the intersection to be considered a split. * tolerance - {Number} If a non-null value is provided, intersections * within the tolerance distance of an existing vertex on the source * will be assumed to occur at the vertex. * * Returns: * {Array} A list of geometries (of this same type as the target) that * result from splitting the target with the source geometry. The * source and target geometry will remain unmodified. If no split * results, null will be returned. If mutual is true and a split * results, return will be an array of two arrays - the first will be * all geometries that result from splitting the source geometry and * the second will be all geometries that result from splitting the * target geometry. */ split: function(target, options) { var results = null; var mutual = options && options.mutual; var sourceSplit, targetSplit, sourceParts, targetParts; if(target instanceof ZOO.Geometry.LineString) { var verts = this.getVertices(); var vert1, vert2, seg, splits, lines, point; var points = []; sourceParts = []; for(var i=0, stop=verts.length-2; i<=stop; ++i) { vert1 = verts[i]; vert2 = verts[i+1]; seg = { x1: vert1.x, y1: vert1.y, x2: vert2.x, y2: vert2.y }; targetParts = targetParts || [target]; if(mutual) points.push(vert1.clone()); for(var j=0; j 0) { lines.unshift(j, 1); Array.prototype.splice.apply(targetParts, lines); j += lines.length - 2; } if(mutual) { for(var k=0, len=splits.points.length; k 0 && points.length > 0) { points.push(vert2.clone()); sourceParts.push(new ZOO.Geometry.LineString(points)); } } else { results = target.splitWith(this, options); } if(targetParts && targetParts.length > 1) targetSplit = true; else targetParts = []; if(sourceParts && sourceParts.length > 1) sourceSplit = true; else sourceParts = []; if(targetSplit || sourceSplit) { if(mutual) results = [sourceParts, targetParts]; else results = targetParts; } return results; }, /** * Method: splitWith * Split this geometry (the target) with the given geometry (the source). * * Parameters: * geometry - {} A geometry used to split this * geometry (the source). * options - {Object} Properties of this object will be used to determine * how the split is conducted. * * Valid options: * mutual - {Boolean} Split the source geometry in addition to the target * geometry. Default is false. * edge - {Boolean} Allow splitting when only edges intersect. Default is * true. If false, a vertex on the source must be within the tolerance * distance of the intersection to be considered a split. * tolerance - {Number} If a non-null value is provided, intersections * within the tolerance distance of an existing vertex on the source * will be assumed to occur at the vertex. * * Returns: * {Array} A list of geometries (of this same type as the target) that * result from splitting the target with the source geometry. The * source and target geometry will remain unmodified. If no split * results, null will be returned. If mutual is true and a split * results, return will be an array of two arrays - the first will be * all geometries that result from splitting the source geometry and * the second will be all geometries that result from splitting the * target geometry. */ splitWith: function(geometry, options) { return geometry.split(this, options); }, /** * Method: getVertices * Return a list of all points in this geometry. * * Parameters: * nodes - {Boolean} For lines, only return vertices that are * endpoints. If false, for lines, only vertices that are not * endpoints will be returned. If not provided, all vertices will * be returned. * * Returns: * {Array} A list of all vertices in the geometry. */ getVertices: function(nodes) { var vertices; if(nodes === true) vertices = [ this.components[0], this.components[this.components.length-1] ]; else if (nodes === false) vertices = this.components.slice(1, this.components.length-1); else vertices = this.components.slice(); return vertices; }, distanceTo: function(geometry, options) { var edge = !(options && options.edge === false); var details = edge && options && options.details; var result, best = {}; var min = Number.POSITIVE_INFINITY; if(geometry instanceof ZOO.Geometry.Point) { var segs = this.getSortedSegments(); var x = geometry.x; var y = geometry.y; var seg; for(var i=0, len=segs.length; i x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2))) break; } } if(details) best = { distance: best.distance, x0: best.x, y0: best.y, x1: x, y1: y }; else best = best.distance; } else if(geometry instanceof ZOO.Geometry.LineString) { var segs0 = this.getSortedSegments(); var segs1 = geometry.getSortedSegments(); var seg0, seg1, intersection, x0, y0; var len1 = segs1.length; var interOptions = {point: true}; outer: for(var i=0, len=segs0.length; i */ ZOO.Geometry.LinearRing = ZOO.Class( ZOO.Geometry.LineString, { /** * Property: componentTypes * {Array(String)} An array of class names representing the types of * components that the collection can include. A null * value means the component types are not restricted. */ componentTypes: ["ZOO.Geometry.Point"], /** * Constructor: ZOO.Geometry.LinearRing * Linear rings are constructed with an array of points. This array * can represent a closed or open ring. If the ring is open (the last * point does not equal the first point), the constructor will close * the ring. If the ring is already closed (the last point does equal * the first point), it will be left closed. * * Parameters: * points - {Array()} points */ initialize: function(points) { ZOO.Geometry.LineString.prototype.initialize.apply(this,arguments); }, /** * Method: addComponent * Adds a point to geometry components. If the point is to be added to * the end of the components array and it is the same as the last point * already in that array, the duplicate point is not added. This has * the effect of closing the ring if it is not already closed, and * doing the right thing if it is already closed. This behavior can * be overridden by calling the method with a non-null index as the * second argument. * * Parameter: * point - {} * index - {Integer} Index into the array to insert the component * * Returns: * {Boolean} Was the Point successfully added? */ addComponent: function(point, index) { var added = false; //remove last point var lastPoint = this.components.pop(); // given an index, add the point // without an index only add non-duplicate points if(index != null || !point.equals(lastPoint)) added = ZOO.Geometry.Collection.prototype.addComponent.apply(this,arguments); //append copy of first point var firstPoint = this.components[0]; ZOO.Geometry.Collection.prototype.addComponent.apply(this,[firstPoint]); return added; }, /** * APIMethod: removeComponent * Removes a point from geometry components. * * Parameters: * point - {} */ removeComponent: function(point) { if (this.components.length > 4) { //remove last point this.components.pop(); //remove our point ZOO.Geometry.Collection.prototype.removeComponent.apply(this,arguments); //append copy of first point var firstPoint = this.components[0]; ZOO.Geometry.Collection.prototype.addComponent.apply(this,[firstPoint]); } }, /** * Method: move * Moves a geometry by the given displacement along positive x and y axes. * This modifies the position of the geometry and clears the cached * bounds. * * Parameters: * x - {Float} Distance to move geometry in positive x direction. * y - {Float} Distance to move geometry in positive y direction. */ move: function(x, y) { for(var i = 0, len=this.components.length; i} Center point for the rotation */ rotate: function(angle, origin) { for(var i=0, len=this.components.length; i} Point of origin for resizing * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. * * Returns: * {ZOO.Geometry} - The current geometry. */ resize: function(scale, origin, ratio) { for(var i=0, len=this.components.length; i} * dest - {} * * Returns: * {} */ transform: function(source, dest) { if (source && dest) { for (var i=0, len=this.components.length; i} The centroid of the ring */ getCentroid: function() { if ( this.components && (this.components.length > 2)) { var sumX = 0.0; var sumY = 0.0; for (var i = 0; i < this.components.length - 1; i++) { var b = this.components[i]; var c = this.components[i+1]; sumX += (b.x + c.x) * (b.x * c.y - c.x * b.y); sumY += (b.y + c.y) * (b.x * c.y - c.x * b.y); } var area = -1 * this.getArea(); var x = sumX / (6 * area); var y = sumY / (6 * area); } return new ZOO.Geometry.Point(x, y); }, /** * Method: getArea * Note - The area is positive if the ring is oriented CW, otherwise * it will be negative. * * Returns: * {Float} The signed area for a ring. */ getArea: function() { var area = 0.0; if ( this.components && (this.components.length > 2)) { var sum = 0.0; for (var i=0, len=this.components.length; i} The spatial reference system * for the geometry coordinates. If not provided, Geographic/WGS84 is * assumed. * * Reference: * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 * * Returns: * {float} The approximate signed geodesic area of the polygon in square * meters. */ getGeodesicArea: function(projection) { var ring = this; // so we can work with a clone if needed if(projection) { var gg = new ZOO.Projection("EPSG:4326"); if(!gg.equals(projection)) { ring = this.clone().transform(projection, gg); } } var area = 0.0; var len = ring.components && ring.components.length; if(len > 2) { var p1, p2; for(var i=0; i} * * Returns: * {Boolean | Number} The point is inside the linear ring. Returns 1 if * the point is coincident with an edge. Returns boolean otherwise. */ containsPoint: function(point) { var approx = OpenLayers.Number.limitSigDigs; var digs = 14; var px = approx(point.x, digs); var py = approx(point.y, digs); function getX(y, x1, y1, x2, y2) { return (((x1 - x2) * y) + ((x2 * y1) - (x1 * y2))) / (y1 - y2); } var numSeg = this.components.length - 1; var start, end, x1, y1, x2, y2, cx, cy; var crosses = 0; for(var i=0; i= x1 && px <= x2) || // right or vert x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert // point on edge crosses = -1; break; } } // ignore other horizontal edges continue; } cx = approx(getX(py, x1, y1, x2, y2), digs); if(cx == px) { // point on line if(y1 < y2 && (py >= y1 && py <= y2) || // upward y1 > y2 && (py <= y1 && py >= y2)) { // downward // point on edge crosses = -1; break; } } if(cx <= px) { // no crossing to the right continue; } if(x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) { // no crossing continue; } if(y1 < y2 && (py >= y1 && py < y2) || // upward y1 > y2 && (py < y1 && py >= y2)) { // downward ++crosses; } } var contained = (crosses == -1) ? // on edge 1 : // even (out) or odd (in) !!(crosses & 1); return contained; }, intersects: function(geometry) { var intersect = false; if(geometry.CLASS_NAME == "ZOO.Geometry.Point") intersect = this.containsPoint(geometry); else if(geometry.CLASS_NAME == "ZOO.Geometry.LineString") intersect = geometry.intersects(this); else if(geometry.CLASS_NAME == "ZOO.Geometry.LinearRing") intersect = ZOO.Geometry.LineString.prototype.intersects.apply( this, [geometry] ); else for(var i=0, len=geometry.components.length; i * components. * * Inherits from: * - */ ZOO.Geometry.MultiLineString = ZOO.Class( ZOO.Geometry.Collection, { componentTypes: ["ZOO.Geometry.LineString"], /** * Constructor: ZOO.Geometry.MultiLineString * Constructor for a MultiLineString Geometry. * * Parameters: * components - {Array()} * */ initialize: function(components) { ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments); }, split: function(geometry, options) { var results = null; var mutual = options && options.mutual; var splits, sourceLine, sourceLines, sourceSplit, targetSplit; var sourceParts = []; var targetParts = [geometry]; for(var i=0, len=this.components.length; i 1) sourceSplit = true; else sourceParts = []; if(targetParts && targetParts.length > 1) targetSplit = true; else targetParts = []; if(sourceSplit || targetSplit) { if(mutual) results = [sourceParts, targetParts]; else results = targetParts; } return results; }, splitWith: function(geometry, options) { var results = null; var mutual = options && options.mutual; var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts; if(geometry instanceof ZOO.Geometry.LineString) { targetParts = []; sourceParts = [geometry]; for(var i=0, len=this.components.length; i 1) sourceSplit = true; else sourceParts = []; if(targetParts && targetParts.length > 1) targetSplit = true; else targetParts = []; if(sourceSplit || targetSplit) { if(mutual) results = [sourceParts, targetParts]; else results = targetParts; } return results; }, CLASS_NAME: "ZOO.Geometry.MultiLineString" }); /** * Class: ZOO.Geometry.Polygon * Polygon is a collection of . * * Inherits from: * - */ ZOO.Geometry.Polygon = ZOO.Class( ZOO.Geometry.Collection, { componentTypes: ["ZOO.Geometry.LinearRing"], /** * Constructor: ZOO.Geometry.Polygon * Constructor for a Polygon geometry. * The first ring (this.component[0])is the outer bounds of the polygon and * all subsequent rings (this.component[1-n]) are internal holes. * * * Parameters: * components - {Array()} */ initialize: function(components) { ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments); }, /** * Method: getArea * Calculated by subtracting the areas of the internal holes from the * area of the outer hole. * * Returns: * {float} The area of the geometry */ getArea: function() { var area = 0.0; if ( this.components && (this.components.length > 0)) { area += Math.abs(this.components[0].getArea()); for (var i=1, len=this.components.length; i} The spatial reference system * for the geometry coordinates. If not provided, Geographic/WGS84 is * assumed. * * Reference: * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 * * Returns: * {float} The approximate geodesic area of the polygon in square meters. */ getGeodesicArea: function(projection) { var area = 0.0; if(this.components && (this.components.length > 0)) { area += Math.abs(this.components[0].getGeodesicArea(projection)); for(var i=1, len=this.components.length; i} * * Returns: * {Boolean | Number} The point is inside the polygon. Returns 1 if the * point is on an edge. Returns boolean otherwise. */ containsPoint: function(point) { var numRings = this.components.length; var contained = false; if(numRings > 0) { // check exterior ring - 1 means on edge, boolean otherwise contained = this.components[0].containsPoint(point); if(contained !== 1) { if(contained && numRings > 1) { // check interior rings var hole; for(var i=1; i} center of polygon. * radius - {Float} distance to vertex, in map units. * sides - {Integer} Number of sides. 20 approximates a circle. * rotation - {Float} original angle of rotation, in degrees. */ ZOO.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) { var angle = Math.PI * ((1/sides) - (1/2)); if(rotation) { angle += (rotation / 180) * Math.PI; } var rotatedAngle, x, y; var points = []; for(var i=0; i * components. Create a new instance with the * constructor. * * Inherits from: * - */ ZOO.Geometry.MultiPolygon = ZOO.Class( ZOO.Geometry.Collection, { componentTypes: ["ZOO.Geometry.Polygon"], /** * Constructor: ZOO.Geometry.MultiPolygon * Create a new MultiPolygon geometry * * Parameters: * components - {Array()} An array of polygons * used to generate the MultiPolygon * */ initialize: function(components) { ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments); }, CLASS_NAME: "ZOO.Geometry.MultiPolygon" }); /** * Class: ZOO.Process * Used to query OGC WPS process defined by its URL and its identifier. * Usefull for chaining localhost process. */ ZOO.Process = ZOO.Class({ /** * Property: schemaLocation * {String} Schema location for a particular minor version. */ schemaLocation: "http://www.opengis.net/wps/1.0.0/../wpsExecute_request.xsd", /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { ows: "http://www.opengis.net/ows/1.1", wps: "http://www.opengis.net/wps/1.0.0", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance", }, /** * Property: url * {String} The OGC's Web PRocessing Service URL, * default is http://localhost/zoo. */ url: 'http://localhost/zoo', /** * Property: identifier * {String} Process identifier in the OGC's Web Processing Service. */ identifier: null, /** * Constructor: ZOO.Process * Create a new Process * * Parameters: * url - {String} The OGC's Web Processing Service URL. * identifier - {String} The process identifier in the OGC's Web Processing Service. * */ initialize: function(url,identifier) { this.url = url; this.identifier = identifier; }, /** * Method: Execute * Query the OGC's Web PRocessing Servcie to Execute the process. * * Parameters: * inputs - {Object} * * Returns: * {String} The OGC's Web processing Service XML response. The result * needs to be interpreted. */ Execute: function(inputs,outputs) { if (this.identifier == null) return null; var body = new XML(''+this.identifier+''+this.buildDataInputsNode(inputs)+this.buildDataOutputsNode(outputs)+''); body = body.toXMLString(); var response = ZOO.Request.Post(this.url,body,['Content-Type: text/xml; charset=UTF-8']); return response; }, buildOutput:{ /** * Method: buildOutput.ResponseDocument * Given an E4XElement representing the WPS ResponseDocument output. * * Parameters: * identifier - {String} the input indetifier * data - {Object} A WPS complex data input. * * Returns: * {E4XElement} A WPS Input node. */ 'ResponseDocument': function(identifier,obj) { var output = new XML(''+identifier+''); if (obj.encoding) output.*::Data.*::ComplexData.@encoding = obj.encoding; if (obj.schema) output.*::Data.*::ComplexData.@schema = obj.schema; output = output.toXMLString(); return output; }, 'RawDataOutput': function(identifier,obj) { var output = new XML(''+identifier+''); if (obj.encoding) output.*::Data.*::ComplexData.@encoding = obj.encoding; if (obj.schema) output.*::Data.*::ComplexData.@schema = obj.schema; output = output.toXMLString(); return output; } }, /** * Property: buildInput * Object containing methods to build WPS inputs. */ buildInput: { /** * Method: buildInput.complex * Given an E4XElement representing the WPS complex data input. * * Parameters: * identifier - {String} the input indetifier * data - {Object} A WPS complex data input. * * Returns: * {E4XElement} A WPS Input node. */ 'complex': function(identifier,data) { var input = new XML(''+identifier+''+(data.value?'':(data.xlink?'':''))+''); if(data.xlink) input.*::Reference.@mimeType = data.mimetype ? data.mimetype : 'application/json'; else input.*::Data.*::ComplexData.@mimeType = data.mimetype ? data.mimetype : 'application/json'; if (data.encoding) input.*::Data.*::ComplexData.@encoding = data.encoding; if (data.schema) input.*::Data.*::ComplexData.@schema = data.schema; input = input.toXMLString(); return input; }, /** * Method: buildInput.reference * Given an E4XElement representing the WPS reference input. * * Parameters: * identifier - {String} the input indetifier * data - {Object} A WPS reference input. * * Returns: * {E4XElement} A WPS Input node. */ 'reference': function(identifier,data) { return ''+identifier+''; }, /** * Method: buildInput.literal * Given an E4XElement representing the WPS literal data input. * * Parameters: * identifier - {String} the input indetifier * data - {Object} A WPS literal data input. * * Returns: * {E4XElement} The WPS Input node. */ 'literal': function(identifier,data) { var input = new XML(''+identifier+''+data.value+''); if (data.type) input.*::Data.*::LiteralData.@dataType = data.type; if (data.uom) input.*::Data.*::LiteralData.@uom = data.uom; input = input.toXMLString(); return input; } }, /** * Method: buildDataInputsNode * Method to build the WPS DataInputs element. * * Parameters: * inputs - {Object} * * Returns: * {E4XElement} The WPS DataInputs node for Execute query. */ buildDataInputsNode:function(inputs){ var data, builder, inputsArray=[]; for (var attr in inputs) { data = inputs[attr]; if (data.mimetype || data.type == 'complex') builder = this.buildInput['complex']; else if (data.type == 'reference' || data.type == 'url') builder = this.buildInput['reference']; else builder = this.buildInput['literal']; inputsArray.push(builder.apply(this,[attr,data])); } return ''+inputsArray.join('\n')+''; }, buildDataOutputsNode:function(outputs){ var data, builder, outputsArray=[]; for (var attr in outputs) { data = outputs[attr]; builder = this.buildOutput[data.type]; outputsArray.push(builder.apply(this,[attr,data])); } return outputsArray.join('\n'); }, CLASS_NAME: "ZOO.Process" });