123 | | /** |
124 | | * Function: rad |
125 | | * |
126 | | * Parameters: |
127 | | * x - {Float} |
128 | | * |
129 | | * Returns: |
130 | | * {Float} |
131 | | */ |
132 | | rad: function(x) {return x*Math.PI/180;}, |
133 | | /** |
134 | | * Function: distVincenty |
135 | | * Given two objects representing points with geographic coordinates, this |
136 | | * calculates the distance between those points on the surface of an |
137 | | * ellipsoid. |
138 | | * |
139 | | * Parameters: |
140 | | * p1 - {<ZOO.Geometry.Point>} (or any object with both .x, .y properties) |
141 | | * p2 - {<ZOO.Geometry.Point>} (or any object with both .x, .y properties) |
142 | | * |
143 | | * Returns: |
144 | | * {Float} The distance (in km) between the two input points as measured on an |
145 | | * ellipsoid. Note that the input point objects must be in geographic |
146 | | * coordinates (decimal degrees) and the return distance is in kilometers. |
147 | | */ |
148 | | distVincenty: function(p1, p2) { |
149 | | var a = 6378137, b = 6356752.3142, f = 1/298.257223563; |
150 | | var L = ZOO.rad(p2.x - p1.y); |
151 | | var U1 = Math.atan((1-f) * Math.tan(ZOO.rad(p1.y))); |
152 | | var U2 = Math.atan((1-f) * Math.tan(ZOO.rad(p2.y))); |
153 | | var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1); |
154 | | var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2); |
155 | | var lambda = L, lambdaP = 2*Math.PI; |
156 | | var iterLimit = 20; |
157 | | while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) { |
158 | | var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda); |
159 | | var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) + |
160 | | (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda)); |
161 | | if (sinSigma==0) { |
162 | | return 0; // co-incident points |
163 | | } |
164 | | var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda; |
165 | | var sigma = Math.atan2(sinSigma, cosSigma); |
166 | | var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma); |
167 | | var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha); |
168 | | var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha; |
169 | | var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha)); |
170 | | lambdaP = lambda; |
171 | | lambda = L + (1-C) * f * Math.sin(alpha) * |
172 | | (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM))); |
173 | | } |
174 | | if (iterLimit==0) { |
175 | | return NaN; // formula failed to converge |
176 | | } |
177 | | var uSq = cosSqAlpha * (a*a - b*b) / (b*b); |
178 | | var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq))); |
179 | | var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq))); |
180 | | var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)- |
181 | | B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM))); |
182 | | var s = b*A*(sigma-deltaSigma); |
183 | | var d = s.toFixed(3)/1000; // round to 1mm precision |
184 | | return d; |
185 | | }, |
186 | | /** |
187 | | * Function: Class |
188 | | * Method used to create ZOO classes. Includes support for |
189 | | * multiple inheritance. |
190 | | */ |
191 | | Class: function() { |
192 | | var Class = function() { |
193 | | this.initialize.apply(this, arguments); |
194 | | }; |
195 | | var extended = {}; |
196 | | var parent; |
197 | | for(var i=0; i<arguments.length; ++i) { |
198 | | if(typeof arguments[i] == "function") { |
199 | | // get the prototype of the superclass |
200 | | parent = arguments[i].prototype; |
201 | | } else { |
202 | | // in this case we're extending with the prototype |
203 | | parent = arguments[i]; |
204 | | } |
205 | | ZOO.extend(extended, parent); |
206 | | } |
207 | | Class.prototype = extended; |
208 | | |
209 | | return Class; |
210 | | }, |
211 | | /** |
212 | | * Function: UpdateStatus |
213 | | * Method used to update the status of the process |
214 | | * |
215 | | * Parameters: |
216 | | * env - {Object} The environment object |
217 | | * value - {Float} the status value between 0 to 100 |
218 | | */ |
219 | | UpdateStatus: function(env,value) { |
220 | | return ZOOUpdateStatus(env,value); |
221 | | } |
222 | | }; |
223 | | |
224 | | /** |
225 | | * Class: ZOO.String |
226 | | * Contains convenience methods for string manipulation |
227 | | */ |
228 | | ZOO.String = { |
229 | | /** |
230 | | * Function: startsWith |
231 | | * Test whether a string starts with another string. |
232 | | * |
233 | | * Parameters: |
234 | | * str - {String} The string to test. |
235 | | * sub - {Sring} The substring to look for. |
236 | | * |
237 | | * Returns: |
238 | | * {Boolean} The first string starts with the second. |
239 | | */ |
240 | | startsWith: function(str, sub) { |
241 | | return (str.indexOf(sub) == 0); |
242 | | }, |
243 | | /** |
244 | | * Function: contains |
245 | | * Test whether a string contains another string. |
246 | | * |
247 | | * Parameters: |
248 | | * str - {String} The string to test. |
249 | | * sub - {String} The substring to look for. |
250 | | * |
251 | | * Returns: |
252 | | * {Boolean} The first string contains the second. |
253 | | */ |
254 | | contains: function(str, sub) { |
255 | | return (str.indexOf(sub) != -1); |
256 | | }, |
257 | | /** |
258 | | * Function: trim |
259 | | * Removes leading and trailing whitespace characters from a string. |
260 | | * |
261 | | * Parameters: |
262 | | * str - {String} The (potentially) space padded string. This string is not |
263 | | * modified. |
264 | | * |
265 | | * Returns: |
266 | | * {String} A trimmed version of the string with all leading and |
267 | | * trailing spaces removed. |
268 | | */ |
269 | | trim: function(str) { |
270 | | return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); |
271 | | }, |
272 | | /** |
273 | | * Function: camelize |
274 | | * Camel-case a hyphenated string. |
275 | | * Ex. "chicken-head" becomes "chickenHead", and |
276 | | * "-chicken-head" becomes "ChickenHead". |
277 | | * |
278 | | * Parameters: |
279 | | * str - {String} The string to be camelized. The original is not modified. |
280 | | * |
281 | | * Returns: |
282 | | * {String} The string, camelized |
283 | | * |
284 | | */ |
285 | | camelize: function(str) { |
286 | | var oStringList = str.split('-'); |
287 | | var camelizedString = oStringList[0]; |
288 | | for (var i=1, len=oStringList.length; i<len; i++) { |
289 | | var s = oStringList[i]; |
290 | | camelizedString += s.charAt(0).toUpperCase() + s.substring(1); |
291 | | } |
292 | | return camelizedString; |
293 | | }, |
294 | | /** |
295 | | * Property: tokenRegEx |
296 | | * Used to find tokens in a string. |
297 | | * Examples: ${a}, ${a.b.c}, ${a-b}, ${5} |
298 | | */ |
299 | | tokenRegEx: /\$\{([\w.]+?)\}/g, |
300 | | /** |
301 | | * Property: numberRegEx |
302 | | * Used to test strings as numbers. |
303 | | */ |
304 | | numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/, |
305 | | /** |
306 | | * Function: isNumeric |
307 | | * Determine whether a string contains only a numeric value. |
308 | | * |
309 | | * Examples: |
310 | | * (code) |
311 | | * ZOO.String.isNumeric("6.02e23") // true |
312 | | * ZOO.String.isNumeric("12 dozen") // false |
313 | | * ZOO.String.isNumeric("4") // true |
314 | | * ZOO.String.isNumeric(" 4 ") // false |
315 | | * (end) |
316 | | * |
317 | | * Returns: |
318 | | * {Boolean} String contains only a number. |
319 | | */ |
320 | | isNumeric: function(value) { |
321 | | return ZOO.String.numberRegEx.test(value); |
322 | | }, |
323 | | /** |
324 | | * Function: numericIf |
325 | | * Converts a string that appears to be a numeric value into a number. |
326 | | * |
327 | | * Returns |
328 | | * {Number|String} a Number if the passed value is a number, a String |
329 | | * otherwise. |
330 | | */ |
331 | | numericIf: function(value) { |
332 | | return ZOO.String.isNumeric(value) ? parseFloat(value) : value; |
333 | | } |
334 | | }; |
335 | | |
336 | | /** |
337 | | * Class: ZOO.Request |
338 | | * Contains convenience methods for working with ZOORequest which |
339 | | * replace XMLHttpRequest. Because of we are not in a browser |
340 | | * JavaScript environment, ZOO Project provides a method to |
341 | | * query servers which is based on curl : ZOORequest. |
342 | | */ |
343 | | ZOO.Request = { |
344 | | /** |
345 | | * Function: GET |
346 | | * Send an HTTP GET request. |
347 | | * |
348 | | * Parameters: |
349 | | * url - {String} The URL to request. |
350 | | * params - {Object} Params to add to the url |
351 | | * |
352 | | * Returns: |
353 | | * {String} Request result. |
354 | | */ |
355 | | Get: function(url,params) { |
356 | | var paramsArray = []; |
357 | | for (var key in params) { |
358 | | var value = params[key]; |
359 | | if ((value != null) && (typeof value != 'function')) { |
360 | | var encodedValue; |
361 | | if (typeof value == 'object' && value.constructor == Array) { |
362 | | /* value is an array; encode items and separate with "," */ |
363 | | var encodedItemArray = []; |
364 | | for (var itemIndex=0, len=value.length; itemIndex<len; itemIndex++) { |
365 | | encodedItemArray.push(encodeURIComponent(value[itemIndex])); |
366 | | } |
367 | | encodedValue = encodedItemArray.join(","); |
368 | | } |
369 | | else { |
370 | | /* value is a string; simply encode */ |
371 | | encodedValue = encodeURIComponent(value); |
372 | | } |
373 | | paramsArray.push(encodeURIComponent(key) + "=" + encodedValue); |
374 | | } |
375 | | } |
376 | | var paramString = paramsArray.join("&"); |
377 | | if(paramString.length > 0) { |
378 | | var separator = (url.indexOf('?') > -1) ? '&' : '?'; |
379 | | url += separator + paramString; |
380 | | } |
381 | | return ZOORequest('GET',url); |
382 | | }, |
383 | | /** |
384 | | * Function: POST |
385 | | * Send an HTTP POST request. |
386 | | * |
387 | | * Parameters: |
388 | | * url - {String} The URL to request. |
389 | | * body - {String} The request's body to send. |
390 | | * headers - {Object} A key-value object of headers to push to |
391 | | * the request's head |
392 | | * |
393 | | * Returns: |
394 | | * {String} Request result. |
395 | | */ |
396 | | Post: function(url,body,headers) { |
397 | | if(!(headers instanceof Array)) { |
398 | | var headersArray = []; |
399 | | for (var name in headers) { |
400 | | headersArray.push(name+': '+headers[name]); |
401 | | } |
402 | | headers = headersArray; |
403 | | } |
404 | | return ZOORequest('POST',url,body,headers); |
405 | | } |
406 | | }; |
407 | | |
408 | | /** |
409 | | * Class: ZOO.Bounds |
410 | | * Instances of this class represent bounding boxes. Data stored as left, |
411 | | * bottom, right, top floats. All values are initialized to null, |
412 | | * however, you should make sure you set them before using the bounds |
413 | | * for anything. |
414 | | */ |
415 | | ZOO.Bounds = ZOO.Class({ |
416 | | /** |
417 | | * Property: left |
418 | | * {Number} Minimum horizontal coordinate. |
419 | | */ |
420 | | left: null, |
421 | | /** |
422 | | * Property: bottom |
423 | | * {Number} Minimum vertical coordinate. |
424 | | */ |
425 | | bottom: null, |
426 | | /** |
427 | | * Property: right |
428 | | * {Number} Maximum horizontal coordinate. |
429 | | */ |
430 | | right: null, |
431 | | /** |
432 | | * Property: top |
433 | | * {Number} Maximum vertical coordinate. |
434 | | */ |
435 | | top: null, |
436 | | /** |
437 | | * Constructor: ZOO.Bounds |
438 | | * Construct a new bounds object. |
439 | | * |
440 | | * Parameters: |
441 | | * left - {Number} The left bounds of the box. Note that for width |
442 | | * calculations, this is assumed to be less than the right value. |
443 | | * bottom - {Number} The bottom bounds of the box. Note that for height |
444 | | * calculations, this is assumed to be more than the top value. |
445 | | * right - {Number} The right bounds. |
446 | | * top - {Number} The top bounds. |
447 | | */ |
448 | | initialize: function(left, bottom, right, top) { |
449 | | if (left != null) |
450 | | this.left = parseFloat(left); |
451 | | if (bottom != null) |
452 | | this.bottom = parseFloat(bottom); |
453 | | if (right != null) |
454 | | this.right = parseFloat(right); |
455 | | if (top != null) |
456 | | this.top = parseFloat(top); |
457 | | }, |
458 | | /** |
459 | | * Method: clone |
460 | | * Create a cloned instance of this bounds. |
461 | | * |
462 | | * Returns: |
463 | | * {<ZOO.Bounds>} A fresh copy of the bounds |
464 | | */ |
465 | | clone:function() { |
466 | | return new ZOO.Bounds(this.left, this.bottom, |
467 | | this.right, this.top); |
468 | | }, |
469 | | /** |
470 | | * Method: equals |
471 | | * Test a two bounds for equivalence. |
472 | | * |
473 | | * Parameters: |
474 | | * bounds - {<ZOO.Bounds>} |
475 | | * |
476 | | * Returns: |
477 | | * {Boolean} The passed-in bounds object has the same left, |
478 | | * right, top, bottom components as this. Note that if bounds |
479 | | * passed in is null, returns false. |
480 | | */ |
481 | | equals:function(bounds) { |
482 | | var equals = false; |
483 | | if (bounds != null) |
484 | | equals = ((this.left == bounds.left) && |
485 | | (this.right == bounds.right) && |
486 | | (this.top == bounds.top) && |
487 | | (this.bottom == bounds.bottom)); |
488 | | return equals; |
489 | | }, |
490 | | /** |
491 | | * Method: toString |
492 | | * |
493 | | * Returns: |
494 | | * {String} String representation of bounds object. |
495 | | * (ex.<i>"left-bottom=(5,42) right-top=(10,45)"</i>) |
496 | | */ |
497 | | toString:function() { |
498 | | return ( "left-bottom=(" + this.left + "," + this.bottom + ")" |
499 | | + " right-top=(" + this.right + "," + this.top + ")" ); |
500 | | }, |
501 | | /** |
502 | | * APIMethod: toArray |
503 | | * |
504 | | * Returns: |
505 | | * {Array} array of left, bottom, right, top |
506 | | */ |
507 | | toArray: function() { |
508 | | return [this.left, this.bottom, this.right, this.top]; |
509 | | }, |
510 | | /** |
511 | | * Method: toBBOX |
512 | | * |
513 | | * Parameters: |
514 | | * decimal - {Integer} How many significant digits in the bbox coords? |
515 | | * Default is 6 |
516 | | * |
517 | | * Returns: |
518 | | * {String} Simple String representation of bounds object. |
519 | | * (ex. <i>"5,42,10,45"</i>) |
520 | | */ |
521 | | toBBOX:function(decimal) { |
522 | | if (decimal== null) |
523 | | decimal = 6; |
524 | | var mult = Math.pow(10, decimal); |
525 | | var bbox = Math.round(this.left * mult) / mult + "," + |
526 | | Math.round(this.bottom * mult) / mult + "," + |
527 | | Math.round(this.right * mult) / mult + "," + |
528 | | Math.round(this.top * mult) / mult; |
529 | | return bbox; |
530 | | }, |
531 | | /** |
532 | | * Method: toGeometry |
533 | | * Create a new polygon geometry based on this bounds. |
534 | | * |
535 | | * Returns: |
536 | | * {<ZOO.Geometry.Polygon>} A new polygon with the coordinates |
537 | | * of this bounds. |
538 | | */ |
539 | | toGeometry: function() { |
540 | | return new ZOO.Geometry.Polygon([ |
541 | | new ZOO.Geometry.LinearRing([ |
542 | | new ZOO.Geometry.Point(this.left, this.bottom), |
543 | | new ZOO.Geometry.Point(this.right, this.bottom), |
544 | | new ZOO.Geometry.Point(this.right, this.top), |
545 | | new ZOO.Geometry.Point(this.left, this.top) |
546 | | ]) |
547 | | ]); |
548 | | }, |
549 | | /** |
550 | | * Method: getWidth |
551 | | * |
552 | | * Returns: |
553 | | * {Float} The width of the bounds |
554 | | */ |
555 | | getWidth:function() { |
556 | | return (this.right - this.left); |
557 | | }, |
558 | | /** |
559 | | * Method: getHeight |
560 | | * |
561 | | * Returns: |
562 | | * {Float} The height of the bounds (top minus bottom). |
563 | | */ |
564 | | getHeight:function() { |
565 | | return (this.top - this.bottom); |
566 | | }, |
567 | | /** |
568 | | * Method: add |
569 | | * |
570 | | * Parameters: |
571 | | * x - {Float} |
572 | | * y - {Float} |
573 | | * |
574 | | * Returns: |
575 | | * {<ZOO.Bounds>} A new bounds whose coordinates are the same as |
576 | | * this, but shifted by the passed-in x and y values. |
577 | | */ |
578 | | add:function(x, y) { |
579 | | if ( (x == null) || (y == null) ) |
580 | | return null; |
581 | | return new ZOO.Bounds(this.left + x, this.bottom + y, |
582 | | this.right + x, this.top + y); |
583 | | }, |
584 | | /** |
585 | | * Method: extend |
586 | | * Extend the bounds to include the point, lonlat, or bounds specified. |
587 | | * Note, this function assumes that left < right and bottom < top. |
588 | | * |
589 | | * Parameters: |
590 | | * object - {Object} Can be Point, or Bounds |
591 | | */ |
592 | | extend:function(object) { |
593 | | var bounds = null; |
594 | | if (object) { |
595 | | // clear cached center location |
596 | | switch(object.CLASS_NAME) { |
597 | | case "ZOO.Geometry.Point": |
598 | | bounds = new ZOO.Bounds(object.x, object.y, |
599 | | object.x, object.y); |
600 | | break; |
601 | | case "ZOO.Bounds": |
602 | | bounds = object; |
603 | | break; |
604 | | } |
605 | | if (bounds) { |
606 | | if ( (this.left == null) || (bounds.left < this.left)) |
607 | | this.left = bounds.left; |
608 | | if ( (this.bottom == null) || (bounds.bottom < this.bottom) ) |
609 | | this.bottom = bounds.bottom; |
610 | | if ( (this.right == null) || (bounds.right > this.right) ) |
611 | | this.right = bounds.right; |
612 | | if ( (this.top == null) || (bounds.top > this.top) ) |
613 | | this.top = bounds.top; |
614 | | } |
615 | | } |
616 | | }, |
617 | | /** |
618 | | * APIMethod: contains |
619 | | * |
620 | | * Parameters: |
621 | | * x - {Float} |
622 | | * y - {Float} |
623 | | * inclusive - {Boolean} Whether or not to include the border. |
624 | | * Default is true. |
625 | | * |
626 | | * Returns: |
627 | | * {Boolean} Whether or not the passed-in coordinates are within this |
628 | | * bounds. |
629 | | */ |
630 | | contains:function(x, y, inclusive) { |
631 | | //set default |
632 | | if (inclusive == null) |
633 | | inclusive = true; |
634 | | if (x == null || y == null) |
635 | | return false; |
636 | | x = parseFloat(x); |
637 | | y = parseFloat(y); |
638 | | |
639 | | var contains = false; |
640 | | if (inclusive) |
641 | | contains = ((x >= this.left) && (x <= this.right) && |
642 | | (y >= this.bottom) && (y <= this.top)); |
643 | | else |
644 | | contains = ((x > this.left) && (x < this.right) && |
645 | | (y > this.bottom) && (y < this.top)); |
646 | | return contains; |
647 | | }, |
648 | | /** |
649 | | * Method: intersectsBounds |
650 | | * Determine whether the target bounds intersects this bounds. Bounds are |
651 | | * considered intersecting if any of their edges intersect or if one |
652 | | * bounds contains the other. |
653 | | * |
654 | | * Parameters: |
655 | | * bounds - {<ZOO.Bounds>} The target bounds. |
656 | | * inclusive - {Boolean} Treat coincident borders as intersecting. Default |
657 | | * is true. If false, bounds that do not overlap but only touch at the |
658 | | * border will not be considered as intersecting. |
659 | | * |
660 | | * Returns: |
661 | | * {Boolean} The passed-in bounds object intersects this bounds. |
662 | | */ |
663 | | intersectsBounds:function(bounds, inclusive) { |
664 | | if (inclusive == null) |
665 | | inclusive = true; |
666 | | var intersects = false; |
667 | | var mightTouch = ( |
668 | | this.left == bounds.right || |
669 | | this.right == bounds.left || |
670 | | this.top == bounds.bottom || |
671 | | this.bottom == bounds.top |
672 | | ); |
673 | | if (inclusive || !mightTouch) { |
674 | | var inBottom = ( |
675 | | ((bounds.bottom >= this.bottom) && (bounds.bottom <= this.top)) || |
676 | | ((this.bottom >= bounds.bottom) && (this.bottom <= bounds.top)) |
677 | | ); |
678 | | var inTop = ( |
679 | | ((bounds.top >= this.bottom) && (bounds.top <= this.top)) || |
680 | | ((this.top > bounds.bottom) && (this.top < bounds.top)) |
681 | | ); |
682 | | var inLeft = ( |
683 | | ((bounds.left >= this.left) && (bounds.left <= this.right)) || |
684 | | ((this.left >= bounds.left) && (this.left <= bounds.right)) |
685 | | ); |
686 | | var inRight = ( |
687 | | ((bounds.right >= this.left) && (bounds.right <= this.right)) || |
688 | | ((this.right >= bounds.left) && (this.right <= bounds.right)) |
689 | | ); |
690 | | intersects = ((inBottom || inTop) && (inLeft || inRight)); |
691 | | } |
692 | | return intersects; |
693 | | }, |
694 | | /** |
695 | | * Method: containsBounds |
696 | | * Determine whether the target bounds is contained within this bounds. |
697 | | * |
698 | | * bounds - {<ZOO.Bounds>} The target bounds. |
699 | | * partial - {Boolean} If any of the target corners is within this bounds |
700 | | * consider the bounds contained. Default is false. If true, the |
701 | | * entire target bounds must be contained within this bounds. |
702 | | * inclusive - {Boolean} Treat shared edges as contained. Default is |
703 | | * true. |
704 | | * |
705 | | * Returns: |
706 | | * {Boolean} The passed-in bounds object is contained within this bounds. |
707 | | */ |
708 | | containsBounds:function(bounds, partial, inclusive) { |
709 | | if (partial == null) |
710 | | partial = false; |
711 | | if (inclusive == null) |
712 | | inclusive = true; |
713 | | var bottomLeft = this.contains(bounds.left, bounds.bottom, inclusive); |
714 | | var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive); |
715 | | var topLeft = this.contains(bounds.left, bounds.top, inclusive); |
716 | | var topRight = this.contains(bounds.right, bounds.top, inclusive); |
717 | | return (partial) ? (bottomLeft || bottomRight || topLeft || topRight) |
718 | | : (bottomLeft && bottomRight && topLeft && topRight); |
719 | | }, |
720 | | CLASS_NAME: 'ZOO.Bounds' |
721 | | }); |
722 | | |
723 | | /** |
724 | | * Class: ZOO.Projection |
725 | | * Class for coordinate transforms between coordinate systems. |
726 | | * Depends on the zoo-proj4js library. zoo-proj4js library |
727 | | * is loaded by the ZOO Kernel with zoo-api. |
728 | | */ |
729 | | ZOO.Projection = ZOO.Class({ |
730 | | /** |
731 | | * Property: proj |
732 | | * {Object} Proj4js.Proj instance. |
733 | | */ |
734 | | proj: null, |
735 | | /** |
736 | | * Property: projCode |
737 | | * {String} |
738 | | */ |
739 | | projCode: null, |
740 | | /** |
741 | | * Constructor: ZOO.Projection |
742 | | * This class offers several methods for interacting with a wrapped |
743 | | * zoo-pro4js projection object. |
744 | | * |
745 | | * Parameters: |
746 | | * projCode - {String} A string identifying the Well Known Identifier for |
747 | | * the projection. |
748 | | * options - {Object} An optional object to set additional properties. |
749 | | * |
750 | | * Returns: |
751 | | * {<ZOO.Projection>} A projection object. |
752 | | */ |
753 | | initialize: function(projCode, options) { |
754 | | ZOO.extend(this, options); |
755 | | this.projCode = projCode; |
756 | | if (Proj4js) { |
757 | | this.proj = new Proj4js.Proj(projCode); |
758 | | } |
759 | | }, |
760 | | /** |
761 | | * Method: getCode |
762 | | * Get the string SRS code. |
763 | | * |
764 | | * Returns: |
765 | | * {String} The SRS code. |
766 | | */ |
767 | | getCode: function() { |
768 | | return this.proj ? this.proj.srsCode : this.projCode; |
769 | | }, |
770 | | /** |
771 | | * Method: getUnits |
772 | | * Get the units string for the projection -- returns null if |
773 | | * zoo-proj4js is not available. |
774 | | * |
775 | | * Returns: |
776 | | * {String} The units abbreviation. |
777 | | */ |
778 | | getUnits: function() { |
779 | | return this.proj ? this.proj.units : null; |
780 | | }, |
781 | | /** |
782 | | * Method: toString |
783 | | * Convert projection to string (getCode wrapper). |
784 | | * |
785 | | * Returns: |
786 | | * {String} The projection code. |
787 | | */ |
788 | | toString: function() { |
789 | | return this.getCode(); |
790 | | }, |
791 | | /** |
792 | | * Method: equals |
793 | | * Test equality of two projection instances. Determines equality based |
794 | | * soley on the projection code. |
795 | | * |
796 | | * Returns: |
797 | | * {Boolean} The two projections are equivalent. |
798 | | */ |
799 | | equals: function(projection) { |
800 | | if (projection && projection.getCode) |
801 | | return this.getCode() == projection.getCode(); |
802 | | else |
803 | | return false; |
804 | | }, |
805 | | /* Method: destroy |
806 | | * Destroy projection object. |
807 | | */ |
808 | | destroy: function() { |
809 | | this.proj = null; |
810 | | this.projCode = null; |
811 | | }, |
812 | | CLASS_NAME: 'ZOO.Projection' |
813 | | }); |
814 | | /** |
815 | | * Method: transform |
816 | | * Transform a point coordinate from one projection to another. Note that |
817 | | * the input point is transformed in place. |
818 | | * |
819 | | * Parameters: |
820 | | * point - {{ZOO.Geometry.Point> | Object} An object with x and y |
821 | | * properties representing coordinates in those dimensions. |
822 | | * sourceProj - {ZOO.Projection} Source map coordinate system |
823 | | * destProj - {ZOO.Projection} Destination map coordinate system |
824 | | * |
825 | | * Returns: |
826 | | * point - {object} A transformed coordinate. The original point is modified. |
827 | | */ |
828 | | ZOO.Projection.transform = function(point, source, dest) { |
829 | | if (source.proj && dest.proj) |
830 | | point = Proj4js.transform(source.proj, dest.proj, point); |
831 | | return point; |
832 | | }; |
833 | | |
834 | | /** |
835 | | * Class: ZOO.Format |
836 | | * Base class for format reading/writing a variety of formats. Subclasses |
837 | | * of ZOO.Format are expected to have read and write methods. |
838 | | */ |
839 | | ZOO.Format = ZOO.Class({ |
840 | | /** |
841 | | * Property: options |
842 | | * {Object} A reference to options passed to the constructor. |
843 | | */ |
844 | | options:null, |
845 | | /** |
846 | | * Property: externalProjection |
847 | | * {<ZOO.Projection>} When passed a externalProjection and |
848 | | * internalProjection, the format will reproject the geometries it |
849 | | * reads or writes. The externalProjection is the projection used by |
850 | | * the content which is passed into read or which comes out of write. |
851 | | * In order to reproject, a projection transformation function for the |
852 | | * specified projections must be available. This support is provided |
853 | | * via zoo-proj4js. |
854 | | */ |
855 | | externalProjection: null, |
856 | | /** |
857 | | * Property: internalProjection |
858 | | * {<ZOO.Projection>} When passed a externalProjection and |
859 | | * internalProjection, the format will reproject the geometries it |
860 | | * reads or writes. The internalProjection is the projection used by |
861 | | * the geometries which are returned by read or which are passed into |
862 | | * write. In order to reproject, a projection transformation function |
863 | | * for the specified projections must be available. This support is |
864 | | * provided via zoo-proj4js. |
865 | | */ |
866 | | internalProjection: null, |
867 | | /** |
868 | | * Property: data |
869 | | * {Object} When <keepData> is true, this is the parsed string sent to |
870 | | * <read>. |
871 | | */ |
872 | | data: null, |
873 | | /** |
874 | | * Property: keepData |
875 | | * {Object} Maintain a reference (<data>) to the most recently read data. |
876 | | * Default is false. |
877 | | */ |
878 | | keepData: false, |
879 | | /** |
880 | | * Constructor: ZOO.Format |
881 | | * Instances of this class are not useful. See one of the subclasses. |
882 | | * |
883 | | * Parameters: |
884 | | * options - {Object} An optional object with properties to set on the |
885 | | * format |
886 | | * |
887 | | * Valid options: |
888 | | * keepData - {Boolean} If true, upon <read>, the data property will be |
889 | | * set to the parsed object (e.g. the json or xml object). |
890 | | * |
891 | | * Returns: |
892 | | * An instance of ZOO.Format |
893 | | */ |
894 | | initialize: function(options) { |
895 | | ZOO.extend(this, options); |
896 | | this.options = options; |
897 | | }, |
898 | | /** |
899 | | * Method: destroy |
900 | | * Clean up. |
901 | | */ |
902 | | destroy: function() { |
903 | | }, |
904 | | /** |
905 | | * Method: read |
906 | | * Read data from a string, and return an object whose type depends on the |
907 | | * subclass. |
908 | | * |
909 | | * Parameters: |
910 | | * data - {string} Data to read/parse. |
911 | | * |
912 | | * Returns: |
913 | | * Depends on the subclass |
914 | | */ |
915 | | read: function(data) { |
916 | | }, |
917 | | /** |
918 | | * Method: write |
919 | | * Accept an object, and return a string. |
920 | | * |
921 | | * Parameters: |
922 | | * object - {Object} Object to be serialized |
923 | | * |
924 | | * Returns: |
925 | | * {String} A string representation of the object. |
926 | | */ |
927 | | write: function(data) { |
928 | | }, |
929 | | CLASS_NAME: 'ZOO.Format' |
930 | | }); |
931 | | /** |
932 | | * Class: ZOO.Format.WKT |
933 | | * Class for reading and writing Well-Known Text. Create a new instance |
934 | | * with the <ZOO.Format.WKT> constructor. |
935 | | * |
936 | | * Inherits from: |
937 | | * - <ZOO.Format> |
938 | | */ |
939 | | ZOO.Format.WKT = ZOO.Class(ZOO.Format, { |
940 | | /** |
941 | | * Constructor: ZOO.Format.WKT |
942 | | * Create a new parser for WKT |
943 | | * |
944 | | * Parameters: |
945 | | * options - {Object} An optional object whose properties will be set on |
946 | | * this instance |
947 | | * |
948 | | * Returns: |
949 | | * {<ZOO.Format.WKT>} A new WKT parser. |
950 | | */ |
951 | | initialize: function(options) { |
952 | | this.regExes = { |
953 | | 'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/, |
954 | | 'spaces': /\s+/, |
955 | | 'parenComma': /\)\s*,\s*\(/, |
956 | | 'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here |
957 | | 'trimParens': /^\s*\(?(.*?)\)?\s*$/ |
958 | | }; |
959 | | ZOO.Format.prototype.initialize.apply(this, [options]); |
960 | | }, |
961 | | /** |
962 | | * Method: read |
963 | | * Deserialize a WKT string and return a vector feature or an |
964 | | * array of vector features. Supports WKT for POINT, |
965 | | * MULTIPOINT, LINESTRING, MULTILINESTRING, POLYGON, |
966 | | * MULTIPOLYGON, and GEOMETRYCOLLECTION. |
967 | | * |
968 | | * Parameters: |
969 | | * wkt - {String} A WKT string |
970 | | * |
971 | | * Returns: |
972 | | * {<ZOO.Feature.Vector>|Array} A feature or array of features for |
973 | | * GEOMETRYCOLLECTION WKT. |
974 | | */ |
975 | | read: function(wkt) { |
976 | | var features, type, str; |
977 | | var matches = this.regExes.typeStr.exec(wkt); |
978 | | if(matches) { |
979 | | type = matches[1].toLowerCase(); |
980 | | str = matches[2]; |
981 | | if(this.parse[type]) { |
982 | | features = this.parse[type].apply(this, [str]); |
983 | | } |
984 | | if (this.internalProjection && this.externalProjection) { |
985 | | if (features && |
986 | | features.CLASS_NAME == "ZOO.Feature") { |
987 | | features.geometry.transform(this.externalProjection, |
988 | | this.internalProjection); |
989 | | } else if (features && |
990 | | type != "geometrycollection" && |
991 | | typeof features == "object") { |
992 | | for (var i=0, len=features.length; i<len; i++) { |
993 | | var component = features[i]; |
994 | | component.geometry.transform(this.externalProjection, |
995 | | this.internalProjection); |
996 | | } |
997 | | } |
998 | | } |
999 | | } |
1000 | | return features; |
1001 | | }, |
1002 | | /** |
1003 | | * Method: write |
1004 | | * Serialize a feature or array of features into a WKT string. |
1005 | | * |
1006 | | * Parameters: |
1007 | | * features - {<ZOO.Feature.Vector>|Array} A feature or array of |
1008 | | * features |
1009 | | * |
1010 | | * Returns: |
1011 | | * {String} The WKT string representation of the input geometries |
1012 | | */ |
1013 | | write: function(features) { |
1014 | | var collection, geometry, type, data, isCollection; |
1015 | | if(features.constructor == Array) { |
1016 | | collection = features; |
1017 | | isCollection = true; |
1018 | | } else { |
1019 | | collection = [features]; |
1020 | | isCollection = false; |
1021 | | } |
1022 | | var pieces = []; |
1023 | | if(isCollection) |
1024 | | pieces.push('GEOMETRYCOLLECTION('); |
1025 | | for(var i=0, len=collection.length; i<len; ++i) { |
1026 | | if(isCollection && i>0) |
1027 | | pieces.push(','); |
1028 | | geometry = collection[i].geometry; |
1029 | | type = geometry.CLASS_NAME.split('.')[2].toLowerCase(); |
1030 | | if(!this.extract[type]) |
1031 | | return null; |
1032 | | if (this.internalProjection && this.externalProjection) { |
1033 | | geometry = geometry.clone(); |
1034 | | geometry.transform(this.internalProjection, |
1035 | | this.externalProjection); |
1036 | | } |
1037 | | data = this.extract[type].apply(this, [geometry]); |
1038 | | pieces.push(type.toUpperCase() + '(' + data + ')'); |
1039 | | } |
1040 | | if(isCollection) |
1041 | | pieces.push(')'); |
1042 | | return pieces.join(''); |
1043 | | }, |
1044 | | /** |
1045 | | * Property: extract |
1046 | | * Object with properties corresponding to the geometry types. |
1047 | | * Property values are functions that do the actual data extraction. |
1048 | | */ |
1049 | | extract: { |
1050 | | /** |
1051 | | * Return a space delimited string of point coordinates. |
1052 | | * @param {<ZOO.Geometry.Point>} point |
1053 | | * @returns {String} A string of coordinates representing the point |
1054 | | */ |
1055 | | 'point': function(point) { |
1056 | | return point.x + ' ' + point.y; |
1057 | | }, |
1058 | | /** |
1059 | | * Return a comma delimited string of point coordinates from a multipoint. |
1060 | | * @param {<ZOO.Geometry.MultiPoint>} multipoint |
1061 | | * @returns {String} A string of point coordinate strings representing |
1062 | | * the multipoint |
1063 | | */ |
1064 | | 'multipoint': function(multipoint) { |
1065 | | var array = []; |
1066 | | for(var i=0, len=multipoint.components.length; i<len; ++i) { |
1067 | | array.push(this.extract.point.apply(this, [multipoint.components[i]])); |
1068 | | } |
1069 | | return array.join(','); |
1070 | | }, |
1071 | | /** |
1072 | | * Return a comma delimited string of point coordinates from a line. |
1073 | | * @param {<ZOO.Geometry.LineString>} linestring |
1074 | | * @returns {String} A string of point coordinate strings representing |
1075 | | * the linestring |
1076 | | */ |
1077 | | 'linestring': function(linestring) { |
1078 | | var array = []; |
1079 | | for(var i=0, len=linestring.components.length; i<len; ++i) { |
1080 | | array.push(this.extract.point.apply(this, [linestring.components[i]])); |
1081 | | } |
1082 | | return array.join(','); |
1083 | | }, |
1084 | | /** |
1085 | | * Return a comma delimited string of linestring strings from a multilinestring. |
1086 | | * @param {<ZOO.Geometry.MultiLineString>} multilinestring |
1087 | | * @returns {String} A string of of linestring strings representing |
1088 | | * the multilinestring |
1089 | | */ |
1090 | | 'multilinestring': function(multilinestring) { |
1091 | | var array = []; |
1092 | | for(var i=0, len=multilinestring.components.length; i<len; ++i) { |
1093 | | array.push('(' + |
1094 | | this.extract.linestring.apply(this, [multilinestring.components[i]]) + |
1095 | | ')'); |
1096 | | } |
1097 | | return array.join(','); |
1098 | | }, |
1099 | | /** |
1100 | | * Return a comma delimited string of linear ring arrays from a polygon. |
1101 | | * @param {<ZOO.Geometry.Polygon>} polygon |
1102 | | * @returns {String} An array of linear ring arrays representing the polygon |
1103 | | */ |
1104 | | 'polygon': function(polygon) { |
1105 | | var array = []; |
1106 | | for(var i=0, len=polygon.components.length; i<len; ++i) { |
1107 | | array.push('(' + |
1108 | | this.extract.linestring.apply(this, [polygon.components[i]]) + |
1109 | | ')'); |
1110 | | } |
1111 | | return array.join(','); |
1112 | | }, |
1113 | | /** |
1114 | | * Return an array of polygon arrays from a multipolygon. |
1115 | | * @param {<ZOO.Geometry.MultiPolygon>} multipolygon |
1116 | | * @returns {Array} An array of polygon arrays representing |
1117 | | * the multipolygon |
1118 | | */ |
1119 | | 'multipolygon': function(multipolygon) { |
1120 | | var array = []; |
1121 | | for(var i=0, len=multipolygon.components.length; i<len; ++i) { |
1122 | | array.push('(' + |
1123 | | this.extract.polygon.apply(this, [multipolygon.components[i]]) + |
1124 | | ')'); |
1125 | | } |
1126 | | return array.join(','); |
1127 | | } |
1128 | | }, |
1129 | | /** |
1130 | | * Property: parse |
1131 | | * Object with properties corresponding to the geometry types. |
1132 | | * Property values are functions that do the actual parsing. |
1133 | | */ |
1134 | | parse: { |
1135 | | /** |
1136 | | * Method: parse.point |
1137 | | * Return point feature given a point WKT fragment. |
1138 | | * |
1139 | | * Parameters: |
1140 | | * str - {String} A WKT fragment representing the point |
1141 | | * Returns: |
1142 | | * {<ZOO.Feature>} A point feature |
1143 | | */ |
1144 | | 'point': function(str) { |
1145 | | var coords = ZOO.String.trim(str).split(this.regExes.spaces); |
1146 | | return new ZOO.Feature( |
1147 | | new ZOO.Geometry.Point(coords[0], coords[1]) |
1148 | | ); |
1149 | | }, |
1150 | | /** |
1151 | | * Method: parse.multipoint |
1152 | | * Return a multipoint feature given a multipoint WKT fragment. |
1153 | | * |
1154 | | * Parameters: |
1155 | | * str - {String} A WKT fragment representing the multipoint |
1156 | | * |
1157 | | * Returns: |
1158 | | * {<ZOO.Feature>} A multipoint feature |
1159 | | */ |
1160 | | 'multipoint': function(str) { |
1161 | | var points = ZOO.String.trim(str).split(','); |
1162 | | var components = []; |
1163 | | for(var i=0, len=points.length; i<len; ++i) { |
1164 | | components.push(this.parse.point.apply(this, [points[i]]).geometry); |
1165 | | } |
1166 | | return new ZOO.Feature( |
1167 | | new ZOO.Geometry.MultiPoint(components) |
1168 | | ); |
1169 | | }, |
1170 | | /** |
1171 | | * Method: parse.linestring |
1172 | | * Return a linestring feature given a linestring WKT fragment. |
1173 | | * |
1174 | | * Parameters: |
1175 | | * str - {String} A WKT fragment representing the linestring |
1176 | | * |
1177 | | * Returns: |
1178 | | * {<ZOO.Feature>} A linestring feature |
1179 | | */ |
1180 | | 'linestring': function(str) { |
1181 | | var points = ZOO.String.trim(str).split(','); |
1182 | | var components = []; |
1183 | | for(var i=0, len=points.length; i<len; ++i) { |
1184 | | components.push(this.parse.point.apply(this, [points[i]]).geometry); |
1185 | | } |
1186 | | return new ZOO.Feature( |
1187 | | new ZOO.Geometry.LineString(components) |
1188 | | ); |
1189 | | }, |
1190 | | /** |
1191 | | * Method: parse.multilinestring |
1192 | | * Return a multilinestring feature given a multilinestring WKT fragment. |
1193 | | * |
1194 | | * Parameters: |
1195 | | * str - {String} A WKT fragment representing the multilinestring |
1196 | | * |
1197 | | * Returns: |
1198 | | * {<ZOO.Feature>} A multilinestring feature |
1199 | | */ |
1200 | | 'multilinestring': function(str) { |
1201 | | var line; |
1202 | | var lines = ZOO.String.trim(str).split(this.regExes.parenComma); |
1203 | | var components = []; |
1204 | | for(var i=0, len=lines.length; i<len; ++i) { |
1205 | | line = lines[i].replace(this.regExes.trimParens, '$1'); |
1206 | | components.push(this.parse.linestring.apply(this, [line]).geometry); |
1207 | | } |
1208 | | return new ZOO.Feature( |
1209 | | new ZOO.Geometry.MultiLineString(components) |
1210 | | ); |
1211 | | }, |
1212 | | /** |
1213 | | * Method: parse.polygon |
1214 | | * Return a polygon feature given a polygon WKT fragment. |
1215 | | * |
1216 | | * Parameters: |
1217 | | * str - {String} A WKT fragment representing the polygon |
1218 | | * |
1219 | | * Returns: |
1220 | | * {<ZOO.Feature>} A polygon feature |
1221 | | */ |
1222 | | 'polygon': function(str) { |
1223 | | var ring, linestring, linearring; |
1224 | | var rings = ZOO.String.trim(str).split(this.regExes.parenComma); |
1225 | | var components = []; |
1226 | | for(var i=0, len=rings.length; i<len; ++i) { |
1227 | | ring = rings[i].replace(this.regExes.trimParens, '$1'); |
1228 | | linestring = this.parse.linestring.apply(this, [ring]).geometry; |
1229 | | linearring = new ZOO.Geometry.LinearRing(linestring.components); |
1230 | | components.push(linearring); |
1231 | | } |
1232 | | return new ZOO.Feature( |
1233 | | new ZOO.Geometry.Polygon(components) |
1234 | | ); |
1235 | | }, |
1236 | | /** |
1237 | | * Method: parse.multipolygon |
1238 | | * Return a multipolygon feature given a multipolygon WKT fragment. |
1239 | | * |
1240 | | * Parameters: |
1241 | | * str - {String} A WKT fragment representing the multipolygon |
1242 | | * |
1243 | | * Returns: |
1244 | | * {<ZOO.Feature>} A multipolygon feature |
1245 | | */ |
1246 | | 'multipolygon': function(str) { |
1247 | | var polygon; |
1248 | | var polygons = ZOO.String.trim(str).split(this.regExes.doubleParenComma); |
1249 | | var components = []; |
1250 | | for(var i=0, len=polygons.length; i<len; ++i) { |
1251 | | polygon = polygons[i].replace(this.regExes.trimParens, '$1'); |
1252 | | components.push(this.parse.polygon.apply(this, [polygon]).geometry); |
1253 | | } |
1254 | | return new ZOO.Feature( |
1255 | | new ZOO.Geometry.MultiPolygon(components) |
1256 | | ); |
1257 | | }, |
1258 | | /** |
1259 | | * Method: parse.geometrycollection |
1260 | | * Return an array of features given a geometrycollection WKT fragment. |
1261 | | * |
1262 | | * Parameters: |
1263 | | * str - {String} A WKT fragment representing the geometrycollection |
1264 | | * |
1265 | | * Returns: |
1266 | | * {Array} An array of ZOO.Feature |
1267 | | */ |
1268 | | 'geometrycollection': function(str) { |
1269 | | // separate components of the collection with | |
1270 | | str = str.replace(/,\s*([A-Za-z])/g, '|$1'); |
1271 | | var wktArray = ZOO.String.trim(str).split('|'); |
1272 | | var components = []; |
1273 | | for(var i=0, len=wktArray.length; i<len; ++i) { |
1274 | | components.push(ZOO.Format.WKT.prototype.read.apply(this,[wktArray[i]])); |
1275 | | } |
1276 | | return components; |
1277 | | } |
1278 | | }, |
1279 | | CLASS_NAME: 'ZOO.Format.WKT' |
1280 | | }); |
1281 | | /** |
1282 | | * Class: ZOO.Format.JSON |
1283 | | * A parser to read/write JSON safely. Create a new instance with the |
1284 | | * <ZOO.Format.JSON> constructor. |
1285 | | * |
1286 | | * Inherits from: |
1287 | | * - <ZOO.Format> |
1288 | | */ |
1289 | | ZOO.Format.JSON = ZOO.Class(ZOO.Format, { |
1290 | | /** |
1291 | | * Property: indent |
1292 | | * {String} For "pretty" printing, the indent string will be used once for |
1293 | | * each indentation level. |
1294 | | */ |
1295 | | indent: " ", |
1296 | | /** |
1297 | | * Property: space |
1298 | | * {String} For "pretty" printing, the space string will be used after |
1299 | | * the ":" separating a name/value pair. |
1300 | | */ |
1301 | | space: " ", |
1302 | | /** |
1303 | | * Property: newline |
1304 | | * {String} For "pretty" printing, the newline string will be used at the |
1305 | | * end of each name/value pair or array item. |
1306 | | */ |
1307 | | newline: "\n", |
1308 | | /** |
1309 | | * Property: level |
1310 | | * {Integer} For "pretty" printing, this is incremented/decremented during |
1311 | | * serialization. |
1312 | | */ |
1313 | | level: 0, |
1314 | | /** |
1315 | | * Property: pretty |
1316 | | * {Boolean} Serialize with extra whitespace for structure. This is set |
1317 | | * by the <write> method. |
1318 | | */ |
1319 | | pretty: false, |
1320 | | /** |
1321 | | * Constructor: ZOO.Format.JSON |
1322 | | * Create a new parser for JSON. |
1323 | | * |
1324 | | * Parameters: |
1325 | | * options - {Object} An optional object whose properties will be set on |
1326 | | * this instance. |
1327 | | */ |
1328 | | initialize: function(options) { |
1329 | | ZOO.Format.prototype.initialize.apply(this, [options]); |
1330 | | }, |
1331 | | /** |
1332 | | * Method: read |
1333 | | * Deserialize a json string. |
1334 | | * |
1335 | | * Parameters: |
1336 | | * json - {String} A JSON string |
1337 | | * filter - {Function} A function which will be called for every key and |
1338 | | * value at every level of the final result. Each value will be |
1339 | | * replaced by the result of the filter function. This can be used to |
1340 | | * reform generic objects into instances of classes, or to transform |
1341 | | * date strings into Date objects. |
1342 | | * |
1343 | | * Returns: |
1344 | | * {Object} An object, array, string, or number . |
1345 | | */ |
1346 | | read: function(json, filter) { |
1347 | | /** |
1348 | | * Parsing happens in three stages. In the first stage, we run the text |
1349 | | * against a regular expression which looks for non-JSON |
1350 | | * characters. We are especially concerned with '()' and 'new' |
1351 | | * because they can cause invocation, and '=' because it can cause |
1352 | | * mutation. But just to be safe, we will reject all unexpected |
1353 | | * characters. |
1354 | | */ |
1355 | | try { |
1356 | | if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@'). |
1357 | | replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). |
1358 | | replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { |
1359 | | /** |
1360 | | * In the second stage we use the eval function to compile the |
1361 | | * text into a JavaScript structure. The '{' operator is |
1362 | | * subject to a syntactic ambiguity in JavaScript - it can |
1363 | | * begin a block or an object literal. We wrap the text in |
1364 | | * parens to eliminate the ambiguity. |
1365 | | */ |
1366 | | var object = eval('(' + json + ')'); |
1367 | | /** |
1368 | | * In the optional third stage, we recursively walk the new |
1369 | | * structure, passing each name/value pair to a filter |
1370 | | * function for possible transformation. |
1371 | | */ |
1372 | | if(typeof filter === 'function') { |
1373 | | function walk(k, v) { |
1374 | | if(v && typeof v === 'object') { |
1375 | | for(var i in v) { |
1376 | | if(v.hasOwnProperty(i)) { |
1377 | | v[i] = walk(i, v[i]); |
1378 | | } |
1379 | | } |
1380 | | } |
1381 | | return filter(k, v); |
1382 | | } |
1383 | | object = walk('', object); |
1384 | | } |
1385 | | if(this.keepData) { |
1386 | | this.data = object; |
1387 | | } |
1388 | | return object; |
1389 | | } |
1390 | | } catch(e) { |
1391 | | // Fall through if the regexp test fails. |
1392 | | } |
1393 | | return null; |
1394 | | }, |
1395 | | /** |
1396 | | * Method: write |
1397 | | * Serialize an object into a JSON string. |
1398 | | * |
1399 | | * Parameters: |
1400 | | * value - {String} The object, array, string, number, boolean or date |
1401 | | * to be serialized. |
1402 | | * pretty - {Boolean} Structure the output with newlines and indentation. |
1403 | | * Default is false. |
1404 | | * |
1405 | | * Returns: |
1406 | | * {String} The JSON string representation of the input value. |
1407 | | */ |
1408 | | write: function(value, pretty) { |
1409 | | this.pretty = !!pretty; |
1410 | | var json = null; |
1411 | | var type = typeof value; |
1412 | | if(this.serialize[type]) { |
1413 | | try { |
1414 | | json = this.serialize[type].apply(this, [value]); |
1415 | | } catch(err) { |
1416 | | //OpenLayers.Console.error("Trouble serializing: " + err); |
1417 | | } |
1418 | | } |
1419 | | return json; |
1420 | | }, |
1421 | | /** |
1422 | | * Method: writeIndent |
1423 | | * Output an indentation string depending on the indentation level. |
1424 | | * |
1425 | | * Returns: |
1426 | | * {String} An appropriate indentation string. |
1427 | | */ |
1428 | | writeIndent: function() { |
1429 | | var pieces = []; |
1430 | | if(this.pretty) { |
1431 | | for(var i=0; i<this.level; ++i) { |
1432 | | pieces.push(this.indent); |
1433 | | } |
1434 | | } |
1435 | | return pieces.join(''); |
1436 | | }, |
1437 | | /** |
1438 | | * Method: writeNewline |
1439 | | * Output a string representing a newline if in pretty printing mode. |
1440 | | * |
1441 | | * Returns: |
1442 | | * {String} A string representing a new line. |
1443 | | */ |
1444 | | writeNewline: function() { |
1445 | | return (this.pretty) ? this.newline : ''; |
1446 | | }, |
1447 | | /** |
1448 | | * Method: writeSpace |
1449 | | * Output a string representing a space if in pretty printing mode. |
1450 | | * |
1451 | | * Returns: |
1452 | | * {String} A space. |
1453 | | */ |
1454 | | writeSpace: function() { |
1455 | | return (this.pretty) ? this.space : ''; |
1456 | | }, |
1457 | | /** |
1458 | | * Property: serialize |
1459 | | * Object with properties corresponding to the serializable data types. |
1460 | | * Property values are functions that do the actual serializing. |
1461 | | */ |
1462 | | serialize: { |
1463 | | /** |
1464 | | * Method: serialize.object |
1465 | | * Transform an object into a JSON string. |
1466 | | * |
1467 | | * Parameters: |
1468 | | * object - {Object} The object to be serialized. |
1469 | | * |
1470 | | * Returns: |
1471 | | * {String} A JSON string representing the object. |
1472 | | */ |
1473 | | 'object': function(object) { |
1474 | | // three special objects that we want to treat differently |
1475 | | if(object == null) |
1476 | | return "null"; |
1477 | | if(object.constructor == Date) |
1478 | | return this.serialize.date.apply(this, [object]); |
1479 | | if(object.constructor == Array) |
1480 | | return this.serialize.array.apply(this, [object]); |
1481 | | var pieces = ['{']; |
1482 | | this.level += 1; |
1483 | | var key, keyJSON, valueJSON; |
1484 | | |
1485 | | var addComma = false; |
1486 | | for(key in object) { |
1487 | | if(object.hasOwnProperty(key)) { |
1488 | | // recursive calls need to allow for sub-classing |
1489 | | keyJSON = ZOO.Format.JSON.prototype.write.apply(this, |
1490 | | [key, this.pretty]); |
1491 | | valueJSON = ZOO.Format.JSON.prototype.write.apply(this, |
1492 | | [object[key], this.pretty]); |
1493 | | if(keyJSON != null && valueJSON != null) { |
1494 | | if(addComma) |
1495 | | pieces.push(','); |
1496 | | pieces.push(this.writeNewline(), this.writeIndent(), |
1497 | | keyJSON, ':', this.writeSpace(), valueJSON); |
1498 | | addComma = true; |
1499 | | } |
1500 | | } |
1501 | | } |
1502 | | this.level -= 1; |
1503 | | pieces.push(this.writeNewline(), this.writeIndent(), '}'); |
1504 | | return pieces.join(''); |
1505 | | }, |
1506 | | /** |
1507 | | * Method: serialize.array |
1508 | | * Transform an array into a JSON string. |
1509 | | * |
1510 | | * Parameters: |
1511 | | * array - {Array} The array to be serialized |
1512 | | * |
1513 | | * Returns: |
1514 | | * {String} A JSON string representing the array. |
1515 | | */ |
1516 | | 'array': function(array) { |
1517 | | var json; |
1518 | | var pieces = ['[']; |
1519 | | this.level += 1; |
1520 | | for(var i=0, len=array.length; i<len; ++i) { |
1521 | | // recursive calls need to allow for sub-classing |
1522 | | json = ZOO.Format.JSON.prototype.write.apply(this, |
1523 | | [array[i], this.pretty]); |
1524 | | if(json != null) { |
1525 | | if(i > 0) |
1526 | | pieces.push(','); |
1527 | | pieces.push(this.writeNewline(), this.writeIndent(), json); |
1528 | | } |
1529 | | } |
1530 | | this.level -= 1; |
1531 | | pieces.push(this.writeNewline(), this.writeIndent(), ']'); |
1532 | | return pieces.join(''); |
1533 | | }, |
1534 | | /** |
1535 | | * Method: serialize.string |
1536 | | * Transform a string into a JSON string. |
1537 | | * |
1538 | | * Parameters: |
1539 | | * string - {String} The string to be serialized |
1540 | | * |
1541 | | * Returns: |
1542 | | * {String} A JSON string representing the string. |
1543 | | */ |
1544 | | 'string': function(string) { |
1545 | | var m = { |
1546 | | '\b': '\\b', |
1547 | | '\t': '\\t', |
1548 | | '\n': '\\n', |
1549 | | '\f': '\\f', |
1550 | | '\r': '\\r', |
1551 | | '"' : '\\"', |
1552 | | '\\': '\\\\' |
1553 | | }; |
1554 | | if(/["\\\x00-\x1f]/.test(string)) { |
1555 | | return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) { |
1556 | | var c = m[b]; |
1557 | | if(c) |
1558 | | return c; |
1559 | | c = b.charCodeAt(); |
1560 | | return '\\u00' + |
1561 | | Math.floor(c / 16).toString(16) + |
1562 | | (c % 16).toString(16); |
1563 | | }) + '"'; |
1564 | | } |
1565 | | return '"' + string + '"'; |
1566 | | }, |
1567 | | /** |
1568 | | * Method: serialize.number |
1569 | | * Transform a number into a JSON string. |
1570 | | * |
1571 | | * Parameters: |
1572 | | * number - {Number} The number to be serialized. |
1573 | | * |
1574 | | * Returns: |
1575 | | * {String} A JSON string representing the number. |
1576 | | */ |
1577 | | 'number': function(number) { |
1578 | | return isFinite(number) ? String(number) : "null"; |
1579 | | }, |
1580 | | /** |
1581 | | * Method: serialize.boolean |
1582 | | * Transform a boolean into a JSON string. |
1583 | | * |
1584 | | * Parameters: |
1585 | | * bool - {Boolean} The boolean to be serialized. |
1586 | | * |
1587 | | * Returns: |
1588 | | * {String} A JSON string representing the boolean. |
1589 | | */ |
1590 | | 'boolean': function(bool) { |
1591 | | return String(bool); |
1592 | | }, |
1593 | | /** |
1594 | | * Method: serialize.date |
1595 | | * Transform a date into a JSON string. |
1596 | | * |
1597 | | * Parameters: |
1598 | | * date - {Date} The date to be serialized. |
1599 | | * |
1600 | | * Returns: |
1601 | | * {String} A JSON string representing the date. |
1602 | | */ |
1603 | | 'date': function(date) { |
1604 | | function format(number) { |
1605 | | // Format integers to have at least two digits. |
1606 | | return (number < 10) ? '0' + number : number; |
1607 | | } |
1608 | | return '"' + date.getFullYear() + '-' + |
1609 | | format(date.getMonth() + 1) + '-' + |
1610 | | format(date.getDate()) + 'T' + |
1611 | | format(date.getHours()) + ':' + |
1612 | | format(date.getMinutes()) + ':' + |
1613 | | format(date.getSeconds()) + '"'; |
1614 | | } |
1615 | | }, |
1616 | | CLASS_NAME: 'ZOO.Format.JSON' |
1617 | | }); |
1618 | | /** |
1619 | | * Class: ZOO.Format.GeoJSON |
1620 | | * Read and write GeoJSON. Create a new parser with the |
1621 | | * <ZOO.Format.GeoJSON> constructor. |
1622 | | * |
1623 | | * Inherits from: |
1624 | | * - <ZOO.Format.JSON> |
1625 | | */ |
1626 | | ZOO.Format.GeoJSON = ZOO.Class(ZOO.Format.JSON, { |
1627 | | /** |
1628 | | * Constructor: ZOO.Format.GeoJSON |
1629 | | * Create a new parser for GeoJSON. |
1630 | | * |
1631 | | * Parameters: |
1632 | | * options - {Object} An optional object whose properties will be set on |
1633 | | * this instance. |
1634 | | */ |
1635 | | initialize: function(options) { |
1636 | | ZOO.Format.JSON.prototype.initialize.apply(this, [options]); |
1637 | | }, |
1638 | | /** |
1639 | | * Method: read |
1640 | | * Deserialize a GeoJSON string. |
1641 | | * |
1642 | | * Parameters: |
1643 | | * json - {String} A GeoJSON string |
1644 | | * type - {String} Optional string that determines the structure of |
1645 | | * the output. Supported values are "Geometry", "Feature", and |
1646 | | * "FeatureCollection". If absent or null, a default of |
1647 | | * "FeatureCollection" is assumed. |
1648 | | * filter - {Function} A function which will be called for every key and |
1649 | | * value at every level of the final result. Each value will be |
1650 | | * replaced by the result of the filter function. This can be used to |
1651 | | * reform generic objects into instances of classes, or to transform |
1652 | | * date strings into Date objects. |
1653 | | * |
1654 | | * Returns: |
1655 | | * {Object} The return depends on the value of the type argument. If type |
1656 | | * is "FeatureCollection" (the default), the return will be an array |
1657 | | * of <ZOO.Feature>. If type is "Geometry", the input json |
1658 | | * must represent a single geometry, and the return will be an |
1659 | | * <ZOO.Geometry>. If type is "Feature", the input json must |
1660 | | * represent a single feature, and the return will be an |
1661 | | * <ZOO.Feature>. |
1662 | | */ |
1663 | | read: function(json, type, filter) { |
1664 | | type = (type) ? type : "FeatureCollection"; |
1665 | | var results = null; |
1666 | | var obj = null; |
1667 | | if (typeof json == "string") |
1668 | | obj = ZOO.Format.JSON.prototype.read.apply(this,[json, filter]); |
1669 | | else |
1670 | | obj = json; |
1671 | | if(!obj) { |
1672 | | //ZOO.Console.error("Bad JSON: " + json); |
1673 | | } else if(typeof(obj.type) != "string") { |
1674 | | //ZOO.Console.error("Bad GeoJSON - no type: " + json); |
1675 | | } else if(this.isValidType(obj, type)) { |
1676 | | switch(type) { |
1677 | | case "Geometry": |
1678 | | try { |
1679 | | results = this.parseGeometry(obj); |
1680 | | } catch(err) { |
1681 | | //ZOO.Console.error(err); |
1682 | | } |
1683 | | break; |
1684 | | case "Feature": |
1685 | | try { |
1686 | | results = this.parseFeature(obj); |
1687 | | results.type = "Feature"; |
1688 | | } catch(err) { |
1689 | | //ZOO.Console.error(err); |
1690 | | } |
1691 | | break; |
1692 | | case "FeatureCollection": |
1693 | | // for type FeatureCollection, we allow input to be any type |
1694 | | results = []; |
1695 | | switch(obj.type) { |
1696 | | case "Feature": |
1697 | | try { |
1698 | | results.push(this.parseFeature(obj)); |
1699 | | } catch(err) { |
1700 | | results = null; |
1701 | | //ZOO.Console.error(err); |
1702 | | } |
1703 | | break; |
1704 | | case "FeatureCollection": |
1705 | | for(var i=0, len=obj.features.length; i<len; ++i) { |
1706 | | try { |
1707 | | results.push(this.parseFeature(obj.features[i])); |
1708 | | } catch(err) { |
1709 | | results = null; |
1710 | | //ZOO.Console.error(err); |
1711 | | } |
1712 | | } |
1713 | | break; |
1714 | | default: |
1715 | | try { |
1716 | | var geom = this.parseGeometry(obj); |
1717 | | results.push(new ZOO.Feature(geom)); |
1718 | | } catch(err) { |
1719 | | results = null; |
1720 | | //ZOO.Console.error(err); |
1721 | | } |
1722 | | } |
1723 | | break; |
1724 | | } |
1725 | | } |
1726 | | return results; |
1727 | | }, |
1728 | | /** |
1729 | | * Method: isValidType |
1730 | | * Check if a GeoJSON object is a valid representative of the given type. |
1731 | | * |
1732 | | * Returns: |
1733 | | * {Boolean} The object is valid GeoJSON object of the given type. |
1734 | | */ |
1735 | | isValidType: function(obj, type) { |
1736 | | var valid = false; |
1737 | | switch(type) { |
1738 | | case "Geometry": |
1739 | | if(ZOO.indexOf( |
1740 | | ["Point", "MultiPoint", "LineString", "MultiLineString", |
1741 | | "Polygon", "MultiPolygon", "Box", "GeometryCollection"], |
1742 | | obj.type) == -1) { |
1743 | | // unsupported geometry type |
1744 | | //ZOO.Console.error("Unsupported geometry type: " +obj.type); |
1745 | | } else { |
1746 | | valid = true; |
1747 | | } |
1748 | | break; |
1749 | | case "FeatureCollection": |
1750 | | // allow for any type to be converted to a feature collection |
1751 | | valid = true; |
1752 | | break; |
1753 | | default: |
1754 | | // for Feature types must match |
1755 | | if(obj.type == type) { |
1756 | | valid = true; |
1757 | | } else { |
1758 | | //ZOO.Console.error("Cannot convert types from " +obj.type + " to " + type); |
1759 | | } |
1760 | | } |
1761 | | return valid; |
1762 | | }, |
1763 | | /** |
1764 | | * Method: parseFeature |
1765 | | * Convert a feature object from GeoJSON into an |
1766 | | * <ZOO.Feature>. |
1767 | | * |
1768 | | * Parameters: |
1769 | | * obj - {Object} An object created from a GeoJSON object |
1770 | | * |
1771 | | * Returns: |
1772 | | * {<ZOO.Feature>} A feature. |
1773 | | */ |
1774 | | parseFeature: function(obj) { |
1775 | | var feature, geometry, attributes, bbox; |
1776 | | attributes = (obj.properties) ? obj.properties : {}; |
1777 | | bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox; |
1778 | | try { |
1779 | | geometry = this.parseGeometry(obj.geometry); |
1780 | | } catch(err) { |
1781 | | // deal with bad geometries |
1782 | | throw err; |
1783 | | } |
1784 | | feature = new ZOO.Feature(geometry, attributes); |
1785 | | if(bbox) |
1786 | | feature.bounds = ZOO.Bounds.fromArray(bbox); |
1787 | | if(obj.id) |
1788 | | feature.fid = obj.id; |
1789 | | return feature; |
1790 | | }, |
1791 | | /** |
1792 | | * Method: parseGeometry |
1793 | | * Convert a geometry object from GeoJSON into an <ZOO.Geometry>. |
1794 | | * |
1795 | | * Parameters: |
1796 | | * obj - {Object} An object created from a GeoJSON object |
1797 | | * |
1798 | | * Returns: |
1799 | | * {<ZOO.Geometry>} A geometry. |
1800 | | */ |
1801 | | parseGeometry: function(obj) { |
1802 | | if (obj == null) |
1803 | | return null; |
1804 | | var geometry, collection = false; |
1805 | | if(obj.type == "GeometryCollection") { |
1806 | | if(!(obj.geometries instanceof Array)) { |
1807 | | throw "GeometryCollection must have geometries array: " + obj; |
1808 | | } |
1809 | | var numGeom = obj.geometries.length; |
1810 | | var components = new Array(numGeom); |
1811 | | for(var i=0; i<numGeom; ++i) { |
1812 | | components[i] = this.parseGeometry.apply( |
1813 | | this, [obj.geometries[i]] |
1814 | | ); |
1815 | | } |
1816 | | geometry = new ZOO.Geometry.Collection(components); |
1817 | | collection = true; |
1818 | | } else { |
1819 | | if(!(obj.coordinates instanceof Array)) { |
1820 | | throw "Geometry must have coordinates array: " + obj; |
1821 | | } |
1822 | | if(!this.parseCoords[obj.type.toLowerCase()]) { |
1823 | | throw "Unsupported geometry type: " + obj.type; |
1824 | | } |
1825 | | try { |
1826 | | geometry = this.parseCoords[obj.type.toLowerCase()].apply( |
1827 | | this, [obj.coordinates] |
1828 | | ); |
1829 | | } catch(err) { |
1830 | | // deal with bad coordinates |
1831 | | throw err; |
1832 | | } |
1833 | | } |
1834 | | // We don't reproject collections because the children are reprojected |
1835 | | // for us when they are created. |
1836 | | if (this.internalProjection && this.externalProjection && !collection) { |
1837 | | geometry.transform(this.externalProjection, |
1838 | | this.internalProjection); |
1839 | | } |
1840 | | return geometry; |
1841 | | }, |
1842 | | /** |
1843 | | * Property: parseCoords |
1844 | | * Object with properties corresponding to the GeoJSON geometry types. |
1845 | | * Property values are functions that do the actual parsing. |
1846 | | */ |
1847 | | parseCoords: { |
1848 | | /** |
1849 | | * Method: parseCoords.point |
1850 | | * Convert a coordinate array from GeoJSON into an |
1851 | | * <ZOO.Geometry.Point>. |
1852 | | * |
1853 | | * Parameters: |
1854 | | * array - {Object} The coordinates array from the GeoJSON fragment. |
1855 | | * |
1856 | | * Returns: |
1857 | | * {<ZOO.Geometry.Point>} A geometry. |
1858 | | */ |
1859 | | "point": function(array) { |
1860 | | if(array.length != 2) { |
1861 | | throw "Only 2D points are supported: " + array; |
1862 | | } |
1863 | | return new ZOO.Geometry.Point(array[0], array[1]); |
1864 | | }, |
1865 | | /** |
1866 | | * Method: parseCoords.multipoint |
1867 | | * Convert a coordinate array from GeoJSON into an |
1868 | | * <ZOO.Geometry.MultiPoint>. |
1869 | | * |
1870 | | * Parameters: |
1871 | | * array - {Object} The coordinates array from the GeoJSON fragment. |
1872 | | * |
1873 | | * Returns: |
1874 | | * {<ZOO.Geometry.MultiPoint>} A geometry. |
1875 | | */ |
1876 | | "multipoint": function(array) { |
1877 | | var points = []; |
1878 | | var p = null; |
1879 | | for(var i=0, len=array.length; i<len; ++i) { |
1880 | | try { |
1881 | | p = this.parseCoords["point"].apply(this, [array[i]]); |
1882 | | } catch(err) { |
1883 | | throw err; |
1884 | | } |
1885 | | points.push(p); |
1886 | | } |
1887 | | return new ZOO.Geometry.MultiPoint(points); |
1888 | | }, |
1889 | | /** |
1890 | | * Method: parseCoords.linestring |
1891 | | * Convert a coordinate array from GeoJSON into an |
1892 | | * <ZOO.Geometry.LineString>. |
1893 | | * |
1894 | | * Parameters: |
1895 | | * array - {Object} The coordinates array from the GeoJSON fragment. |
1896 | | * |
1897 | | * Returns: |
1898 | | * {<ZOO.Geometry.LineString>} A geometry. |
1899 | | */ |
1900 | | "linestring": function(array) { |
1901 | | var points = []; |
1902 | | var p = null; |
1903 | | for(var i=0, len=array.length; i<len; ++i) { |
1904 | | try { |
1905 | | p = this.parseCoords["point"].apply(this, [array[i]]); |
1906 | | } catch(err) { |
1907 | | throw err; |
1908 | | } |
1909 | | points.push(p); |
1910 | | } |
1911 | | return new ZOO.Geometry.LineString(points); |
1912 | | }, |
1913 | | /** |
1914 | | * Method: parseCoords.multilinestring |
1915 | | * Convert a coordinate array from GeoJSON into an |
1916 | | * <ZOO.Geometry.MultiLineString>. |
1917 | | * |
1918 | | * Parameters: |
1919 | | * array - {Object} The coordinates array from the GeoJSON fragment. |
1920 | | * |
1921 | | * Returns: |
1922 | | * {<ZOO.Geometry.MultiLineString>} A geometry. |
1923 | | */ |
1924 | | "multilinestring": function(array) { |
1925 | | var lines = []; |
1926 | | var l = null; |
1927 | | for(var i=0, len=array.length; i<len; ++i) { |
1928 | | try { |
1929 | | l = this.parseCoords["linestring"].apply(this, [array[i]]); |
1930 | | } catch(err) { |
1931 | | throw err; |
1932 | | } |
1933 | | lines.push(l); |
1934 | | } |
1935 | | return new ZOO.Geometry.MultiLineString(lines); |
1936 | | }, |
1937 | | /** |
1938 | | * Method: parseCoords.polygon |
1939 | | * Convert a coordinate array from GeoJSON into an |
1940 | | * <ZOO.Geometry.Polygon>. |
1941 | | * |
1942 | | * Parameters: |
1943 | | * array - {Object} The coordinates array from the GeoJSON fragment. |
1944 | | * |
1945 | | * Returns: |
1946 | | * {<ZOO.Geometry.Polygon>} A geometry. |
1947 | | */ |
1948 | | "polygon": function(array) { |
1949 | | var rings = []; |
1950 | | var r, l; |
1951 | | for(var i=0, len=array.length; i<len; ++i) { |
1952 | | try { |
1953 | | l = this.parseCoords["linestring"].apply(this, [array[i]]); |
1954 | | } catch(err) { |
1955 | | throw err; |
1956 | | } |
1957 | | r = new ZOO.Geometry.LinearRing(l.components); |
1958 | | rings.push(r); |
1959 | | } |
1960 | | return new ZOO.Geometry.Polygon(rings); |
1961 | | }, |
1962 | | /** |
1963 | | * Method: parseCoords.multipolygon |
1964 | | * Convert a coordinate array from GeoJSON into an |
1965 | | * <ZOO.Geometry.MultiPolygon>. |
1966 | | * |
1967 | | * Parameters: |
1968 | | * array - {Object} The coordinates array from the GeoJSON fragment. |
1969 | | * |
1970 | | * Returns: |
1971 | | * {<ZOO.Geometry.MultiPolygon>} A geometry. |
1972 | | */ |
1973 | | "multipolygon": function(array) { |
1974 | | var polys = []; |
1975 | | var p = null; |
1976 | | for(var i=0, len=array.length; i<len; ++i) { |
1977 | | try { |
1978 | | p = this.parseCoords["polygon"].apply(this, [array[i]]); |
1979 | | } catch(err) { |
1980 | | throw err; |
1981 | | } |
1982 | | polys.push(p); |
1983 | | } |
1984 | | return new ZOO.Geometry.MultiPolygon(polys); |
1985 | | }, |
1986 | | /** |
1987 | | * Method: parseCoords.box |
1988 | | * Convert a coordinate array from GeoJSON into an |
1989 | | * <ZOO.Geometry.Polygon>. |
1990 | | * |
1991 | | * Parameters: |
1992 | | * array - {Object} The coordinates array from the GeoJSON fragment. |
1993 | | * |
1994 | | * Returns: |
1995 | | * {<ZOO.Geometry.Polygon>} A geometry. |
1996 | | */ |
1997 | | "box": function(array) { |
1998 | | if(array.length != 2) { |
1999 | | throw "GeoJSON box coordinates must have 2 elements"; |
2000 | | } |
2001 | | return new ZOO.Geometry.Polygon([ |
2002 | | new ZOO.Geometry.LinearRing([ |
2003 | | new ZOO.Geometry.Point(array[0][0], array[0][1]), |
2004 | | new ZOO.Geometry.Point(array[1][0], array[0][1]), |
2005 | | new ZOO.Geometry.Point(array[1][0], array[1][1]), |
2006 | | new ZOO.Geometry.Point(array[0][0], array[1][1]), |
2007 | | new Z0O.Geometry.Point(array[0][0], array[0][1]) |
2008 | | ]) |
2009 | | ]); |
2010 | | } |
2011 | | }, |
2012 | | /** |
2013 | | * Method: write |
2014 | | * Serialize a feature, geometry, array of features into a GeoJSON string. |
2015 | | * |
2016 | | * Parameters: |
2017 | | * obj - {Object} An <ZOO.Feature>, <ZOO.Geometry>, |
2018 | | * or an array of features. |
2019 | | * pretty - {Boolean} Structure the output with newlines and indentation. |
2020 | | * Default is false. |
2021 | | * |
2022 | | * Returns: |
2023 | | * {String} The GeoJSON string representation of the input geometry, |
2024 | | * features, or array of features. |
2025 | | */ |
2026 | | write: function(obj, pretty) { |
2027 | | var geojson = { |
2028 | | "type": null |
2029 | | }; |
2030 | | if(obj instanceof Array) { |
2031 | | geojson.type = "FeatureCollection"; |
2032 | | var numFeatures = obj.length; |
2033 | | geojson.features = new Array(numFeatures); |
2034 | | for(var i=0; i<numFeatures; ++i) { |
2035 | | var element = obj[i]; |
2036 | | if(!element instanceof ZOO.Feature) { |
2037 | | var msg = "FeatureCollection only supports collections " + |
2038 | | "of features: " + element; |
2039 | | throw msg; |
2040 | | } |
2041 | | geojson.features[i] = this.extract.feature.apply(this, [element]); |
2042 | | } |
2043 | | } else if (obj.CLASS_NAME.indexOf("ZOO.Geometry") == 0) { |
2044 | | geojson = this.extract.geometry.apply(this, [obj]); |
2045 | | } else if (obj instanceof ZOO.Feature) { |
2046 | | geojson = this.extract.feature.apply(this, [obj]); |
2047 | | /* |
2048 | | if(obj.layer && obj.layer.projection) { |
2049 | | geojson.crs = this.createCRSObject(obj); |
2050 | | } |
2051 | | */ |
2052 | | } |
2053 | | return ZOO.Format.JSON.prototype.write.apply(this, |
2054 | | [geojson, pretty]); |
2055 | | }, |
2056 | | /** |
2057 | | * Method: createCRSObject |
2058 | | * Create the CRS object for an object. |
2059 | | * |
2060 | | * Parameters: |
2061 | | * object - {<ZOO.Feature>} |
2062 | | * |
2063 | | * Returns: |
2064 | | * {Object} An object which can be assigned to the crs property |
2065 | | * of a GeoJSON object. |
2066 | | */ |
2067 | | createCRSObject: function(object) { |
2068 | | //var proj = object.layer.projection.toString(); |
2069 | | var proj = object.projection.toString(); |
2070 | | var crs = {}; |
2071 | | if (proj.match(/epsg:/i)) { |
2072 | | var code = parseInt(proj.substring(proj.indexOf(":") + 1)); |
2073 | | if (code == 4326) { |
2074 | | crs = { |
2075 | | "type": "OGC", |
2076 | | "properties": { |
2077 | | "urn": "urn:ogc:def:crs:OGC:1.3:CRS84" |
2078 | | } |
2079 | | }; |
2080 | | } else { |
2081 | | crs = { |
2082 | | "type": "EPSG", |
2083 | | "properties": { |
2084 | | "code": code |
2085 | | } |
2086 | | }; |
2087 | | } |
2088 | | } |
2089 | | return crs; |
2090 | | }, |
2091 | | /** |
2092 | | * Property: extract |
2093 | | * Object with properties corresponding to the GeoJSON types. |
2094 | | * Property values are functions that do the actual value extraction. |
2095 | | */ |
2096 | | extract: { |
2097 | | /** |
2098 | | * Method: extract.feature |
2099 | | * Return a partial GeoJSON object representing a single feature. |
2100 | | * |
2101 | | * Parameters: |
2102 | | * feature - {<ZOO.Feature>} |
2103 | | * |
2104 | | * Returns: |
2105 | | * {Object} An object representing the point. |
2106 | | */ |
2107 | | 'feature': function(feature) { |
2108 | | var geom = this.extract.geometry.apply(this, [feature.geometry]); |
2109 | | return { |
2110 | | "type": "Feature", |
2111 | | "id": feature.fid == null ? feature.id : feature.fid, |
2112 | | "properties": feature.attributes, |
2113 | | "geometry": geom |
2114 | | }; |
2115 | | }, |
2116 | | /** |
2117 | | * Method: extract.geometry |
2118 | | * Return a GeoJSON object representing a single geometry. |
2119 | | * |
2120 | | * Parameters: |
2121 | | * geometry - {<ZOO.Geometry>} |
2122 | | * |
2123 | | * Returns: |
2124 | | * {Object} An object representing the geometry. |
2125 | | */ |
2126 | | 'geometry': function(geometry) { |
2127 | | if (geometry == null) |
2128 | | return null; |
2129 | | if (this.internalProjection && this.externalProjection) { |
2130 | | geometry = geometry.clone(); |
2131 | | geometry.transform(this.internalProjection, |
2132 | | this.externalProjection); |
2133 | | } |
2134 | | var geometryType = geometry.CLASS_NAME.split('.')[2]; |
2135 | | var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]); |
2136 | | var json; |
2137 | | if(geometryType == "Collection") |
2138 | | json = { |
2139 | | "type": "GeometryCollection", |
2140 | | "geometries": data |
2141 | | }; |
2142 | | else |
2143 | | json = { |
2144 | | "type": geometryType, |
2145 | | "coordinates": data |
2146 | | }; |
2147 | | return json; |
2148 | | }, |
2149 | | /** |
2150 | | * Method: extract.point |
2151 | | * Return an array of coordinates from a point. |
2152 | | * |
2153 | | * Parameters: |
2154 | | * point - {<ZOO.Geometry.Point>} |
2155 | | * |
2156 | | * Returns: |
2157 | | * {Array} An array of coordinates representing the point. |
2158 | | */ |
2159 | | 'point': function(point) { |
2160 | | return [point.x, point.y]; |
2161 | | }, |
2162 | | /** |
2163 | | * Method: extract.multipoint |
2164 | | * Return an array of coordinates from a multipoint. |
2165 | | * |
2166 | | * Parameters: |
2167 | | * multipoint - {<ZOO.Geometry.MultiPoint>} |
2168 | | * |
2169 | | * Returns: |
2170 | | * {Array} An array of point coordinate arrays representing |
2171 | | * the multipoint. |
2172 | | */ |
2173 | | 'multipoint': function(multipoint) { |
2174 | | var array = []; |
2175 | | for(var i=0, len=multipoint.components.length; i<len; ++i) { |
2176 | | array.push(this.extract.point.apply(this, [multipoint.components[i]])); |
2177 | | } |
2178 | | return array; |
2179 | | }, |
2180 | | /** |
2181 | | * Method: extract.linestring |
2182 | | * Return an array of coordinate arrays from a linestring. |
2183 | | * |
2184 | | * Parameters: |
2185 | | * linestring - {<ZOO.Geometry.LineString>} |
2186 | | * |
2187 | | * Returns: |
2188 | | * {Array} An array of coordinate arrays representing |
2189 | | * the linestring. |
2190 | | */ |
2191 | | 'linestring': function(linestring) { |
2192 | | var array = []; |
2193 | | for(var i=0, len=linestring.components.length; i<len; ++i) { |
2194 | | array.push(this.extract.point.apply(this, [linestring.components[i]])); |
2195 | | } |
2196 | | return array; |
2197 | | }, |
2198 | | /** |
2199 | | * Method: extract.multilinestring |
2200 | | * Return an array of linestring arrays from a linestring. |
2201 | | * |
2202 | | * Parameters: |
2203 | | * multilinestring - {<ZOO.Geometry.MultiLineString>} |
2204 | | * |
2205 | | * Returns: |
2206 | | * {Array} An array of linestring arrays representing |
2207 | | * the multilinestring. |
2208 | | */ |
2209 | | 'multilinestring': function(multilinestring) { |
2210 | | var array = []; |
2211 | | for(var i=0, len=multilinestring.components.length; i<len; ++i) { |
2212 | | array.push(this.extract.linestring.apply(this, [multilinestring.components[i]])); |
2213 | | } |
2214 | | return array; |
2215 | | }, |
2216 | | /** |
2217 | | * Method: extract.polygon |
2218 | | * Return an array of linear ring arrays from a polygon. |
2219 | | * |
2220 | | * Parameters: |
2221 | | * polygon - {<ZOO.Geometry.Polygon>} |
2222 | | * |
2223 | | * Returns: |
2224 | | * {Array} An array of linear ring arrays representing the polygon. |
2225 | | */ |
2226 | | 'polygon': function(polygon) { |
2227 | | var array = []; |
2228 | | for(var i=0, len=polygon.components.length; i<len; ++i) { |
2229 | | array.push(this.extract.linestring.apply(this, [polygon.components[i]])); |
2230 | | } |
2231 | | return array; |
2232 | | }, |
2233 | | /** |
2234 | | * Method: extract.multipolygon |
2235 | | * Return an array of polygon arrays from a multipolygon. |
2236 | | * |
2237 | | * Parameters: |
2238 | | * multipolygon - {<ZOO.Geometry.MultiPolygon>} |
2239 | | * |
2240 | | * Returns: |
2241 | | * {Array} An array of polygon arrays representing |
2242 | | * the multipolygon |
2243 | | */ |
2244 | | 'multipolygon': function(multipolygon) { |
2245 | | var array = []; |
2246 | | for(var i=0, len=multipolygon.components.length; i<len; ++i) { |
2247 | | array.push(this.extract.polygon.apply(this, [multipolygon.components[i]])); |
2248 | | } |
2249 | | return array; |
2250 | | }, |
2251 | | /** |
2252 | | * Method: extract.collection |
2253 | | * Return an array of geometries from a geometry collection. |
2254 | | * |
2255 | | * Parameters: |
2256 | | * collection - {<ZOO.Geometry.Collection>} |
2257 | | * |
2258 | | * Returns: |
2259 | | * {Array} An array of geometry objects representing the geometry |
2260 | | * collection. |
2261 | | */ |
2262 | | 'collection': function(collection) { |
2263 | | var len = collection.components.length; |
2264 | | var array = new Array(len); |
2265 | | for(var i=0; i<len; ++i) { |
2266 | | array[i] = this.extract.geometry.apply( |
2267 | | this, [collection.components[i]] |
2268 | | ); |
2269 | | } |
2270 | | return array; |
2271 | | } |
2272 | | }, |
2273 | | CLASS_NAME: 'ZOO.Format.GeoJSON' |
2274 | | }); |
2275 | | /** |
2276 | | * Class: ZOO.Format.KML |
2277 | | * Read/Write KML. Create a new instance with the <ZOO.Format.KML> |
2278 | | * constructor. |
2279 | | * |
2280 | | * Inherits from: |
2281 | | * - <ZOO.Format> |
2282 | | */ |
2283 | | ZOO.Format.KML = ZOO.Class(ZOO.Format, { |
2284 | | /** |
2285 | | * Property: kmlns |
2286 | | * {String} KML Namespace to use. Defaults to 2.2 namespace. |
2287 | | */ |
2288 | | kmlns: "http://www.opengis.net/kml/2.2", |
2289 | | /** |
2290 | | * Property: foldersName |
2291 | | * {String} Name of the folders. Default is "ZOO export". |
2292 | | * If set to null, no name element will be created. |
2293 | | */ |
2294 | | foldersName: "ZOO export", |
2295 | | /** |
2296 | | * Property: foldersDesc |
2297 | | * {String} Description of the folders. Default is "Exported on [date]." |
2298 | | * If set to null, no description element will be created. |
2299 | | */ |
2300 | | foldersDesc: "Created on " + new Date(), |
2301 | | /** |
2302 | | * Property: placemarksDesc |
2303 | | * {String} Name of the placemarks. Default is "No description available". |
2304 | | */ |
2305 | | placemarksDesc: "No description available", |
2306 | | /** |
2307 | | * Property: extractAttributes |
2308 | | * {Boolean} Extract attributes from KML. Default is true. |
2309 | | * Extracting styleUrls requires this to be set to true |
2310 | | */ |
2311 | | extractAttributes: true, |
2312 | | /** |
2313 | | * Constructor: ZOO.Format.KML |
2314 | | * Create a new parser for KML. |
2315 | | * |
2316 | | * Parameters: |
2317 | | * options - {Object} An optional object whose properties will be set on |
2318 | | * this instance. |
2319 | | */ |
2320 | | initialize: function(options) { |
2321 | | // compile regular expressions once instead of every time they are used |
2322 | | this.regExes = { |
2323 | | trimSpace: (/^\s*|\s*$/g), |
2324 | | removeSpace: (/\s*/g), |
2325 | | splitSpace: (/\s+/), |
2326 | | trimComma: (/\s*,\s*/g), |
2327 | | kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/), |
2328 | | kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/), |
2329 | | straightBracket: (/\$\[(.*?)\]/g) |
2330 | | }; |
2331 | | // KML coordinates are always in longlat WGS84 |
2332 | | this.externalProjection = new ZOO.Projection("EPSG:4326"); |
2333 | | ZOO.Format.prototype.initialize.apply(this, [options]); |
2334 | | }, |
2335 | | /** |
2336 | | * APIMethod: read |
2337 | | * Read data from a string, and return a list of features. |
2338 | | * |
2339 | | * Parameters: |
2340 | | * data - {String} data to read/parse. |
2341 | | * |
2342 | | * Returns: |
2343 | | * {Array(<ZOO.Feature>)} List of features. |
2344 | | */ |
2345 | | read: function(data) { |
2346 | | this.features = []; |
2347 | | data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, ""); |
2348 | | data = new XML(data); |
2349 | | var placemarks = data..*::Placemark; |
2350 | | this.parseFeatures(placemarks); |
2351 | | return this.features; |
2352 | | }, |
2353 | | /** |
2354 | | * Method: parseFeatures |
2355 | | * Loop through all Placemark nodes and parse them. |
2356 | | * Will create a list of features |
2357 | | * |
2358 | | * Parameters: |
2359 | | * nodes - {Array} of {E4XElement} data to read/parse. |
2360 | | * options - {Object} Hash of options |
2361 | | * |
2362 | | */ |
2363 | | parseFeatures: function(nodes) { |
2364 | | var features = new Array(nodes.length()); |
2365 | | for(var i=0, len=nodes.length(); i<len; i++) { |
2366 | | var featureNode = nodes[i]; |
2367 | | var feature = this.parseFeature.apply(this,[featureNode]) ; |
2368 | | features[i] = feature; |
2369 | | } |
2370 | | this.features = this.features.concat(features); |
2371 | | }, |
2372 | | /** |
2373 | | * Method: parseFeature |
2374 | | * This function is the core of the KML parsing code in ZOO. |
2375 | | * It creates the geometries that are then attached to the returned |
2376 | | * feature, and calls parseAttributes() to get attribute data out. |
2377 | | * |
2378 | | * Parameters: |
2379 | | * node - {E4XElement} |
2380 | | * |
2381 | | * Returns: |
2382 | | * {<ZOO.Feature>} A vector feature. |
2383 | | */ |
2384 | | parseFeature: function(node) { |
2385 | | // only accept one geometry per feature - look for highest "order" |
2386 | | var order = ["MultiGeometry", "Polygon", "LineString", "Point"]; |
2387 | | var type, nodeList, geometry, parser; |
2388 | | for(var i=0, len=order.length; i<len; ++i) { |
2389 | | type = order[i]; |
2390 | | nodeList = node.descendants(QName(null,type)); |
2391 | | if (nodeList.length()> 0) { |
2392 | | var parser = this.parseGeometry[type.toLowerCase()]; |
2393 | | if(parser) { |
2394 | | geometry = parser.apply(this, [nodeList[0]]); |
2395 | | if (this.internalProjection && this.externalProjection) { |
2396 | | geometry.transform(this.externalProjection, |
2397 | | this.internalProjection); |
2398 | | } |
2399 | | } |
2400 | | // stop looking for different geometry types |
2401 | | break; |
2402 | | } |
2403 | | } |
2404 | | // construct feature (optionally with attributes) |
2405 | | var attributes; |
2406 | | if(this.extractAttributes) { |
2407 | | attributes = this.parseAttributes(node); |
2408 | | } |
2409 | | var feature = new ZOO.Feature(geometry, attributes); |
2410 | | var fid = node.@id || node.@name; |
2411 | | if(fid != null) |
2412 | | feature.fid = fid; |
2413 | | return feature; |
2414 | | }, |
2415 | | /** |
2416 | | * Property: parseGeometry |
2417 | | * Properties of this object are the functions that parse geometries based |
2418 | | * on their type. |
2419 | | */ |
2420 | | parseGeometry: { |
2421 | | /** |
2422 | | * Method: parseGeometry.point |
2423 | | * Given a KML node representing a point geometry, create a ZOO |
2424 | | * point geometry. |
2425 | | * |
2426 | | * Parameters: |
2427 | | * node - {E4XElement} A KML Point node. |
2428 | | * |
2429 | | * Returns: |
2430 | | * {<ZOO.Geometry.Point>} A point geometry. |
2431 | | */ |
2432 | | 'point': function(node) { |
2433 | | var coordString = node.*::coordinates.toString(); |
2434 | | coordString = coordString.replace(this.regExes.removeSpace, ""); |
2435 | | coords = coordString.split(","); |
2436 | | var point = null; |
2437 | | if(coords.length > 1) { |
2438 | | // preserve third dimension |
2439 | | if(coords.length == 2) { |
2440 | | coords[2] = null; |
2441 | | } |
2442 | | point = new ZOO.Geometry.Point(coords[0], coords[1], coords[2]); |
2443 | | } |
2444 | | return point; |
2445 | | }, |
2446 | | /** |
2447 | | * Method: parseGeometry.linestring |
2448 | | * Given a KML node representing a linestring geometry, create a |
2449 | | * ZOO linestring geometry. |
2450 | | * |
2451 | | * Parameters: |
2452 | | * node - {E4XElement} A KML LineString node. |
2453 | | * |
2454 | | * Returns: |
2455 | | * {<ZOO.Geometry.LineString>} A linestring geometry. |
2456 | | */ |
2457 | | 'linestring': function(node, ring) { |
2458 | | var line = null; |
2459 | | var coordString = node.*::coordinates.toString(); |
2460 | | coordString = coordString.replace(this.regExes.trimSpace, |
2461 | | ""); |
2462 | | coordString = coordString.replace(this.regExes.trimComma, |
2463 | | ","); |
2464 | | var pointList = coordString.split(this.regExes.splitSpace); |
2465 | | var numPoints = pointList.length; |
2466 | | var points = new Array(numPoints); |
2467 | | var coords, numCoords; |
2468 | | for(var i=0; i<numPoints; ++i) { |
2469 | | coords = pointList[i].split(","); |
2470 | | numCoords = coords.length; |
2471 | | if(numCoords > 1) { |
2472 | | if(coords.length == 2) { |
2473 | | coords[2] = null; |
2474 | | } |
2475 | | points[i] = new ZOO.Geometry.Point(coords[0], |
2476 | | coords[1], |
2477 | | coords[2]); |
2478 | | } |
2479 | | } |
2480 | | if(numPoints) { |
2481 | | if(ring) { |
2482 | | line = new ZOO.Geometry.LinearRing(points); |
2483 | | } else { |
2484 | | line = new ZOO.Geometry.LineString(points); |
2485 | | } |
2486 | | } else { |
2487 | | throw "Bad LineString coordinates: " + coordString; |
2488 | | } |
2489 | | return line; |
2490 | | }, |
2491 | | /** |
2492 | | * Method: parseGeometry.polygon |
2493 | | * Given a KML node representing a polygon geometry, create a |
2494 | | * ZOO polygon geometry. |
2495 | | * |
2496 | | * Parameters: |
2497 | | * node - {E4XElement} A KML Polygon node. |
2498 | | * |
2499 | | * Returns: |
2500 | | * {<ZOO.Geometry.Polygon>} A polygon geometry. |
2501 | | */ |
2502 | | 'polygon': function(node) { |
2503 | | var nodeList = node..*::LinearRing; |
2504 | | var numRings = nodeList.length(); |
2505 | | var components = new Array(numRings); |
2506 | | if(numRings > 0) { |
2507 | | // this assumes exterior ring first, inner rings after |
2508 | | var ring; |
2509 | | for(var i=0, len=nodeList.length(); i<len; ++i) { |
2510 | | ring = this.parseGeometry.linestring.apply(this, |
2511 | | [nodeList[i], true]); |
2512 | | if(ring) { |
2513 | | components[i] = ring; |
2514 | | } else { |
2515 | | throw "Bad LinearRing geometry: " + i; |
2516 | | } |
2517 | | } |
2518 | | } |
2519 | | return new ZOO.Geometry.Polygon(components); |
2520 | | }, |
2521 | | /** |
2522 | | * Method: parseGeometry.multigeometry |
2523 | | * Given a KML node representing a multigeometry, create a |
2524 | | * ZOO geometry collection. |
2525 | | * |
2526 | | * Parameters: |
2527 | | * node - {E4XElement} A KML MultiGeometry node. |
2528 | | * |
2529 | | * Returns: |
2530 | | * {<ZOO.Geometry.Collection>} A geometry collection. |
2531 | | */ |
2532 | | 'multigeometry': function(node) { |
2533 | | var child, parser; |
2534 | | var parts = []; |
2535 | | var children = node.*::*; |
2536 | | for(var i=0, len=children.length(); i<len; ++i ) { |
2537 | | child = children[i]; |
2538 | | var type = child.localName(); |
2539 | | var parser = this.parseGeometry[type.toLowerCase()]; |
2540 | | if(parser) { |
2541 | | parts.push(parser.apply(this, [child])); |
2542 | | } |
2543 | | } |
2544 | | return new ZOO.Geometry.Collection(parts); |
2545 | | } |
2546 | | }, |
2547 | | /** |
2548 | | * Method: parseAttributes |
2549 | | * |
2550 | | * Parameters: |
2551 | | * node - {E4XElement} |
2552 | | * |
2553 | | * Returns: |
2554 | | * {Object} An attributes object. |
2555 | | */ |
2556 | | parseAttributes: function(node) { |
2557 | | var attributes = {}; |
2558 | | var edNodes = node.*::ExtendedData; |
2559 | | if (edNodes.length() > 0) { |
2560 | | attributes = this.parseExtendedData(edNodes[0]) |
2561 | | } |
2562 | | var child, grandchildren; |
2563 | | var children = node.*::*; |
2564 | | for(var i=0, len=children.length(); i<len; ++i) { |
2565 | | child = children[i]; |
2566 | | grandchildren = child..*::*; |
2567 | | if(grandchildren.length() == 1) { |
2568 | | var name = child.localName(); |
2569 | | var value = child.toString(); |
2570 | | if (value) { |
2571 | | value = value.replace(this.regExes.trimSpace, ""); |
2572 | | attributes[name] = value; |
2573 | | } |
2574 | | } |
2575 | | } |
2576 | | return attributes; |
2577 | | }, |
2578 | | /** |
2579 | | * Method: parseExtendedData |
2580 | | * Parse ExtendedData from KML. Limited support for schemas/datatypes. |
2581 | | * See http://code.google.com/apis/kml/documentation/kmlreference.html#extendeddata |
2582 | | * for more information on extendeddata. |
2583 | | * |
2584 | | * Parameters: |
2585 | | * node - {E4XElement} |
2586 | | * |
2587 | | * Returns: |
2588 | | * {Object} An attributes object. |
2589 | | */ |
2590 | | parseExtendedData: function(node) { |
2591 | | var attributes = {}; |
2592 | | var dataNodes = node.*::Data; |
2593 | | for (var i = 0, len = dataNodes.length(); i < len; i++) { |
2594 | | var data = dataNodes[i]; |
2595 | | var key = data.@name; |
2596 | | var ed = {}; |
2597 | | var valueNode = data.*::value; |
2598 | | if (valueNode.length() > 0) |
2599 | | ed['value'] = valueNode[0].toString(); |
2600 | | var nameNode = data.*::displayName; |
2601 | | if (nameNode.length() > 0) |
2602 | | ed['displayName'] = valueNode[0].toString(); |
2603 | | attributes[key] = ed; |
2604 | | } |
2605 | | return attributes; |
2606 | | }, |
2607 | | /** |
2608 | | * Method: write |
2609 | | * Accept Feature Collection, and return a string. |
2610 | | * |
2611 | | * Parameters: |
2612 | | * features - {Array(<ZOO.Feature>} An array of features. |
2613 | | * |
2614 | | * Returns: |
2615 | | * {String} A KML string. |
2616 | | */ |
2617 | | write: function(features) { |
2618 | | if(!(features instanceof Array)) |
2619 | | features = [features]; |
2620 | | var kml = new XML('<kml xmlns="'+this.kmlns+'"></kml>'); |
2621 | | var folder = kml.Document.Folder; |
2622 | | folder.name = this.foldersName; |
2623 | | folder.description = this.foldersDesc; |
2624 | | for(var i=0, len=features.length; i<len; ++i) { |
2625 | | folder.Placemark[i] = this.createPlacemark(features[i]); |
2626 | | } |
2627 | | return kml.toXMLString(); |
2628 | | }, |
2629 | | /** |
2630 | | * Method: createPlacemark |
2631 | | * Creates and returns a KML placemark node representing the given feature. |
2632 | | * |
2633 | | * Parameters: |
2634 | | * feature - {<ZOO.Feature>} |
2635 | | * |
2636 | | * Returns: |
2637 | | * {E4XElement} |
2638 | | */ |
2639 | | createPlacemark: function(feature) { |
2640 | | var placemark = new XML('<Placemark xmlns="'+this.kmlns+'"></Placemark>'); |
2641 | | placemark.name = (feature.attributes.name) ? |
2642 | | feature.attributes.name : feature.id; |
2643 | | placemark.description = (feature.attributes.description) ? |
2644 | | feature.attributes.description : this.placemarksDesc; |
2645 | | if(feature.fid != null) |
2646 | | placemark.@id = feature.fid; |
2647 | | placemark.*[2] = this.buildGeometryNode(feature.geometry); |
2648 | | return placemark; |
2649 | | }, |
2650 | | /** |
2651 | | * Method: buildGeometryNode |
2652 | | * Builds and returns a KML geometry node with the given geometry. |
2653 | | * |
2654 | | * Parameters: |
2655 | | * geometry - {<ZOO.Geometry>} |
2656 | | * |
2657 | | * Returns: |
2658 | | * {E4XElement} |
2659 | | */ |
2660 | | buildGeometryNode: function(geometry) { |
2661 | | if (this.internalProjection && this.externalProjection) { |
2662 | | geometry = geometry.clone(); |
2663 | | geometry.transform(this.internalProjection, |
2664 | | this.externalProjection); |
2665 | | } |
2666 | | var className = geometry.CLASS_NAME; |
2667 | | var type = className.substring(className.lastIndexOf(".") + 1); |
2668 | | var builder = this.buildGeometry[type.toLowerCase()]; |
2669 | | var node = null; |
2670 | | if(builder) { |
2671 | | node = builder.apply(this, [geometry]); |
2672 | | } |
2673 | | return node; |
2674 | | }, |
2675 | | /** |
2676 | | * Property: buildGeometry |
2677 | | * Object containing methods to do the actual geometry node building |
2678 | | * based on geometry type. |
2679 | | */ |
2680 | | buildGeometry: { |
2681 | | /** |
2682 | | * Method: buildGeometry.point |
2683 | | * Given a ZOO point geometry, create a KML point. |
2684 | | * |
2685 | | * Parameters: |
2686 | | * geometry - {<ZOO.Geometry.Point>} A point geometry. |
2687 | | * |
2688 | | * Returns: |
2689 | | * {E4XElement} A KML point node. |
2690 | | */ |
2691 | | 'point': function(geometry) { |
2692 | | var kml = new XML('<Point xmlns="'+this.kmlns+'"></Point>'); |
2693 | | kml.coordinates = this.buildCoordinatesNode(geometry); |
2694 | | return kml; |
2695 | | }, |
2696 | | /** |
2697 | | * Method: buildGeometry.multipoint |
2698 | | * Given a ZOO multipoint geometry, create a KML |
2699 | | * GeometryCollection. |
2700 | | * |
2701 | | * Parameters: |
2702 | | * geometry - {<ZOO.Geometry.MultiPoint>} A multipoint geometry. |
2703 | | * |
2704 | | * Returns: |
2705 | | * {E4XElement} A KML GeometryCollection node. |
2706 | | */ |
2707 | | 'multipoint': function(geometry) { |
2708 | | return this.buildGeometry.collection.apply(this, [geometry]); |
2709 | | }, |
2710 | | /** |
2711 | | * Method: buildGeometry.linestring |
2712 | | * Given a ZOO linestring geometry, create a KML linestring. |
2713 | | * |
2714 | | * Parameters: |
2715 | | * geometry - {<ZOO.Geometry.LineString>} A linestring geometry. |
2716 | | * |
2717 | | * Returns: |
2718 | | * {E4XElement} A KML linestring node. |
2719 | | */ |
2720 | | 'linestring': function(geometry) { |
2721 | | var kml = new XML('<LineString xmlns="'+this.kmlns+'"></LineString>'); |
2722 | | kml.coordinates = this.buildCoordinatesNode(geometry); |
2723 | | return kml; |
2724 | | }, |
2725 | | /** |
2726 | | * Method: buildGeometry.multilinestring |
2727 | | * Given a ZOO multilinestring geometry, create a KML |
2728 | | * GeometryCollection. |
2729 | | * |
2730 | | * Parameters: |
2731 | | * geometry - {<ZOO.Geometry.MultiLineString>} A multilinestring geometry. |
2732 | | * |
2733 | | * Returns: |
2734 | | * {E4XElement} A KML GeometryCollection node. |
2735 | | */ |
2736 | | 'multilinestring': function(geometry) { |
2737 | | return this.buildGeometry.collection.apply(this, [geometry]); |
2738 | | }, |
2739 | | /** |
2740 | | * Method: buildGeometry.linearring |
2741 | | * Given a ZOO linearring geometry, create a KML linearring. |
2742 | | * |
2743 | | * Parameters: |
2744 | | * geometry - {<ZOO.Geometry.LinearRing>} A linearring geometry. |
2745 | | * |
2746 | | * Returns: |
2747 | | * {E4XElement} A KML linearring node. |
2748 | | */ |
2749 | | 'linearring': function(geometry) { |
2750 | | var kml = new XML('<LinearRing xmlns="'+this.kmlns+'"></LinearRing>'); |
2751 | | kml.coordinates = this.buildCoordinatesNode(geometry); |
2752 | | return kml; |
2753 | | }, |
2754 | | /** |
2755 | | * Method: buildGeometry.polygon |
2756 | | * Given a ZOO polygon geometry, create a KML polygon. |
2757 | | * |
2758 | | * Parameters: |
2759 | | * geometry - {<ZOO.Geometry.Polygon>} A polygon geometry. |
2760 | | * |
2761 | | * Returns: |
2762 | | * {E4XElement} A KML polygon node. |
2763 | | */ |
2764 | | 'polygon': function(geometry) { |
2765 | | var kml = new XML('<Polygon xmlns="'+this.kmlns+'"></Polygon>'); |
2766 | | var rings = geometry.components; |
2767 | | var ringMember, ringGeom, type; |
2768 | | for(var i=0, len=rings.length; i<len; ++i) { |
2769 | | type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs"; |
2770 | | ringMember = new XML('<'+type+' xmlns="'+this.kmlns+'"></'+type+'>'); |
2771 | | ringMember.LinearRing = this.buildGeometry.linearring.apply(this,[rings[i]]); |
2772 | | kml.*[i] = ringMember; |
2773 | | } |
2774 | | return kml; |
2775 | | }, |
2776 | | /** |
2777 | | * Method: buildGeometry.multipolygon |
2778 | | * Given a ZOO multipolygon geometry, create a KML |
2779 | | * GeometryCollection. |
2780 | | * |
2781 | | * Parameters: |
2782 | | * geometry - {<ZOO.Geometry.Point>} A multipolygon geometry. |
2783 | | * |
2784 | | * Returns: |
2785 | | * {E4XElement} A KML GeometryCollection node. |
2786 | | */ |
2787 | | 'multipolygon': function(geometry) { |
2788 | | return this.buildGeometry.collection.apply(this, [geometry]); |
2789 | | }, |
2790 | | /** |
2791 | | * Method: buildGeometry.collection |
2792 | | * Given a ZOO geometry collection, create a KML MultiGeometry. |
2793 | | * |
2794 | | * Parameters: |
2795 | | * geometry - {<ZOO.Geometry.Collection>} A geometry collection. |
2796 | | * |
2797 | | * Returns: |
2798 | | * {E4XElement} A KML MultiGeometry node. |
2799 | | */ |
2800 | | 'collection': function(geometry) { |
2801 | | var kml = new XML('<MultiGeometry xmlns="'+this.kmlns+'"></MultiGeometry>'); |
2802 | | var child; |
2803 | | for(var i=0, len=geometry.components.length; i<len; ++i) { |
2804 | | kml.*[i] = this.buildGeometryNode.apply(this,[geometry.components[i]]); |
2805 | | } |
2806 | | return kml; |
2807 | | } |
2808 | | }, |
2809 | | /** |
2810 | | * Method: buildCoordinatesNode |
2811 | | * Builds and returns the KML coordinates node with the given geometry |
2812 | | * <coordinates>...</coordinates> |
2813 | | * |
2814 | | * Parameters: |
2815 | | * geometry - {<ZOO.Geometry>} |
2816 | | * |
2817 | | * Return: |
2818 | | * {E4XElement} |
2819 | | */ |
2820 | | buildCoordinatesNode: function(geometry) { |
2821 | | var cooridnates = new XML('<coordinates xmlns="'+this.kmlns+'"></coordinates>'); |
2822 | | var points = geometry.components; |
2823 | | if(points) { |
2824 | | // LineString or LinearRing |
2825 | | var point; |
2826 | | var numPoints = points.length; |
2827 | | var parts = new Array(numPoints); |
2828 | | for(var i=0; i<numPoints; ++i) { |
2829 | | point = points[i]; |
2830 | | parts[i] = point.x + "," + point.y; |
2831 | | } |
2832 | | coordinates = parts.join(" "); |
2833 | | } else { |
2834 | | // Point |
2835 | | coordinates = geometry.x + "," + geometry.y; |
2836 | | } |
2837 | | return coordinates; |
2838 | | }, |
2839 | | CLASS_NAME: 'ZOO.Format.KML' |
2840 | | }); |
2841 | | /** |
2842 | | * Class: ZOO.Format.GML |
2843 | | * Read/Write GML. Create a new instance with the <ZOO.Format.GML> |
2844 | | * constructor. Supports the GML simple features profile. |
2845 | | * |
2846 | | * Inherits from: |
2847 | | * - <ZOO.Format> |
2848 | | */ |
2849 | | ZOO.Format.GML = ZOO.Class(ZOO.Format, { |
2850 | | /** |
2851 | | * Property: schemaLocation |
2852 | | * {String} Schema location for a particular minor version. |
2853 | | */ |
2854 | | schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd", |
2855 | | /** |
2856 | | * Property: namespaces |
2857 | | * {Object} Mapping of namespace aliases to namespace URIs. |
2858 | | */ |
2859 | | namespaces: { |
2860 | | ogr: "http://ogr.maptools.org/", |
2861 | | gml: "http://www.opengis.net/gml", |
2862 | | xlink: "http://www.w3.org/1999/xlink", |
2863 | | xsi: "http://www.w3.org/2001/XMLSchema-instance", |
2864 | | wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection |
2865 | | }, |
2866 | | /** |
2867 | | * Property: defaultPrefix |
2868 | | */ |
2869 | | defaultPrefix: 'ogr', |
2870 | | /** |
2871 | | * Property: collectionName |
2872 | | * {String} Name of featureCollection element. |
2873 | | */ |
2874 | | collectionName: "FeatureCollection", |
2875 | | /* |
2876 | | * Property: featureName |
2877 | | * {String} Element name for features. Default is "sql_statement". |
2878 | | */ |
2879 | | featureName: "sql_statement", |
2880 | | /** |
2881 | | * Property: geometryName |
2882 | | * {String} Name of geometry element. Defaults to "geometryProperty". |
2883 | | */ |
2884 | | geometryName: "geometryProperty", |
2885 | | /** |
2886 | | * Property: xy |
2887 | | * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x) |
2888 | | * Changing is not recommended, a new Format should be instantiated. |
2889 | | */ |
2890 | | xy: true, |
2891 | | /** |
2892 | | * Constructor: ZOO.Format.GML |
2893 | | * Create a new parser for GML. |
2894 | | * |
2895 | | * Parameters: |
2896 | | * options - {Object} An optional object whose properties will be set on |
2897 | | * this instance. |
2898 | | */ |
2899 | | initialize: function(options) { |
2900 | | // compile regular expressions once instead of every time they are used |
2901 | | this.regExes = { |
2902 | | trimSpace: (/^\s*|\s*$/g), |
2903 | | removeSpace: (/\s*/g), |
2904 | | splitSpace: (/\s+/), |
2905 | | trimComma: (/\s*,\s*/g) |
2906 | | }; |
2907 | | ZOO.Format.prototype.initialize.apply(this, [options]); |
2908 | | }, |
2909 | | /** |
2910 | | * Method: read |
2911 | | * Read data from a string, and return a list of features. |
2912 | | * |
2913 | | * Parameters: |
2914 | | * data - {String} data to read/parse. |
2915 | | * |
2916 | | * Returns: |
2917 | | * {Array(<ZOO.Feature>)} An array of features. |
2918 | | */ |
2919 | | read: function(data) { |
2920 | | this.features = []; |
2921 | | data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, ""); |
2922 | | data = new XML(data); |
2923 | | |
2924 | | var gmlns = Namespace(this.namespaces['gml']); |
2925 | | var featureNodes = data..gmlns::featureMember; |
2926 | | var features = []; |
2927 | | for(var i=0,len=featureNodes.length(); i<len; i++) { |
2928 | | var feature = this.parseFeature(featureNodes[i]); |
2929 | | if(feature) { |
2930 | | features.push(feature); |
2931 | | } |
2932 | | } |
2933 | | return features; |
2934 | | }, |
2935 | | /** |
2936 | | * Method: parseFeature |
2937 | | * This function is the core of the GML parsing code in ZOO. |
2938 | | * It creates the geometries that are then attached to the returned |
2939 | | * feature, and calls parseAttributes() to get attribute data out. |
2940 | | * |
2941 | | * Parameters: |
2942 | | * node - {E4XElement} A GML feature node. |
2943 | | */ |
2944 | | parseFeature: function(node) { |
2945 | | // only accept one geometry per feature - look for highest "order" |
2946 | | var gmlns = Namespace(this.namespaces['gml']); |
2947 | | var order = ["MultiPolygon", "Polygon", |
2948 | | "MultiLineString", "LineString", |
2949 | | "MultiPoint", "Point", "Envelope", "Box"]; |
2950 | | var type, nodeList, geometry, parser; |
2951 | | for(var i=0; i<order.length; ++i) { |
2952 | | type = order[i]; |
2953 | | nodeList = node.descendants(QName(gmlns,type)); |
2954 | | if (nodeList.length() > 0) { |
2955 | | var parser = this.parseGeometry[type.toLowerCase()]; |
2956 | | if(parser) { |
2957 | | geometry = parser.apply(this, [nodeList[0]]); |
2958 | | if (this.internalProjection && this.externalProjection) { |
2959 | | geometry.transform(this.externalProjection, |
2960 | | this.internalProjection); |
2961 | | } |
2962 | | } |
2963 | | // stop looking for different geometry types |
2964 | | break; |
2965 | | } |
2966 | | } |
2967 | | var attributes; |
2968 | | if(this.extractAttributes) { |
2969 | | attributes = this.parseAttributes(node); |
2970 | | } |
2971 | | var feature = new ZOO.Feature(geometry, attributes); |
2972 | | return feature; |
2973 | | }, |
2974 | | /** |
2975 | | * Property: parseGeometry |
2976 | | * Properties of this object are the functions that parse geometries based |
2977 | | * on their type. |
2978 | | */ |
2979 | | parseGeometry: { |
2980 | | /** |
2981 | | * Method: parseGeometry.point |
2982 | | * Given a GML node representing a point geometry, create a ZOO |
2983 | | * point geometry. |
2984 | | * |
2985 | | * Parameters: |
2986 | | * node - {E4XElement} A GML node. |
2987 | | * |
2988 | | * Returns: |
2989 | | * {<ZOO.Geometry.Point>} A point geometry. |
2990 | | */ |
2991 | | 'point': function(node) { |
2992 | | /** |
2993 | | * Three coordinate variations to consider: |
2994 | | * 1) <gml:pos>x y z</gml:pos> |
2995 | | * 2) <gml:coordinates>x, y, z</gml:coordinates> |
2996 | | * 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord> |
2997 | | */ |
2998 | | var nodeList, coordString; |
2999 | | var coords = []; |
3000 | | // look for <gml:pos> |
3001 | | var nodeList = node..*::pos; |
3002 | | if(nodeList.length() > 0) { |
3003 | | coordString = nodeList[0].toString(); |
3004 | | coordString = coordString.replace(this.regExes.trimSpace, ""); |
3005 | | coords = coordString.split(this.regExes.splitSpace); |
3006 | | } |
3007 | | // look for <gml:coordinates> |
3008 | | if(coords.length == 0) { |
3009 | | nodeList = node..*::coordinates; |
3010 | | if(nodeList.length() > 0) { |
3011 | | coordString = nodeList[0].toString(); |
3012 | | coordString = coordString.replace(this.regExes.removeSpace,""); |
3013 | | coords = coordString.split(","); |
3014 | | } |
3015 | | } |
3016 | | // look for <gml:coord> |
3017 | | if(coords.length == 0) { |
3018 | | nodeList = node..*::coord; |
3019 | | if(nodeList.length() > 0) { |
3020 | | var xList = nodeList[0].*::X; |
3021 | | var yList = nodeList[0].*::Y; |
3022 | | if(xList.length() > 0 && yList.length() > 0) |
3023 | | coords = [xList[0].toString(), |
3024 | | yList[0].toString()]; |
3025 | | } |
3026 | | } |
3027 | | // preserve third dimension |
3028 | | if(coords.length == 2) |
3029 | | coords[2] = null; |
3030 | | if (this.xy) |
3031 | | return new ZOO.Geometry.Point(coords[0],coords[1],coords[2]); |
3032 | | else |
3033 | | return new ZOO.Geometry.Point(coords[1],coords[0],coords[2]); |
3034 | | }, |
3035 | | /** |
3036 | | * Method: parseGeometry.multipoint |
3037 | | * Given a GML node representing a multipoint geometry, create a |
3038 | | * ZOO multipoint geometry. |
3039 | | * |
3040 | | * Parameters: |
3041 | | * node - {E4XElement} A GML node. |
3042 | | * |
3043 | | * Returns: |
3044 | | * {<ZOO.Geometry.MultiPoint>} A multipoint geometry. |
3045 | | */ |
3046 | | 'multipoint': function(node) { |
3047 | | var nodeList = node..*::Point; |
3048 | | var components = []; |
3049 | | if(nodeList.length() > 0) { |
3050 | | var point; |
3051 | | for(var i=0, len=nodeList.length(); i<len; ++i) { |
3052 | | point = this.parseGeometry.point.apply(this, [nodeList[i]]); |
3053 | | if(point) |
3054 | | components.push(point); |
3055 | | } |
3056 | | } |
3057 | | return new ZOO.Geometry.MultiPoint(components); |
3058 | | }, |
3059 | | /** |
3060 | | * Method: parseGeometry.linestring |
3061 | | * Given a GML node representing a linestring geometry, create a |
3062 | | * ZOO linestring geometry. |
3063 | | * |
3064 | | * Parameters: |
3065 | | * node - {E4XElement} A GML node. |
3066 | | * |
3067 | | * Returns: |
3068 | | * {<ZOO.Geometry.LineString>} A linestring geometry. |
3069 | | */ |
3070 | | 'linestring': function(node, ring) { |
3071 | | /** |
3072 | | * Two coordinate variations to consider: |
3073 | | * 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList> |
3074 | | * 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates> |
3075 | | */ |
3076 | | var nodeList, coordString; |
3077 | | var coords = []; |
3078 | | var points = []; |
3079 | | // look for <gml:posList> |
3080 | | nodeList = node..*::posList; |
3081 | | if(nodeList.length() > 0) { |
3082 | | coordString = nodeList[0].toString(); |
3083 | | coordString = coordString.replace(this.regExes.trimSpace, ""); |
3084 | | coords = coordString.split(this.regExes.splitSpace); |
3085 | | var dim = parseInt(nodeList[0].@dimension); |
3086 | | var j, x, y, z; |
3087 | | for(var i=0; i<coords.length/dim; ++i) { |
3088 | | j = i * dim; |
3089 | | x = coords[j]; |
3090 | | y = coords[j+1]; |
3091 | | z = (dim == 2) ? null : coords[j+2]; |
3092 | | if (this.xy) |
3093 | | points.push(new ZOO.Geometry.Point(x, y, z)); |
3094 | | else |
3095 | | points.push(new Z0O.Geometry.Point(y, x, z)); |
3096 | | } |
3097 | | } |
3098 | | // look for <gml:coordinates> |
3099 | | if(coords.length == 0) { |
3100 | | nodeList = node..*::coordinates; |
3101 | | if(nodeList.length() > 0) { |
3102 | | coordString = nodeList[0].toString(); |
3103 | | coordString = coordString.replace(this.regExes.trimSpace,""); |
3104 | | coordString = coordString.replace(this.regExes.trimComma,","); |
3105 | | var pointList = coordString.split(this.regExes.splitSpace); |
3106 | | for(var i=0; i<pointList.length; ++i) { |
3107 | | coords = pointList[i].split(","); |
3108 | | if(coords.length == 2) |
3109 | | coords[2] = null; |
3110 | | if (this.xy) |
3111 | | points.push(new ZOO.Geometry.Point(coords[0],coords[1],coords[2])); |
3112 | | else |
3113 | | points.push(new ZOO.Geometry.Point(coords[1],coords[0],coords[2])); |
3114 | | } |
3115 | | } |
3116 | | } |
3117 | | var line = null; |
3118 | | if(points.length != 0) { |
3119 | | if(ring) |
3120 | | line = new ZOO.Geometry.LinearRing(points); |
3121 | | else |
3122 | | line = new ZOO.Geometry.LineString(points); |
3123 | | } |
3124 | | return line; |
3125 | | }, |
3126 | | /** |
3127 | | * Method: parseGeometry.multilinestring |
3128 | | * Given a GML node representing a multilinestring geometry, create a |
3129 | | * ZOO multilinestring geometry. |
3130 | | * |
3131 | | * Parameters: |
3132 | | * node - {E4XElement} A GML node. |
3133 | | * |
3134 | | * Returns: |
3135 | | * {<ZOO.Geometry.MultiLineString>} A multilinestring geometry. |
3136 | | */ |
3137 | | 'multilinestring': function(node) { |
3138 | | var nodeList = node..*::LineString; |
3139 | | var components = []; |
3140 | | if(nodeList.length() > 0) { |
3141 | | var line; |
3142 | | for(var i=0, len=nodeList.length(); i<len; ++i) { |
3143 | | line = this.parseGeometry.linestring.apply(this, [nodeList[i]]); |
3144 | | if(point) |
3145 | | components.push(point); |
3146 | | } |
3147 | | } |
3148 | | return new ZOO.Geometry.MultiLineString(components); |
3149 | | }, |
3150 | | /** |
3151 | | * Method: parseGeometry.polygon |
3152 | | * Given a GML node representing a polygon geometry, create a |
3153 | | * ZOO polygon geometry. |
3154 | | * |
3155 | | * Parameters: |
3156 | | * node - {E4XElement} A GML node. |
3157 | | * |
3158 | | * Returns: |
3159 | | * {<ZOO.Geometry.Polygon>} A polygon geometry. |
3160 | | */ |
3161 | | 'polygon': function(node) { |
3162 | | nodeList = node..*::LinearRing; |
3163 | | var components = []; |
3164 | | if(nodeList.length() > 0) { |
3165 | | // this assumes exterior ring first, inner rings after |
3166 | | var ring; |
3167 | | for(var i=0, len = nodeList.length(); i<len; ++i) { |
3168 | | ring = this.parseGeometry.linestring.apply(this,[nodeList[i], true]); |
3169 | | if(ring) |
3170 | | components.push(ring); |
3171 | | } |
3172 | | } |
3173 | | return new ZOO.Geometry.Polygon(components); |
3174 | | }, |
3175 | | /** |
3176 | | * Method: parseGeometry.multipolygon |
3177 | | * Given a GML node representing a multipolygon geometry, create a |
3178 | | * ZOO multipolygon geometry. |
3179 | | * |
3180 | | * Parameters: |
3181 | | * node - {E4XElement} A GML node. |
3182 | | * |
3183 | | * Returns: |
3184 | | * {<ZOO.Geometry.MultiPolygon>} A multipolygon geometry. |
3185 | | */ |
3186 | | 'multipolygon': function(node) { |
3187 | | var nodeList = node..*::Polygon; |
3188 | | var components = []; |
3189 | | if(nodeList.length() > 0) { |
3190 | | var polygon; |
3191 | | for(var i=0, len=nodeList.length(); i<len; ++i) { |
3192 | | polygon = this.parseGeometry.polygon.apply(this, [nodeList[i]]); |
3193 | | if(polygon) |
3194 | | components.push(polygon); |
3195 | | } |
3196 | | } |
3197 | | return new ZOO.Geometry.MultiPolygon(components); |
3198 | | }, |
3199 | | /** |
3200 | | * Method: parseGeometry.polygon |
3201 | | * Given a GML node representing an envelope, create a |
3202 | | * ZOO polygon geometry. |
3203 | | * |
3204 | | * Parameters: |
3205 | | * node - {E4XElement} A GML node. |
3206 | | * |
3207 | | * Returns: |
3208 | | * {<ZOO.Geometry.Polygon>} A polygon geometry. |
3209 | | */ |
3210 | | 'envelope': function(node) { |
3211 | | var components = []; |
3212 | | var coordString; |
3213 | | var envelope; |
3214 | | var lpoint = node..*::lowerCorner; |
3215 | | if (lpoint.length() > 0) { |
3216 | | var coords = []; |
3217 | | if(lpoint.length() > 0) { |
3218 | | coordString = lpoint[0].toString(); |
3219 | | coordString = coordString.replace(this.regExes.trimSpace, ""); |
3220 | | coords = coordString.split(this.regExes.splitSpace); |
3221 | | } |
3222 | | if(coords.length == 2) |
3223 | | coords[2] = null; |
3224 | | if (this.xy) |
3225 | | var lowerPoint = new ZOO.Geometry.Point(coords[0], coords[1],coords[2]); |
3226 | | else |
3227 | | var lowerPoint = new ZOO.Geometry.Point(coords[1], coords[0],coords[2]); |
3228 | | } |
3229 | | var upoint = node..*::upperCorner; |
3230 | | if (upoint.length() > 0) { |
3231 | | var coords = []; |
3232 | | if(upoint.length > 0) { |
3233 | | coordString = upoint[0].toString(); |
3234 | | coordString = coordString.replace(this.regExes.trimSpace, ""); |
3235 | | coords = coordString.split(this.regExes.splitSpace); |
3236 | | } |
3237 | | if(coords.length == 2) |
3238 | | coords[2] = null; |
3239 | | if (this.xy) |
3240 | | var upperPoint = new ZOO.Geometry.Point(coords[0], coords[1],coords[2]); |
3241 | | else |
3242 | | var upperPoint = new ZOO.Geometry.Point(coords[1], coords[0],coords[2]); |
3243 | | } |
3244 | | if (lowerPoint && upperPoint) { |
3245 | | components.push(new ZOO.Geometry.Point(lowerPoint.x, lowerPoint.y)); |
3246 | | components.push(new ZOO.Geometry.Point(upperPoint.x, lowerPoint.y)); |
3247 | | components.push(new ZOO.Geometry.Point(upperPoint.x, upperPoint.y)); |
3248 | | components.push(new ZOO.Geometry.Point(lowerPoint.x, upperPoint.y)); |
3249 | | components.push(new ZOO.Geometry.Point(lowerPoint.x, lowerPoint.y)); |
3250 | | var ring = new ZOO.Geometry.LinearRing(components); |
3251 | | envelope = new ZOO.Geometry.Polygon([ring]); |
3252 | | } |
3253 | | return envelope; |
3254 | | } |
3255 | | }, |
3256 | | /** |
3257 | | * Method: parseAttributes |
3258 | | * |
3259 | | * Parameters: |
3260 | | * node - {<E4XElement>} |
3261 | | * |
3262 | | * Returns: |
3263 | | * {Object} An attributes object. |
3264 | | */ |
3265 | | parseAttributes: function(node) { |
3266 | | var attributes = {}; |
3267 | | // assume attributes are children of the first type 1 child |
3268 | | var childNode = node.*::*[0]; |
3269 | | var child, grandchildren; |
3270 | | var children = childNode.*::*; |
3271 | | for(var i=0, len=children.length(); i<len; ++i) { |
3272 | | child = children[i]; |
3273 | | grandchildren = child..*::*; |
3274 | | if(grandchildren.length() == 1) { |
3275 | | var name = child.localName(); |
3276 | | var value = child.toString(); |
3277 | | if (value) { |
3278 | | value = value.replace(this.regExes.trimSpace, ""); |
3279 | | attributes[name] = value; |
3280 | | } else |
3281 | | attributes[name] = null; |
3282 | | } |
3283 | | } |
3284 | | return attributes; |
3285 | | }, |
3286 | | /** |
3287 | | * Method: write |
3288 | | * Generate a GML document string given a list of features. |
3289 | | * |
3290 | | * Parameters: |
3291 | | * features - {Array(<ZOO.Feature>)} List of features to |
3292 | | * serialize into a string. |
3293 | | * |
3294 | | * Returns: |
3295 | | * {String} A string representing the GML document. |
3296 | | */ |
3297 | | write: function(features) { |
3298 | | if(!(features instanceof Array)) { |
3299 | | features = [features]; |
3300 | | } |
3301 | | var pfx = this.defaultPrefix; |
3302 | | var name = pfx+':'+this.collectionName; |
3303 | | var gml = new XML('<'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'" xmlns:gml="'+this.namespaces['gml']+'" xmlns:xsi="'+this.namespaces['xsi']+'" xsi:schemaLocation="'+this.schemaLocation+'"></'+name+'>'); |
3304 | | for(var i=0; i<features.length; i++) { |
3305 | | gml.*::*[i] = this.createFeature(features[i]); |
3306 | | } |
3307 | | return gml.toXMLString(); |
3308 | | }, |
3309 | | /** |
3310 | | * Method: createFeature |
3311 | | * Accept an ZOO.Feature, and build a GML node for it. |
3312 | | * |
3313 | | * Parameters: |
3314 | | * feature - {<ZOO.Feature>} The feature to be built as GML. |
3315 | | * |
3316 | | * Returns: |
3317 | | * {E4XElement} A node reprensting the feature in GML. |
3318 | | */ |
3319 | | createFeature: function(feature) { |
3320 | | var pfx = this.defaultPrefix; |
3321 | | var name = pfx+':'+this.featureName; |
3322 | | var fid = feature.fid || feature.id; |
3323 | | var gml = new XML('<gml:featureMember xmlns:gml="'+this.namespaces['gml']+'"><'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'" fid="'+fid+'"></'+name+'></gml:featureMember>'); |
3324 | | var geometry = feature.geometry; |
3325 | | gml.*::*[0].*::* = this.buildGeometryNode(geometry); |
3326 | | for(var attr in feature.attributes) { |
3327 | | var attrNode = new XML('<'+pfx+':'+attr+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'">'+feature.attributes[attr]+'</'+pfx+':'+attr+'>'); |
3328 | | gml.*::*[0].appendChild(attrNode); |
3329 | | } |
3330 | | return gml; |
3331 | | }, |
3332 | | /** |
3333 | | * Method: buildGeometryNode |
3334 | | * |
3335 | | * Parameters: |
3336 | | * geometry - {<ZOO.Geometry>} The geometry to be built as GML. |
3337 | | * |
3338 | | * Returns: |
3339 | | * {E4XElement} A node reprensting the geometry in GML. |
3340 | | */ |
3341 | | buildGeometryNode: function(geometry) { |
3342 | | if (this.externalProjection && this.internalProjection) { |
3343 | | geometry = geometry.clone(); |
3344 | | geometry.transform(this.internalProjection, |
3345 | | this.externalProjection); |
3346 | | } |
3347 | | var className = geometry.CLASS_NAME; |
3348 | | var type = className.substring(className.lastIndexOf(".") + 1); |
3349 | | var builder = this.buildGeometry[type.toLowerCase()]; |
3350 | | var pfx = this.defaultPrefix; |
3351 | | var name = pfx+':'+this.geometryName; |
3352 | | var gml = new XML('<'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'"></'+name+'>'); |
3353 | | if (builder) |
3354 | | gml.*::* = builder.apply(this, [geometry]); |
3355 | | return gml; |
3356 | | }, |
3357 | | /** |
3358 | | * Property: buildGeometry |
3359 | | * Object containing methods to do the actual geometry node building |
3360 | | * based on geometry type. |
3361 | | */ |
3362 | | buildGeometry: { |
3363 | | /** |
3364 | | * Method: buildGeometry.point |
3365 | | * Given a ZOO point geometry, create a GML point. |
3366 | | * |
3367 | | * Parameters: |
3368 | | * geometry - {<ZOO.Geometry.Point>} A point geometry. |
3369 | | * |
3370 | | * Returns: |
3371 | | * {E4XElement} A GML point node. |
3372 | | */ |
3373 | | 'point': function(geometry) { |
3374 | | var gml = new XML('<gml:Point xmlns:gml="'+this.namespaces['gml']+'"></gml:Point>'); |
3375 | | gml.*::*[0] = this.buildCoordinatesNode(geometry); |
3376 | | return gml; |
3377 | | }, |
3378 | | /** |
3379 | | * Method: buildGeometry.multipoint |
3380 | | * Given a ZOO multipoint geometry, create a GML multipoint. |
3381 | | * |
3382 | | * Parameters: |
3383 | | * geometry - {<ZOO.Geometry.MultiPoint>} A multipoint geometry. |
3384 | | * |
3385 | | * Returns: |
3386 | | * {E4XElement} A GML multipoint node. |
3387 | | */ |
3388 | | 'multipoint': function(geometry) { |
3389 | | var gml = new XML('<gml:MultiPoint xmlns:gml="'+this.namespaces['gml']+'"></gml:MultiPoint>'); |
3390 | | var points = geometry.components; |
3391 | | var pointMember; |
3392 | | for(var i=0; i<points.length; i++) { |
3393 | | pointMember = new XML('<gml:pointMember xmlns:gml="'+this.namespaces['gml']+'"></gml:pointMember>'); |
3394 | | pointMember.*::* = this.buildGeometry.point.apply(this,[points[i]]); |
3395 | | gml.*::*[i] = pointMember; |
3396 | | } |
3397 | | return gml; |
3398 | | }, |
3399 | | /** |
3400 | | * Method: buildGeometry.linestring |
3401 | | * Given a ZOO linestring geometry, create a GML linestring. |
3402 | | * |
3403 | | * Parameters: |
3404 | | * geometry - {<ZOO.Geometry.LineString>} A linestring geometry. |
3405 | | * |
3406 | | * Returns: |
3407 | | * {E4XElement} A GML linestring node. |
3408 | | */ |
3409 | | 'linestring': function(geometry) { |
3410 | | var gml = new XML('<gml:LineString xmlns:gml="'+this.namespaces['gml']+'"></gml:LineString>'); |
3411 | | gml.*::*[0] = this.buildCoordinatesNode(geometry); |
3412 | | return gml; |
3413 | | }, |
3414 | | /** |
3415 | | * Method: buildGeometry.multilinestring |
3416 | | * Given a ZOO multilinestring geometry, create a GML |
3417 | | * multilinestring. |
3418 | | * |
3419 | | * Parameters: |
3420 | | * geometry - {<ZOO.Geometry.MultiLineString>} A multilinestring |
3421 | | * geometry. |
3422 | | * |
3423 | | * Returns: |
3424 | | * {E4XElement} A GML multilinestring node. |
3425 | | */ |
3426 | | 'multilinestring': function(geometry) { |
3427 | | var gml = new XML('<gml:MultiLineString xmlns:gml="'+this.namespaces['gml']+'"></gml:MultiLineString>'); |
3428 | | var lines = geometry.components; |
3429 | | var lineMember; |
3430 | | for(var i=0; i<lines.length; i++) { |
3431 | | lineMember = new XML('<gml:lineStringMember xmlns:gml="'+this.namespaces['gml']+'"></gml:lineStringMember>'); |
3432 | | lineMember.*::* = this.buildGeometry.linestring.apply(this,[lines[i]]); |
3433 | | gml.*::*[i] = lineMember; |
3434 | | } |
3435 | | return gml; |
3436 | | }, |
3437 | | /** |
3438 | | * Method: buildGeometry.linearring |
3439 | | * Given a ZOO linearring geometry, create a GML linearring. |
3440 | | * |
3441 | | * Parameters: |
3442 | | * geometry - {<ZOO.Geometry.LinearRing>} A linearring geometry. |
3443 | | * |
3444 | | * Returns: |
3445 | | * {E4XElement} A GML linearring node. |
3446 | | */ |
3447 | | 'linearring': function(geometry) { |
3448 | | var gml = new XML('<gml:LinearRing xmlns:gml="'+this.namespaces['gml']+'"></gml:LinearRing>'); |
3449 | | gml.*::*[0] = this.buildCoordinatesNode(geometry); |
3450 | | return gml; |
3451 | | }, |
3452 | | /** |
3453 | | * Method: buildGeometry.polygon |
3454 | | * Given an ZOO polygon geometry, create a GML polygon. |
3455 | | * |
3456 | | * Parameters: |
3457 | | * geometry - {<ZOO.Geometry.Polygon>} A polygon geometry. |
3458 | | * |
3459 | | * Returns: |
3460 | | * {E4XElement} A GML polygon node. |
3461 | | */ |
3462 | | 'polygon': function(geometry) { |
3463 | | var gml = new XML('<gml:Polygon xmlns:gml="'+this.namespaces['gml']+'"></gml:Polygon>'); |
3464 | | var rings = geometry.components; |
3465 | | var ringMember, type; |
3466 | | for(var i=0; i<rings.length; ++i) { |
3467 | | type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs"; |
3468 | | var ringMember = new XML('<gml:'+type+' xmlns:gml="'+this.namespaces['gml']+'"></gml:'+type+'>'); |
3469 | | ringMember.*::* = this.buildGeometry.linearring.apply(this,[rings[i]]); |
3470 | | gml.*::*[i] = ringMember; |
3471 | | } |
3472 | | return gml; |
3473 | | }, |
3474 | | /** |
3475 | | * Method: buildGeometry.multipolygon |
3476 | | * Given a ZOO multipolygon geometry, create a GML multipolygon. |
3477 | | * |
3478 | | * Parameters: |
3479 | | * geometry - {<ZOO.Geometry.MultiPolygon>} A multipolygon |
3480 | | * geometry. |
3481 | | * |
3482 | | * Returns: |
3483 | | * {E4XElement} A GML multipolygon node. |
3484 | | */ |
3485 | | 'multipolygon': function(geometry) { |
3486 | | var gml = new XML('<gml:MultiPolygon xmlns:gml="'+this.namespaces['gml']+'"></gml:MultiPolygon>'); |
3487 | | var polys = geometry.components; |
3488 | | var polyMember; |
3489 | | for(var i=0; i<polys.length; i++) { |
3490 | | polyMember = new XML('<gml:polygonMember xmlns:gml="'+this.namespaces['gml']+'"></gml:polygonMember>'); |
3491 | | polyMember.*::* = this.buildGeometry.polygon.apply(this,[polys[i]]); |
3492 | | gml.*::*[i] = polyMember; |
3493 | | } |
3494 | | return gml; |
3495 | | } |
3496 | | }, |
3497 | | /** |
3498 | | * Method: buildCoordinatesNode |
3499 | | * builds the coordinates XmlNode |
3500 | | * (code) |
3501 | | * <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates> |
3502 | | * (end) |
3503 | | * Parameters: |
3504 | | * geometry - {<ZOO.Geometry>} |
3505 | | * |
3506 | | * Returns: |
3507 | | * {E4XElement} created E4XElement |
3508 | | */ |
3509 | | buildCoordinatesNode: function(geometry) { |
3510 | | var parts = []; |
3511 | | if(geometry instanceof ZOO.Bounds){ |
3512 | | parts.push(geometry.left + "," + geometry.bottom); |
3513 | | parts.push(geometry.right + "," + geometry.top); |
3514 | | } else { |
3515 | | var points = (geometry.components) ? geometry.components : [geometry]; |
3516 | | for(var i=0; i<points.length; i++) { |
3517 | | parts.push(points[i].x + "," + points[i].y); |
3518 | | } |
3519 | | } |
3520 | | return new XML('<gml:coordinates xmlns:gml="'+this.namespaces['gml']+'" decimal="." cs=", " ts=" ">'+parts.join(" ")+'</gml:coordinates>'); |
3521 | | }, |
3522 | | CLASS_NAME: 'ZOO.Format.GML' |
3523 | | }); |
3524 | | /** |
3525 | | * Class: ZOO.Format.WPS |
3526 | | * Read/Write WPS. Create a new instance with the <ZOO.Format.WPS> |
3527 | | * constructor. Supports only parseExecuteResponse. |
3528 | | * |
3529 | | * Inherits from: |
3530 | | * - <ZOO.Format> |
3531 | | */ |
3532 | | ZOO.Format.WPS = ZOO.Class(ZOO.Format, { |
3533 | | /** |
3534 | | * Property: schemaLocation |
3535 | | * {String} Schema location for a particular minor version. |
3536 | | */ |
3537 | | schemaLocation: "http://www.opengis.net/wps/1.0.0/../wpsExecute_request.xsd", |
3538 | | /** |
3539 | | * Property: namespaces |
3540 | | * {Object} Mapping of namespace aliases to namespace URIs. |
3541 | | */ |
3542 | | namespaces: { |
3543 | | ows: "http://www.opengis.net/ows/1.1", |
3544 | | wps: "http://www.opengis.net/wps/1.0.0", |
3545 | | xlink: "http://www.w3.org/1999/xlink", |
3546 | | xsi: "http://www.w3.org/2001/XMLSchema-instance", |
3547 | | }, |
3548 | | /** |
3549 | | * Method: read |
3550 | | * |
3551 | | * Parameters: |
3552 | | * data - {String} A WPS xml document |
3553 | | * |
3554 | | * Returns: |
3555 | | * {Object} Execute response. |
3556 | | */ |
3557 | | read:function(data) { |
3558 | | data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, ""); |
3559 | | data = new XML(data); |
3560 | | switch (data.localName()) { |
3561 | | case 'ExecuteResponse': |
3562 | | return this.parseExecuteResponse(data); |
3563 | | default: |
3564 | | return null; |
3565 | | } |
3566 | | }, |
3567 | | /** |
3568 | | * Method: parseExecuteResponse |
3569 | | * |
3570 | | * Parameters: |
3571 | | * node - {E4XElement} A WPS ExecuteResponse document |
3572 | | * |
3573 | | * Returns: |
3574 | | * {Object} Execute response. |
3575 | | */ |
3576 | | parseExecuteResponse: function(node) { |
3577 | | var outputs = node.*::ProcessOutputs.*::Output; |
3578 | | if (outputs.length() > 0) { |
3579 | | var data = outputs[0].*::Data.*::*[0]; |
3580 | | var builder = this.parseData[data.localName().toLowerCase()]; |
3581 | | if (builder) |
3582 | | return builder.apply(this,[data]); |
3583 | | else |
3584 | | return null; |
3585 | | } else |
3586 | | return null; |
3587 | | }, |
3588 | | /** |
3589 | | * Property: parseData |
3590 | | * Object containing methods to analyse data response. |
3591 | | */ |
3592 | | parseData: { |
3593 | | /** |
3594 | | * Method: parseData.complexdata |
3595 | | * Given an Object representing the WPS complex data response. |
3596 | | * |
3597 | | * Parameters: |
3598 | | * node - {E4XElement} A WPS node. |
3599 | | * |
3600 | | * Returns: |
3601 | | * {Object} A WPS complex data response. |
3602 | | */ |
3603 | | 'complexdata': function(node) { |
3604 | | var result = {value:node.toString()}; |
3605 | | if (node.@mimeType.length()>0) |
3606 | | result.mimeType = node.@mimeType; |
3607 | | if (node.@encoding.length()>0) |
3608 | | result.encoding = node.@encoding; |
3609 | | if (node.@schema.length()>0) |
3610 | | result.schema = node.@schema; |
3611 | | return result; |
3612 | | }, |
3613 | | /** |
3614 | | * Method: parseData.literaldata |
3615 | | * Given an Object representing the WPS literal data response. |
3616 | | * |
3617 | | * Parameters: |
3618 | | * node - {E4XElement} A WPS node. |
3619 | | * |
3620 | | * Returns: |
3621 | | * {Object} A WPS literal data response. |
3622 | | */ |
3623 | | 'literaldata': function(node) { |
3624 | | var result = {value:node.toString()}; |
3625 | | if (node.@dataType.length()>0) |
3626 | | result.dataType = node.@dataType; |
3627 | | if (node.@uom.length()>0) |
3628 | | result.uom = node.@uom; |
3629 | | return result; |
3630 | | } |
3631 | | }, |
3632 | | CLASS_NAME: 'ZOO.Format.WPS' |
3633 | | }); |
3634 | | |
3635 | | /** |
3636 | | * Class: ZOO.Feature |
3637 | | * Vector features use the ZOO.Geometry classes as geometry description. |
3638 | | * They have an 'attributes' property, which is the data object |
3639 | | */ |
3640 | | ZOO.Feature = ZOO.Class({ |
3641 | | /** |
3642 | | * Property: fid |
3643 | | * {String} |
3644 | | */ |
3645 | | fid: null, |
3646 | | /** |
3647 | | * Property: geometry |
3648 | | * {<ZOO.Geometry>} |
3649 | | */ |
3650 | | geometry: null, |
3651 | | /** |
3652 | | * Property: attributes |
3653 | | * {Object} This object holds arbitrary properties that describe the |
3654 | | * feature. |
3655 | | */ |
3656 | | attributes: null, |
3657 | | /** |
3658 | | * Property: bounds |
3659 | | * {<ZOO.Bounds>} The box bounding that feature's geometry, that |
3660 | | * property can be set by an <ZOO.Format> object when |
3661 | | * deserializing the feature, so in most cases it represents an |
3662 | | * information set by the server. |
3663 | | */ |
3664 | | bounds: null, |
3665 | | /** |
3666 | | * Constructor: ZOO.Feature |
3667 | | * Create a vector feature. |
3668 | | * |
3669 | | * Parameters: |
3670 | | * geometry - {<ZOO.Geometry>} The geometry that this feature |
3671 | | * represents. |
3672 | | * attributes - {Object} An optional object that will be mapped to the |
3673 | | * <attributes> property. |
3674 | | */ |
3675 | | initialize: function(geometry, attributes) { |
3676 | | this.geometry = geometry ? geometry : null; |
3677 | | this.attributes = {}; |
3678 | | if (attributes) |
3679 | | this.attributes = ZOO.extend(this.attributes,attributes); |
3680 | | }, |
3681 | | /** |
3682 | | * Method: destroy |
3683 | | * nullify references to prevent circular references and memory leaks |
3684 | | */ |
3685 | | destroy: function() { |
3686 | | this.geometry = null; |
3687 | | }, |
3688 | | /** |
3689 | | * Method: clone |
3690 | | * Create a clone of this vector feature. Does not set any non-standard |
3691 | | * properties. |
3692 | | * |
3693 | | * Returns: |
3694 | | * {<ZOO.Feature>} An exact clone of this vector feature. |
3695 | | */ |
3696 | | clone: function () { |
3697 | | return new ZOO.Feature(this.geometry ? this.geometry.clone() : null, |
3698 | | this.attributes); |
3699 | | }, |
3700 | | /** |
3701 | | * Method: move |
3702 | | * Moves the feature and redraws it at its new location |
3703 | | * |
3704 | | * Parameters: |
3705 | | * x - {Float} |
3706 | | * y - {Float} |
3707 | | */ |
3708 | | move: function(x, y) { |
3709 | | if(!this.geometry.move) |
3710 | | return; |
3711 | | |
3712 | | this.geometry.move(x,y); |
3713 | | return this.geometry; |
3714 | | }, |
3715 | | CLASS_NAME: 'ZOO.Feature' |
3716 | | }); |
3717 | | |
3718 | | /** |
3719 | | * Class: ZOO.Geometry |
3720 | | * A Geometry is a description of a geographic object. Create an instance |
3721 | | * of this class with the <ZOO.Geometry> constructor. This is a base class, |
3722 | | * typical geometry types are described by subclasses of this class. |
3723 | | */ |
3724 | | ZOO.Geometry = ZOO.Class({ |
3725 | | /** |
3726 | | * Property: id |
3727 | | * {String} A unique identifier for this geometry. |
3728 | | */ |
3729 | | id: null, |
3730 | | /** |
3731 | | * Property: parent |
3732 | | * {<ZOO.Geometry>}This is set when a Geometry is added as component |
3733 | | * of another geometry |
3734 | | */ |
3735 | | parent: null, |
3736 | | /** |
3737 | | * Property: bounds |
3738 | | * {<ZOO.Bounds>} The bounds of this geometry |
3739 | | */ |
3740 | | bounds: null, |
3741 | | /** |
3742 | | * Constructor: ZOO.Geometry |
3743 | | * Creates a geometry object. |
3744 | | */ |
3745 | | initialize: function() { |
3746 | | //generate unique id |
3747 | | }, |
3748 | | /** |
3749 | | * Method: destroy |
3750 | | * Destroy this geometry. |
3751 | | */ |
3752 | | destroy: function() { |
3753 | | this.id = null; |
3754 | | this.bounds = null; |
3755 | | }, |
3756 | | /** |
3757 | | * Method: clone |
3758 | | * Create a clone of this geometry. Does not set any non-standard |
3759 | | * properties of the cloned geometry. |
3760 | | * |
3761 | | * Returns: |
3762 | | * {<ZOO.Geometry>} An exact clone of this geometry. |
3763 | | */ |
3764 | | clone: function() { |
3765 | | return new ZOO.Geometry(); |
3766 | | }, |
3767 | | /** |
3768 | | * Method: extendBounds |
3769 | | * Extend the existing bounds to include the new bounds. |
3770 | | * If geometry's bounds is not yet set, then set a new Bounds. |
3771 | | * |
3772 | | * Parameters: |
3773 | | * newBounds - {<ZOO.Bounds>} |
3774 | | */ |
3775 | | extendBounds: function(newBounds){ |
3776 | | var bounds = this.getBounds(); |
3777 | | if (!bounds) |
3778 | | this.setBounds(newBounds); |
3779 | | else |
3780 | | this.bounds.extend(newBounds); |
3781 | | }, |
3782 | | /** |
3783 | | * Set the bounds for this Geometry. |
3784 | | * |
3785 | | * Parameters: |
3786 | | * bounds - {<ZOO.Bounds>} |
3787 | | */ |
3788 | | setBounds: function(bounds) { |
3789 | | if (bounds) |
3790 | | this.bounds = bounds.clone(); |
3791 | | }, |
3792 | | /** |
3793 | | * Method: clearBounds |
3794 | | * Nullify this components bounds and that of its parent as well. |
3795 | | */ |
3796 | | clearBounds: function() { |
3797 | | this.bounds = null; |
3798 | | if (this.parent) |
3799 | | this.parent.clearBounds(); |
3800 | | }, |
3801 | | /** |
3802 | | * Method: getBounds |
3803 | | * Get the bounds for this Geometry. If bounds is not set, it |
3804 | | * is calculated again, this makes queries faster. |
3805 | | * |
3806 | | * Returns: |
3807 | | * {<ZOO.Bounds>} |
3808 | | */ |
3809 | | getBounds: function() { |
3810 | | if (this.bounds == null) { |
3811 | | this.calculateBounds(); |
3812 | | } |
3813 | | return this.bounds; |
3814 | | }, |
3815 | | /** |
3816 | | * Method: calculateBounds |
3817 | | * Recalculate the bounds for the geometry. |
3818 | | */ |
3819 | | calculateBounds: function() { |
3820 | | // This should be overridden by subclasses. |
3821 | | return this.bounds; |
3822 | | }, |
3823 | | distanceTo: function(geometry, options) { |
3824 | | }, |
3825 | | getVertices: function(nodes) { |
3826 | | }, |
3827 | | getLength: function() { |
3828 | | return 0.0; |
3829 | | }, |
3830 | | getArea: function() { |
3831 | | return 0.0; |
3832 | | }, |
3833 | | getCentroid: function() { |
3834 | | return null; |
3835 | | }, |
3836 | | /** |
3837 | | * Method: toString |
3838 | | * Returns the Well-Known Text representation of a geometry |
3839 | | * |
3840 | | * Returns: |
3841 | | * {String} Well-Known Text |
3842 | | */ |
3843 | | toString: function() { |
3844 | | return ZOO.Format.WKT.prototype.write( |
3845 | | new ZOO.Feature(this) |
3846 | | ); |
3847 | | }, |
3848 | | CLASS_NAME: 'ZOO.Geometry' |
3849 | | }); |
3850 | | /** |
3851 | | * Function: OpenLayers.Geometry.fromWKT |
3852 | | * Generate a geometry given a Well-Known Text string. |
3853 | | * |
3854 | | * Parameters: |
3855 | | * wkt - {String} A string representing the geometry in Well-Known Text. |
3856 | | * |
3857 | | * Returns: |
3858 | | * {<ZOO.Geometry>} A geometry of the appropriate class. |
3859 | | */ |
3860 | | ZOO.Geometry.fromWKT = function(wkt) { |
3861 | | var format = arguments.callee.format; |
3862 | | if(!format) { |
3863 | | format = new ZOO.Format.WKT(); |
3864 | | arguments.callee.format = format; |
3865 | | } |
3866 | | var geom; |
3867 | | var result = format.read(wkt); |
3868 | | if(result instanceof ZOO.Feature) { |
3869 | | geom = result.geometry; |
3870 | | } else if(result instanceof Array) { |
3871 | | var len = result.length; |
3872 | | var components = new Array(len); |
3873 | | for(var i=0; i<len; ++i) { |
3874 | | components[i] = result[i].geometry; |
3875 | | } |
3876 | | geom = new ZOO.Geometry.Collection(components); |
3877 | | } |
3878 | | return geom; |
3879 | | }; |
3880 | | ZOO.Geometry.segmentsIntersect = function(seg1, seg2, options) { |
3881 | | var point = options && options.point; |
3882 | | var tolerance = options && options.tolerance; |
3883 | | var intersection = false; |
3884 | | var x11_21 = seg1.x1 - seg2.x1; |
3885 | | var y11_21 = seg1.y1 - seg2.y1; |
3886 | | var x12_11 = seg1.x2 - seg1.x1; |
3887 | | var y12_11 = seg1.y2 - seg1.y1; |
3888 | | var y22_21 = seg2.y2 - seg2.y1; |
3889 | | var x22_21 = seg2.x2 - seg2.x1; |
3890 | | var d = (y22_21 * x12_11) - (x22_21 * y12_11); |
3891 | | var n1 = (x22_21 * y11_21) - (y22_21 * x11_21); |
3892 | | var n2 = (x12_11 * y11_21) - (y12_11 * x11_21); |
3893 | | if(d == 0) { |
3894 | | // parallel |
3895 | | if(n1 == 0 && n2 == 0) { |
3896 | | // coincident |
3897 | | intersection = true; |
3898 | | } |
3899 | | } else { |
3900 | | var along1 = n1 / d; |
3901 | | var along2 = n2 / d; |
3902 | | if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) { |
3903 | | // intersect |
3904 | | if(!point) { |
3905 | | intersection = true; |
3906 | | } else { |
3907 | | // calculate the intersection point |
3908 | | var x = seg1.x1 + (along1 * x12_11); |
3909 | | var y = seg1.y1 + (along1 * y12_11); |
3910 | | intersection = new ZOO.Geometry.Point(x, y); |
3911 | | } |
3912 | | } |
3913 | | } |
3914 | | if(tolerance) { |
3915 | | var dist; |
3916 | | if(intersection) { |
3917 | | if(point) { |
3918 | | var segs = [seg1, seg2]; |
3919 | | var seg, x, y; |
3920 | | // check segment endpoints for proximity to intersection |
3921 | | // set intersection to first endpoint within the tolerance |
3922 | | outer: for(var i=0; i<2; ++i) { |
3923 | | seg = segs[i]; |
3924 | | for(var j=1; j<3; ++j) { |
3925 | | x = seg["x" + j]; |
3926 | | y = seg["y" + j]; |
3927 | | dist = Math.sqrt( |
3928 | | Math.pow(x - intersection.x, 2) + |
3929 | | Math.pow(y - intersection.y, 2) |
3930 | | ); |
3931 | | if(dist < tolerance) { |
3932 | | intersection.x = x; |
3933 | | intersection.y = y; |
3934 | | break outer; |
3935 | | } |
3936 | | } |
3937 | | } |
3938 | | } |
3939 | | } else { |
3940 | | // no calculated intersection, but segments could be within |
3941 | | // the tolerance of one another |
3942 | | var segs = [seg1, seg2]; |
3943 | | var source, target, x, y, p, result; |
3944 | | // check segment endpoints for proximity to intersection |
3945 | | // set intersection to first endpoint within the tolerance |
3946 | | outer: for(var i=0; i<2; ++i) { |
3947 | | source = segs[i]; |
3948 | | target = segs[(i+1)%2]; |
3949 | | for(var j=1; j<3; ++j) { |
3950 | | p = {x: source["x"+j], y: source["y"+j]}; |
3951 | | result = ZOO.Geometry.distanceToSegment(p, target); |
3952 | | if(result.distance < tolerance) { |
3953 | | if(point) { |
3954 | | intersection = new ZOO.Geometry.Point(p.x, p.y); |
3955 | | } else { |
3956 | | intersection = true; |
3957 | | } |
3958 | | break outer; |
3959 | | } |
3960 | | } |
3961 | | } |
3962 | | } |
3963 | | } |
3964 | | return intersection; |
3965 | | }; |
3966 | | ZOO.Geometry.distanceToSegment = function(point, segment) { |
3967 | | var x0 = point.x; |
3968 | | var y0 = point.y; |
3969 | | var x1 = segment.x1; |
3970 | | var y1 = segment.y1; |
3971 | | var x2 = segment.x2; |
3972 | | var y2 = segment.y2; |
3973 | | var dx = x2 - x1; |
3974 | | var dy = y2 - y1; |
3975 | | var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) / |
3976 | | (Math.pow(dx, 2) + Math.pow(dy, 2)); |
3977 | | var x, y; |
3978 | | if(along <= 0.0) { |
3979 | | x = x1; |
3980 | | y = y1; |
3981 | | } else if(along >= 1.0) { |
3982 | | x = x2; |
3983 | | y = y2; |
3984 | | } else { |
3985 | | x = x1 + along * dx; |
3986 | | y = y1 + along * dy; |
3987 | | } |
3988 | | return { |
3989 | | distance: Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)), |
3990 | | x: x, y: y |
3991 | | }; |
3992 | | }; |
3993 | | /** |
3994 | | * Class: OpenLayers.Geometry.Collection |
3995 | | * A Collection is exactly what it sounds like: A collection of different |
3996 | | * Geometries. These are stored in the local parameter <components> (which |
3997 | | * can be passed as a parameter to the constructor). |
3998 | | * |
3999 | | * As new geometries are added to the collection, they are NOT cloned. |
4000 | | * When removing geometries, they need to be specified by reference (ie you |
4001 | | * have to pass in the *exact* geometry to be removed). |
4002 | | * |
4003 | | * The <getArea> and <getLength> functions here merely iterate through |
4004 | | * the components, summing their respective areas and lengths. |
4005 | | * |
4006 | | * Create a new instance with the <ZOO.Geometry.Collection> constructor. |
4007 | | * |
4008 | | * Inerhits from: |
4009 | | * - <ZOO.Geometry> |
4010 | | */ |
4011 | | ZOO.Geometry.Collection = ZOO.Class(ZOO.Geometry, { |
4012 | | /** |
4013 | | * Property: components |
4014 | | * {Array(<ZOO.Geometry>)} The component parts of this geometry |
4015 | | */ |
4016 | | components: null, |
4017 | | /** |
4018 | | * Property: componentTypes |
4019 | | * {Array(String)} An array of class names representing the types of |
4020 | | * components that the collection can include. A null value means the |
4021 | | * component types are not restricted. |
4022 | | */ |
4023 | | componentTypes: null, |
4024 | | /** |
4025 | | * Constructor: ZOO.Geometry.Collection |
4026 | | * Creates a Geometry Collection -- a list of geoms. |
4027 | | * |
4028 | | * Parameters: |
4029 | | * components - {Array(<ZOO.Geometry>)} Optional array of geometries |
4030 | | * |
4031 | | */ |
4032 | | initialize: function (components) { |
4033 | | ZOO.Geometry.prototype.initialize.apply(this, arguments); |
4034 | | this.components = []; |
4035 | | if (components != null) { |
4036 | | this.addComponents(components); |
4037 | | } |
4038 | | }, |
4039 | | /** |
4040 | | * Method: destroy |
4041 | | * Destroy this geometry. |
4042 | | */ |
4043 | | destroy: function () { |
4044 | | this.components.length = 0; |
4045 | | this.components = null; |
4046 | | }, |
4047 | | /** |
4048 | | * Method: clone |
4049 | | * Clone this geometry. |
4050 | | * |
4051 | | * Returns: |
4052 | | * {<ZOO.Geometry.Collection>} An exact clone of this collection |
4053 | | */ |
4054 | | clone: function() { |
4055 | | var geometry = eval("new " + this.CLASS_NAME + "()"); |
4056 | | for(var i=0, len=this.components.length; i<len; i++) { |
4057 | | geometry.addComponent(this.components[i].clone()); |
4058 | | } |
4059 | | return geometry; |
4060 | | }, |
4061 | | /** |
4062 | | * Method: getComponentsString |
4063 | | * Get a string representing the components for this collection |
4064 | | * |
4065 | | * Returns: |
4066 | | * {String} A string representation of the components of this geometry |
4067 | | */ |
4068 | | getComponentsString: function(){ |
4069 | | var strings = []; |
4070 | | for(var i=0, len=this.components.length; i<len; i++) { |
4071 | | strings.push(this.components[i].toShortString()); |
4072 | | } |
4073 | | return strings.join(","); |
4074 | | }, |
4075 | | /** |
4076 | | * Method: calculateBounds |
4077 | | * Recalculate the bounds by iterating through the components and |
4078 | | * calling calling extendBounds() on each item. |
4079 | | */ |
4080 | | calculateBounds: function() { |
4081 | | this.bounds = null; |
4082 | | if ( this.components && this.components.length > 0) { |
4083 | | this.setBounds(this.components[0].getBounds()); |
4084 | | for (var i=1, len=this.components.length; i<len; i++) { |
4085 | | this.extendBounds(this.components[i].getBounds()); |
4086 | | } |
4087 | | } |
4088 | | return this.bounds |
4089 | | }, |
4090 | | /** |
4091 | | * APIMethod: addComponents |
4092 | | * Add components to this geometry. |
4093 | | * |
4094 | | * Parameters: |
4095 | | * components - {Array(<ZOO.Geometry>)} An array of geometries to add |
4096 | | */ |
4097 | | addComponents: function(components){ |
4098 | | if(!(components instanceof Array)) |
4099 | | components = [components]; |
4100 | | for(var i=0, len=components.length; i<len; i++) { |
4101 | | this.addComponent(components[i]); |
4102 | | } |
4103 | | }, |
4104 | | /** |
4105 | | * Method: addComponent |
4106 | | * Add a new component (geometry) to the collection. If this.componentTypes |
4107 | | * is set, then the component class name must be in the componentTypes array. |
4108 | | * |
4109 | | * The bounds cache is reset. |
4110 | | * |
4111 | | * Parameters: |
4112 | | * component - {<ZOO.Geometry>} A geometry to add |
4113 | | * index - {int} Optional index into the array to insert the component |
4114 | | * |
4115 | | * Returns: |
4116 | | * {Boolean} The component geometry was successfully added |
4117 | | */ |
4118 | | addComponent: function(component, index) { |
4119 | | var added = false; |
4120 | | if(component) { |
4121 | | if(this.componentTypes == null || |
4122 | | (ZOO.indexOf(this.componentTypes, |
4123 | | component.CLASS_NAME) > -1)) { |
4124 | | if(index != null && (index < this.components.length)) { |
4125 | | var components1 = this.components.slice(0, index); |
4126 | | var components2 = this.components.slice(index, |
4127 | | this.components.length); |
4128 | | components1.push(component); |
4129 | | this.components = components1.concat(components2); |
4130 | | } else { |
4131 | | this.components.push(component); |
4132 | | } |
4133 | | component.parent = this; |
4134 | | this.clearBounds(); |
4135 | | added = true; |
4136 | | } |
4137 | | } |
4138 | | return added; |
4139 | | }, |
4140 | | /** |
4141 | | * Method: removeComponents |
4142 | | * Remove components from this geometry. |
4143 | | * |
4144 | | * Parameters: |
4145 | | * components - {Array(<ZOO.Geometry>)} The components to be removed |
4146 | | */ |
4147 | | removeComponents: function(components) { |
4148 | | if(!(components instanceof Array)) |
4149 | | components = [components]; |
4150 | | for(var i=components.length-1; i>=0; --i) { |
4151 | | this.removeComponent(components[i]); |
4152 | | } |
4153 | | }, |
4154 | | /** |
4155 | | * Method: removeComponent |
4156 | | * Remove a component from this geometry. |
4157 | | * |
4158 | | * Parameters: |
4159 | | * component - {<ZOO.Geometry>} |
4160 | | */ |
4161 | | removeComponent: function(component) { |
4162 | | ZOO.removeItem(this.components, component); |
4163 | | // clearBounds() so that it gets recalculated on the next call |
4164 | | // to this.getBounds(); |
4165 | | this.clearBounds(); |
4166 | | }, |
4167 | | /** |
4168 | | * Method: getLength |
4169 | | * Calculate the length of this geometry |
4170 | | * |
4171 | | * Returns: |
4172 | | * {Float} The length of the geometry |
4173 | | */ |
4174 | | getLength: function() { |
4175 | | var length = 0.0; |
4176 | | for (var i=0, len=this.components.length; i<len; i++) { |
4177 | | length += this.components[i].getLength(); |
4178 | | } |
4179 | | return length; |
4180 | | }, |
4181 | | /** |
4182 | | * APIMethod: getArea |
4183 | | * Calculate the area of this geometry. Note how this function is |
4184 | | * overridden in <ZOO.Geometry.Polygon>. |
4185 | | * |
4186 | | * Returns: |
4187 | | * {Float} The area of the collection by summing its parts |
4188 | | */ |
4189 | | getArea: function() { |
4190 | | var area = 0.0; |
4191 | | for (var i=0, len=this.components.length; i<len; i++) { |
4192 | | area += this.components[i].getArea(); |
4193 | | } |
4194 | | return area; |
4195 | | }, |
4196 | | /** |
4197 | | * APIMethod: getGeodesicArea |
4198 | | * Calculate the approximate area of the polygon were it projected onto |
4199 | | * the earth. |
4200 | | * |
4201 | | * Parameters: |
4202 | | * projection - {<ZOO.Projection>} The spatial reference system |
4203 | | * for the geometry coordinates. If not provided, Geographic/WGS84 is |
4204 | | * assumed. |
4205 | | * |
4206 | | * Reference: |
4207 | | * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for |
4208 | | * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion |
4209 | | * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 |
4210 | | * |
4211 | | * Returns: |
4212 | | * {float} The approximate geodesic area of the geometry in square meters. |
4213 | | */ |
4214 | | getGeodesicArea: function(projection) { |
4215 | | var area = 0.0; |
4216 | | for(var i=0, len=this.components.length; i<len; i++) { |
4217 | | area += this.components[i].getGeodesicArea(projection); |
4218 | | } |
4219 | | return area; |
4220 | | }, |
4221 | | /** |
4222 | | * Method: getCentroid |
4223 | | * |
4224 | | * Returns: |
4225 | | * {<ZOO.Geometry.Point>} The centroid of the collection |
4226 | | */ |
4227 | | getCentroid: function() { |
4228 | | return this.components.length && this.components[0].getCentroid(); |
4229 | | }, |
4230 | | /** |
4231 | | * Method: getGeodesicLength |
4232 | | * Calculate the approximate length of the geometry were it projected onto |
4233 | | * the earth. |
4234 | | * |
4235 | | * projection - {<ZOO.Projection>} The spatial reference system |
4236 | | * for the geometry coordinates. If not provided, Geographic/WGS84 is |
4237 | | * assumed. |
4238 | | * |
4239 | | * Returns: |
4240 | | * {Float} The appoximate geodesic length of the geometry in meters. |
4241 | | */ |
4242 | | getGeodesicLength: function(projection) { |
4243 | | var length = 0.0; |
4244 | | for(var i=0, len=this.components.length; i<len; i++) { |
4245 | | length += this.components[i].getGeodesicLength(projection); |
4246 | | } |
4247 | | return length; |
4248 | | }, |
4249 | | /** |
4250 | | * Method: move |
4251 | | * Moves a geometry by the given displacement along positive x and y axes. |
4252 | | * This modifies the position of the geometry and clears the cached |
4253 | | * bounds. |
4254 | | * |
4255 | | * Parameters: |
4256 | | * x - {Float} Distance to move geometry in positive x direction. |
4257 | | * y - {Float} Distance to move geometry in positive y direction. |
4258 | | */ |
4259 | | move: function(x, y) { |
4260 | | for(var i=0, len=this.components.length; i<len; i++) { |
4261 | | this.components[i].move(x, y); |
4262 | | } |
4263 | | }, |
4264 | | /** |
4265 | | * Method: rotate |
4266 | | * Rotate a geometry around some origin |
4267 | | * |
4268 | | * Parameters: |
4269 | | * angle - {Float} Rotation angle in degrees (measured counterclockwise |
4270 | | * from the positive x-axis) |
4271 | | * origin - {<ZOO.Geometry.Point>} Center point for the rotation |
4272 | | */ |
4273 | | rotate: function(angle, origin) { |
4274 | | for(var i=0, len=this.components.length; i<len; ++i) { |
4275 | | this.components[i].rotate(angle, origin); |
4276 | | } |
4277 | | }, |
4278 | | /** |
4279 | | * Method: resize |
4280 | | * Resize a geometry relative to some origin. Use this method to apply |
4281 | | * a uniform scaling to a geometry. |
4282 | | * |
4283 | | * Parameters: |
4284 | | * scale - {Float} Factor by which to scale the geometry. A scale of 2 |
4285 | | * doubles the size of the geometry in each dimension |
4286 | | * (lines, for example, will be twice as long, and polygons |
4287 | | * will have four times the area). |
4288 | | * origin - {<ZOO.Geometry.Point>} Point of origin for resizing |
4289 | | * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. |
4290 | | * |
4291 | | * Returns: |
4292 | | * {ZOO.Geometry} - The current geometry. |
4293 | | */ |
4294 | | resize: function(scale, origin, ratio) { |
4295 | | for(var i=0; i<this.components.length; ++i) { |
4296 | | this.components[i].resize(scale, origin, ratio); |
4297 | | } |
4298 | | return this; |
4299 | | }, |
4300 | | distanceTo: function(geometry, options) { |
4301 | | var edge = !(options && options.edge === false); |
4302 | | var details = edge && options && options.details; |
4303 | | var result, best; |
4304 | | var min = Number.POSITIVE_INFINITY; |
4305 | | for(var i=0, len=this.components.length; i<len; ++i) { |
4306 | | result = this.components[i].distanceTo(geometry, options); |
4307 | | distance = details ? result.distance : result; |
4308 | | if(distance < min) { |
4309 | | min = distance; |
4310 | | best = result; |
4311 | | if(min == 0) |
4312 | | break; |
4313 | | } |
4314 | | } |
4315 | | return best; |
4316 | | }, |
4317 | | /** |
4318 | | * Method: equals |
4319 | | * Determine whether another geometry is equivalent to this one. Geometries |
4320 | | * are considered equivalent if all components have the same coordinates. |
4321 | | * |
4322 | | * Parameters: |
4323 | | * geom - {<ZOO.Geometry>} The geometry to test. |
4324 | | * |
4325 | | * Returns: |
4326 | | * {Boolean} The supplied geometry is equivalent to this geometry. |
4327 | | */ |
4328 | | equals: function(geometry) { |
4329 | | var equivalent = true; |
4330 | | if(!geometry || !geometry.CLASS_NAME || |
4331 | | (this.CLASS_NAME != geometry.CLASS_NAME)) |
4332 | | equivalent = false; |
4333 | | else if(!(geometry.components instanceof Array) || |
4334 | | (geometry.components.length != this.components.length)) |
4335 | | equivalent = false; |
4336 | | else |
4337 | | for(var i=0, len=this.components.length; i<len; ++i) { |
4338 | | if(!this.components[i].equals(geometry.components[i])) { |
4339 | | equivalent = false; |
4340 | | break; |
4341 | | } |
4342 | | } |
4343 | | return equivalent; |
4344 | | }, |
4345 | | /** |
4346 | | * Method: transform |
4347 | | * Reproject the components geometry from source to dest. |
4348 | | * |
4349 | | * Parameters: |
4350 | | * source - {<ZOO.Projection>} |
4351 | | * dest - {<ZOO.Projection>} |
4352 | | * |
4353 | | * Returns: |
4354 | | * {<ZOO.Geometry>} |
4355 | | */ |
4356 | | transform: function(source, dest) { |
4357 | | if (source && dest) { |
4358 | | for (var i=0, len=this.components.length; i<len; i++) { |
4359 | | var component = this.components[i]; |
4360 | | component.transform(source, dest); |
4361 | | } |
4362 | | this.bounds = null; |
4363 | | } |
4364 | | return this; |
4365 | | }, |
4366 | | /** |
4367 | | * Method: intersects |
4368 | | * Determine if the input geometry intersects this one. |
4369 | | * |
4370 | | * Parameters: |
4371 | | * geometry - {<ZOO.Geometry>} Any type of geometry. |
4372 | | * |
4373 | | * Returns: |
4374 | | * {Boolean} The input geometry intersects this one. |
4375 | | */ |
4376 | | intersects: function(geometry) { |
4377 | | var intersect = false; |
4378 | | for(var i=0, len=this.components.length; i<len; ++ i) { |
4379 | | intersect = geometry.intersects(this.components[i]); |
4380 | | if(intersect) |
4381 | | break; |
4382 | | } |
4383 | | return intersect; |
4384 | | }, |
4385 | | /** |
4386 | | * Method: getVertices |
4387 | | * Return a list of all points in this geometry. |
4388 | | * |
4389 | | * Parameters: |
4390 | | * nodes - {Boolean} For lines, only return vertices that are |
4391 | | * endpoints. If false, for lines, only vertices that are not |
4392 | | * endpoints will be returned. If not provided, all vertices will |
4393 | | * be returned. |
4394 | | * |
4395 | | * Returns: |
4396 | | * {Array} A list of all vertices in the geometry. |
4397 | | */ |
4398 | | getVertices: function(nodes) { |
4399 | | var vertices = []; |
4400 | | for(var i=0, len=this.components.length; i<len; ++i) { |
4401 | | Array.prototype.push.apply( |
4402 | | vertices, this.components[i].getVertices(nodes) |
4403 | | ); |
4404 | | } |
4405 | | return vertices; |
4406 | | }, |
4407 | | CLASS_NAME: 'ZOO.Geometry.Collection' |
4408 | | }); |
4409 | | /** |
4410 | | * Class: ZOO.Geometry.Point |
4411 | | * Point geometry class. |
4412 | | * |
4413 | | * Inherits from: |
4414 | | * - <ZOO.Geometry> |
4415 | | */ |
4416 | | ZOO.Geometry.Point = ZOO.Class(ZOO.Geometry, { |
4417 | | /** |
4418 | | * Property: x |
4419 | | * {float} |
4420 | | */ |
4421 | | x: null, |
4422 | | /** |
4423 | | * Property: y |
4424 | | * {float} |
4425 | | */ |
4426 | | y: null, |
4427 | | /** |
4428 | | * Constructor: ZOO.Geometry.Point |
4429 | | * Construct a point geometry. |
4430 | | * |
4431 | | * Parameters: |
4432 | | * x - {float} |
4433 | | * y - {float} |
4434 | | * |
4435 | | */ |
4436 | | initialize: function(x, y) { |
4437 | | ZOO.Geometry.prototype.initialize.apply(this, arguments); |
4438 | | this.x = parseFloat(x); |
4439 | | this.y = parseFloat(y); |
4440 | | }, |
4441 | | /** |
4442 | | * Method: clone |
4443 | | * |
4444 | | * Returns: |
4445 | | * {<ZOO.Geometry.Point>} An exact clone of this ZOO.Geometry.Point |
4446 | | */ |
4447 | | clone: function(obj) { |
4448 | | if (obj == null) |
4449 | | obj = new ZOO.Geometry.Point(this.x, this.y); |
4450 | | // catch any randomly tagged-on properties |
4451 | | // ZOO.Util.applyDefaults(obj, this); |
4452 | | return obj; |
4453 | | }, |
4454 | | /** |
4455 | | * Method: calculateBounds |
4456 | | * Create a new Bounds based on the x/y |
4457 | | */ |
4458 | | calculateBounds: function () { |
4459 | | this.bounds = new ZOO.Bounds(this.x, this.y, |
4460 | | this.x, this.y); |
4461 | | }, |
4462 | | distanceTo: function(geometry, options) { |
4463 | | var edge = !(options && options.edge === false); |
4464 | | var details = edge && options && options.details; |
4465 | | var distance, x0, y0, x1, y1, result; |
4466 | | if(geometry instanceof ZOO.Geometry.Point) { |
4467 | | x0 = this.x; |
4468 | | y0 = this.y; |
4469 | | x1 = geometry.x; |
4470 | | y1 = geometry.y; |
4471 | | distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2)); |
4472 | | result = !details ? |
4473 | | distance : {x0: x0, y0: y0, x1: x1, y1: y1, distance: distance}; |
4474 | | } else { |
4475 | | result = geometry.distanceTo(this, options); |
4476 | | if(details) { |
4477 | | // switch coord order since this geom is target |
4478 | | result = { |
4479 | | x0: result.x1, y0: result.y1, |
4480 | | x1: result.x0, y1: result.y0, |
4481 | | distance: result.distance |
4482 | | }; |
4483 | | } |
4484 | | } |
4485 | | return result; |
4486 | | }, |
4487 | | /** |
4488 | | * Method: equals |
4489 | | * Determine whether another geometry is equivalent to this one. Geometries |
4490 | | * are considered equivalent if all components have the same coordinates. |
4491 | | * |
4492 | | * Parameters: |
4493 | | * geom - {<ZOO.Geometry.Point>} The geometry to test. |
4494 | | * |
4495 | | * Returns: |
4496 | | * {Boolean} The supplied geometry is equivalent to this geometry. |
4497 | | */ |
4498 | | equals: function(geom) { |
4499 | | var equals = false; |
4500 | | if (geom != null) |
4501 | | equals = ((this.x == geom.x && this.y == geom.y) || |
4502 | | (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y))); |
4503 | | return equals; |
4504 | | }, |
4505 | | /** |
4506 | | * Method: toShortString |
4507 | | * |
4508 | | * Returns: |
4509 | | * {String} Shortened String representation of Point object. |
4510 | | * (ex. <i>"5, 42"</i>) |
4511 | | */ |
4512 | | toShortString: function() { |
4513 | | return (this.x + ", " + this.y); |
4514 | | }, |
4515 | | /** |
4516 | | * Method: move |
4517 | | * Moves a geometry by the given displacement along positive x and y axes. |
4518 | | * This modifies the position of the geometry and clears the cached |
4519 | | * bounds. |
4520 | | * |
4521 | | * Parameters: |
4522 | | * x - {Float} Distance to move geometry in positive x direction. |
4523 | | * y - {Float} Distance to move geometry in positive y direction. |
4524 | | */ |
4525 | | move: function(x, y) { |
4526 | | this.x = this.x + x; |
4527 | | this.y = this.y + y; |
4528 | | this.clearBounds(); |
4529 | | }, |
4530 | | /** |
4531 | | * Method: rotate |
4532 | | * Rotate a point around another. |
4533 | | * |
4534 | | * Parameters: |
4535 | | * angle - {Float} Rotation angle in degrees (measured counterclockwise |
4536 | | * from the positive x-axis) |
4537 | | * origin - {<ZOO.Geometry.Point>} Center point for the rotation |
4538 | | */ |
4539 | | rotate: function(angle, origin) { |
4540 | | angle *= Math.PI / 180; |
4541 | | var radius = this.distanceTo(origin); |
4542 | | var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x); |
4543 | | this.x = origin.x + (radius * Math.cos(theta)); |
4544 | | this.y = origin.y + (radius * Math.sin(theta)); |
4545 | | this.clearBounds(); |
4546 | | }, |
4547 | | /** |
4548 | | * Method: getCentroid |
4549 | | * |
4550 | | * Returns: |
4551 | | * {<ZOO.Geometry.Point>} The centroid of the collection |
4552 | | */ |
4553 | | getCentroid: function() { |
4554 | | return new ZOO.Geometry.Point(this.x, this.y); |
4555 | | }, |
4556 | | /** |
4557 | | * Method: resize |
4558 | | * Resize a point relative to some origin. For points, this has the effect |
4559 | | * of scaling a vector (from the origin to the point). This method is |
4560 | | * more useful on geometry collection subclasses. |
4561 | | * |
4562 | | * Parameters: |
4563 | | * scale - {Float} Ratio of the new distance from the origin to the old |
4564 | | * distance from the origin. A scale of 2 doubles the |
4565 | | * distance between the point and origin. |
4566 | | * origin - {<ZOO.Geometry.Point>} Point of origin for resizing |
4567 | | * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. |
4568 | | * |
4569 | | * Returns: |
4570 | | * {ZOO.Geometry} - The current geometry. |
4571 | | */ |
4572 | | resize: function(scale, origin, ratio) { |
4573 | | ratio = (ratio == undefined) ? 1 : ratio; |
4574 | | this.x = origin.x + (scale * ratio * (this.x - origin.x)); |
4575 | | this.y = origin.y + (scale * (this.y - origin.y)); |
4576 | | this.clearBounds(); |
4577 | | return this; |
4578 | | }, |
4579 | | /** |
4580 | | * Method: intersects |
4581 | | * Determine if the input geometry intersects this one. |
4582 | | * |
4583 | | * Parameters: |
4584 | | * geometry - {<ZOO.Geometry>} Any type of geometry. |
4585 | | * |
4586 | | * Returns: |
4587 | | * {Boolean} The input geometry intersects this one. |
4588 | | */ |
4589 | | intersects: function(geometry) { |
4590 | | var intersect = false; |
4591 | | if(geometry.CLASS_NAME == "ZOO.Geometry.Point") { |
4592 | | intersect = this.equals(geometry); |
4593 | | } else { |
4594 | | intersect = geometry.intersects(this); |
4595 | | } |
4596 | | return intersect; |
4597 | | }, |
4598 | | /** |
4599 | | * Method: transform |
4600 | | * Translate the x,y properties of the point from source to dest. |
4601 | | * |
4602 | | * Parameters: |
4603 | | * source - {<ZOO.Projection>} |
4604 | | * dest - {<ZOO.Projection>} |
4605 | | * |
4606 | | * Returns: |
4607 | | * {<ZOO.Geometry>} |
4608 | | */ |
4609 | | transform: function(source, dest) { |
4610 | | if ((source && dest)) { |
4611 | | ZOO.Projection.transform( |
4612 | | this, source, dest); |
4613 | | this.bounds = null; |
4614 | | } |
4615 | | return this; |
4616 | | }, |
4617 | | /** |
4618 | | * Method: getVertices |
4619 | | * Return a list of all points in this geometry. |
4620 | | * |
4621 | | * Parameters: |
4622 | | * nodes - {Boolean} For lines, only return vertices that are |
4623 | | * endpoints. If false, for lines, only vertices that are not |
4624 | | * endpoints will be returned. If not provided, all vertices will |
4625 | | * be returned. |
4626 | | * |
4627 | | * Returns: |
4628 | | * {Array} A list of all vertices in the geometry. |
4629 | | */ |
4630 | | getVertices: function(nodes) { |
4631 | | return [this]; |
4632 | | }, |
4633 | | CLASS_NAME: 'ZOO.Geometry.Point' |
4634 | | }); |
4635 | | /** |
4636 | | * Class: ZOO.Geometry.Surface |
4637 | | * Surface geometry class. |
4638 | | * |
4639 | | * Inherits from: |
4640 | | * - <ZOO.Geometry> |
4641 | | */ |
4642 | | ZOO.Geometry.Surface = ZOO.Class(ZOO.Geometry, { |
4643 | | initialize: function() { |
4644 | | ZOO.Geometry.prototype.initialize.apply(this, arguments); |
4645 | | }, |
4646 | | CLASS_NAME: "ZOO.Geometry.Surface" |
4647 | | }); |
4648 | | /** |
4649 | | * Class: ZOO.Geometry.MultiPoint |
4650 | | * MultiPoint is a collection of Points. Create a new instance with the |
4651 | | * <ZOO.Geometry.MultiPoint> constructor. |
4652 | | * |
4653 | | * Inherits from: |
4654 | | * - <ZOO.Geometry.Collection> |
4655 | | */ |
4656 | | ZOO.Geometry.MultiPoint = ZOO.Class( |
4657 | | ZOO.Geometry.Collection, { |
4658 | | /** |
4659 | | * Property: componentTypes |
4660 | | * {Array(String)} An array of class names representing the types of |
4661 | | * components that the collection can include. A null value means the |
4662 | | * component types are not restricted. |
4663 | | */ |
4664 | | componentTypes: ["ZOO.Geometry.Point"], |
4665 | | /** |
4666 | | * Constructor: ZOO.Geometry.MultiPoint |
4667 | | * Create a new MultiPoint Geometry |
4668 | | * |
4669 | | * Parameters: |
4670 | | * components - {Array(<ZOO.Geometry.Point>)} |
4671 | | * |
4672 | | * Returns: |
4673 | | * {<ZOO.Geometry.MultiPoint>} |
4674 | | */ |
4675 | | initialize: function(components) { |
4676 | | ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments); |
4677 | | }, |
4678 | | /** |
4679 | | * Method: addPoint |
4680 | | * Wrapper for <ZOO.Geometry.Collection.addComponent> |
4681 | | * |
4682 | | * Parameters: |
4683 | | * point - {<ZOO.Geometry.Point>} Point to be added |
4684 | | * index - {Integer} Optional index |
4685 | | */ |
4686 | | addPoint: function(point, index) { |
4687 | | this.addComponent(point, index); |
4688 | | }, |
4689 | | /** |
4690 | | * Method: removePoint |
4691 | | * Wrapper for <ZOO.Geometry.Collection.removeComponent> |
4692 | | * |
4693 | | * Parameters: |
4694 | | * point - {<ZOO.Geometry.Point>} Point to be removed |
4695 | | */ |
4696 | | removePoint: function(point){ |
4697 | | this.removeComponent(point); |
4698 | | }, |
4699 | | CLASS_NAME: "ZOO.Geometry.MultiPoint" |
4700 | | }); |
4701 | | /** |
4702 | | * Class: ZOO.Geometry.Curve |
4703 | | * A Curve is a MultiPoint, whose points are assumed to be connected. To |
4704 | | * this end, we provide a "getLength()" function, which iterates through |
4705 | | * the points, summing the distances between them. |
4706 | | * |
4707 | | * Inherits: |
4708 | | * - <ZOO.Geometry.MultiPoint> |
4709 | | */ |
4710 | | ZOO.Geometry.Curve = ZOO.Class(ZOO.Geometry.MultiPoint, { |
4711 | | /** |
4712 | | * Property: componentTypes |
4713 | | * {Array(String)} An array of class names representing the types of |
4714 | | * components that the collection can include. A null |
4715 | | * value means the component types are not restricted. |
4716 | | */ |
4717 | | componentTypes: ["ZOO.Geometry.Point"], |
4718 | | /** |
4719 | | * Constructor: ZOO.Geometry.Curve |
4720 | | * |
4721 | | * Parameters: |
4722 | | * point - {Array(<ZOO.Geometry.Point>)} |
4723 | | */ |
4724 | | initialize: function(points) { |
4725 | | ZOO.Geometry.MultiPoint.prototype.initialize.apply(this,arguments); |
4726 | | }, |
4727 | | /** |
4728 | | * Method: getLength |
4729 | | * |
4730 | | * Returns: |
4731 | | * {Float} The length of the curve |
4732 | | */ |
4733 | | getLength: function() { |
4734 | | var length = 0.0; |
4735 | | if ( this.components && (this.components.length > 1)) { |
4736 | | for(var i=1, len=this.components.length; i<len; i++) { |
4737 | | length += this.components[i-1].distanceTo(this.components[i]); |
4738 | | } |
4739 | | } |
4740 | | return length; |
4741 | | }, |
4742 | | /** |
4743 | | * APIMethod: getGeodesicLength |
4744 | | * Calculate the approximate length of the geometry were it projected onto |
4745 | | * the earth. |
4746 | | * |
4747 | | * projection - {<ZOO.Projection>} The spatial reference system |
4748 | | * for the geometry coordinates. If not provided, Geographic/WGS84 is |
4749 | | * assumed. |
4750 | | * |
4751 | | * Returns: |
4752 | | * {Float} The appoximate geodesic length of the geometry in meters. |
4753 | | */ |
4754 | | getGeodesicLength: function(projection) { |
4755 | | var geom = this; // so we can work with a clone if needed |
4756 | | if(projection) { |
4757 | | var gg = new ZOO.Projection("EPSG:4326"); |
4758 | | if(!gg.equals(projection)) { |
4759 | | geom = this.clone().transform(projection, gg); |
4760 | | } |
4761 | | } |
4762 | | var length = 0.0; |
4763 | | if(geom.components && (geom.components.length > 1)) { |
4764 | | var p1, p2; |
4765 | | for(var i=1, len=geom.components.length; i<len; i++) { |
4766 | | p1 = geom.components[i-1]; |
4767 | | p2 = geom.components[i]; |
4768 | | // this returns km and requires x/y properties |
4769 | | length += ZOO.distVincenty(p1,p2); |
4770 | | } |
4771 | | } |
4772 | | // convert to m |
4773 | | return length * 1000; |
4774 | | }, |
4775 | | CLASS_NAME: "ZOO.Geometry.Curve" |
4776 | | }); |
4777 | | /** |
4778 | | * Class: ZOO.Geometry.LineString |
4779 | | * A LineString is a Curve which, once two points have been added to it, can |
4780 | | * never be less than two points long. |
4781 | | * |
4782 | | * Inherits from: |
4783 | | * - <ZOO.Geometry.Curve> |
4784 | | */ |
4785 | | ZOO.Geometry.LineString = ZOO.Class(ZOO.Geometry.Curve, { |
4786 | | /** |
4787 | | * Constructor: ZOO.Geometry.LineString |
4788 | | * Create a new LineString geometry |
4789 | | * |
4790 | | * Parameters: |
4791 | | * points - {Array(<ZOO.Geometry.Point>)} An array of points used to |
4792 | | * generate the linestring |
4793 | | * |
4794 | | */ |
4795 | | initialize: function(points) { |
4796 | | ZOO.Geometry.Curve.prototype.initialize.apply(this, arguments); |
4797 | | }, |
4798 | | /** |
4799 | | * Method: removeComponent |
4800 | | * Only allows removal of a point if there are three or more points in |
4801 | | * the linestring. (otherwise the result would be just a single point) |
4802 | | * |
4803 | | * Parameters: |
4804 | | * point - {<ZOO.Geometry.Point>} The point to be removed |
4805 | | */ |
4806 | | removeComponent: function(point) { |
4807 | | if ( this.components && (this.components.length > 2)) |
4808 | | ZOO.Geometry.Collection.prototype.removeComponent.apply(this,arguments); |
4809 | | }, |
4810 | | /** |
4811 | | * Method: intersects |
4812 | | * Test for instersection between two geometries. This is a cheapo |
4813 | | * implementation of the Bently-Ottmann algorigithm. It doesn't |
4814 | | * really keep track of a sweep line data structure. It is closer |
4815 | | * to the brute force method, except that segments are sorted and |
4816 | | * potential intersections are only calculated when bounding boxes |
4817 | | * intersect. |
4818 | | * |
4819 | | * Parameters: |
4820 | | * geometry - {<ZOO.Geometry>} |
4821 | | * |
4822 | | * Returns: |
4823 | | * {Boolean} The input geometry intersects this geometry. |
4824 | | */ |
4825 | | intersects: function(geometry) { |
4826 | | var intersect = false; |
4827 | | var type = geometry.CLASS_NAME; |
4828 | | if(type == "ZOO.Geometry.LineString" || |
4829 | | type == "ZOO.Geometry.LinearRing" || |
4830 | | type == "ZOO.Geometry.Point") { |
4831 | | var segs1 = this.getSortedSegments(); |
4832 | | var segs2; |
4833 | | if(type == "ZOO.Geometry.Point") |
4834 | | segs2 = [{ |
4835 | | x1: geometry.x, y1: geometry.y, |
4836 | | x2: geometry.x, y2: geometry.y |
4837 | | }]; |
4838 | | else |
4839 | | segs2 = geometry.getSortedSegments(); |
4840 | | var seg1, seg1x1, seg1x2, seg1y1, seg1y2, |
4841 | | seg2, seg2y1, seg2y2; |
4842 | | // sweep right |
4843 | | outer: for(var i=0, len=segs1.length; i<len; ++i) { |
4844 | | seg1 = segs1[i]; |
4845 | | seg1x1 = seg1.x1; |
4846 | | seg1x2 = seg1.x2; |
4847 | | seg1y1 = seg1.y1; |
4848 | | seg1y2 = seg1.y2; |
4849 | | inner: for(var j=0, jlen=segs2.length; j<jlen; ++j) { |
4850 | | seg2 = segs2[j]; |
4851 | | if(seg2.x1 > seg1x2) |
4852 | | break; |
4853 | | if(seg2.x2 < seg1x1) |
4854 | | continue; |
4855 | | seg2y1 = seg2.y1; |
4856 | | seg2y2 = seg2.y2; |
4857 | | if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) |
4858 | | continue; |
4859 | | if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) |
4860 | | continue; |
4861 | | if(ZOO.Geometry.segmentsIntersect(seg1, seg2)) { |
4862 | | intersect = true; |
4863 | | break outer; |
4864 | | } |
4865 | | } |
4866 | | } |
4867 | | } else { |
4868 | | intersect = geometry.intersects(this); |
4869 | | } |
4870 | | return intersect; |
4871 | | }, |
4872 | | /** |
4873 | | * Method: getSortedSegments |
4874 | | * |
4875 | | * Returns: |
4876 | | * {Array} An array of segment objects. Segment objects have properties |
4877 | | * x1, y1, x2, and y2. The start point is represented by x1 and y1. |
4878 | | * The end point is represented by x2 and y2. Start and end are |
4879 | | * ordered so that x1 < x2. |
4880 | | */ |
4881 | | getSortedSegments: function() { |
4882 | | var numSeg = this.components.length - 1; |
4883 | | var segments = new Array(numSeg); |
4884 | | for(var i=0; i<numSeg; ++i) { |
4885 | | point1 = this.components[i]; |
4886 | | point2 = this.components[i + 1]; |
4887 | | if(point1.x < point2.x) |
4888 | | segments[i] = { |
4889 | | x1: point1.x, |
4890 | | y1: point1.y, |
4891 | | x2: point2.x, |
4892 | | y2: point2.y |
4893 | | }; |
4894 | | else |
4895 | | segments[i] = { |
4896 | | x1: point2.x, |
4897 | | y1: point2.y, |
4898 | | x2: point1.x, |
4899 | | y2: point1.y |
4900 | | }; |
4901 | | } |
4902 | | // more efficient to define this somewhere static |
4903 | | function byX1(seg1, seg2) { |
4904 | | return seg1.x1 - seg2.x1; |
4905 | | } |
4906 | | return segments.sort(byX1); |
4907 | | }, |
4908 | | /** |
4909 | | * Method: splitWithSegment |
4910 | | * Split this geometry with the given segment. |
4911 | | * |
4912 | | * Parameters: |
4913 | | * seg - {Object} An object with x1, y1, x2, and y2 properties referencing |
4914 | | * segment endpoint coordinates. |
4915 | | * options - {Object} Properties of this object will be used to determine |
4916 | | * how the split is conducted. |
4917 | | * |
4918 | | * Valid options: |
4919 | | * edge - {Boolean} Allow splitting when only edges intersect. Default is |
4920 | | * true. If false, a vertex on the source segment must be within the |
4921 | | * tolerance distance of the intersection to be considered a split. |
4922 | | * tolerance - {Number} If a non-null value is provided, intersections |
4923 | | * within the tolerance distance of one of the source segment's |
4924 | | * endpoints will be assumed to occur at the endpoint. |
4925 | | * |
4926 | | * Returns: |
4927 | | * {Object} An object with *lines* and *points* properties. If the given |
4928 | | * segment intersects this linestring, the lines array will reference |
4929 | | * geometries that result from the split. The points array will contain |
4930 | | * all intersection points. Intersection points are sorted along the |
4931 | | * segment (in order from x1,y1 to x2,y2). |
4932 | | */ |
4933 | | splitWithSegment: function(seg, options) { |
4934 | | var edge = !(options && options.edge === false); |
4935 | | var tolerance = options && options.tolerance; |
4936 | | var lines = []; |
4937 | | var verts = this.getVertices(); |
4938 | | var points = []; |
4939 | | var intersections = []; |
4940 | | var split = false; |
4941 | | var vert1, vert2, point; |
4942 | | var node, vertex, target; |
4943 | | var interOptions = {point: true, tolerance: tolerance}; |
4944 | | var result = null; |
4945 | | for(var i=0, stop=verts.length-2; i<=stop; ++i) { |
4946 | | vert1 = verts[i]; |
4947 | | points.push(vert1.clone()); |
4948 | | vert2 = verts[i+1]; |
4949 | | target = {x1: vert1.x, y1: vert1.y, x2: vert2.x, y2: vert2.y}; |
4950 | | point = ZOO.Geometry.segmentsIntersect(seg, target, interOptions); |
4951 | | if(point instanceof ZOO.Geometry.Point) { |
4952 | | if((point.x === seg.x1 && point.y === seg.y1) || |
4953 | | (point.x === seg.x2 && point.y === seg.y2) || |
4954 | | point.equals(vert1) || point.equals(vert2)) |
4955 | | vertex = true; |
4956 | | else |
4957 | | vertex = false; |
4958 | | if(vertex || edge) { |
4959 | | // push intersections different than the previous |
4960 | | if(!point.equals(intersections[intersections.length-1])) |
4961 | | intersections.push(point.clone()); |
4962 | | if(i === 0) { |
4963 | | if(point.equals(vert1)) |
4964 | | continue; |
4965 | | } |
4966 | | if(point.equals(vert2)) |
4967 | | continue; |
4968 | | split = true; |
4969 | | if(!point.equals(vert1)) |
4970 | | points.push(point); |
4971 | | lines.push(new ZOO.Geometry.LineString(points)); |
4972 | | points = [point.clone()]; |
4973 | | } |
4974 | | } |
4975 | | } |
4976 | | if(split) { |
4977 | | points.push(vert2.clone()); |
4978 | | lines.push(new ZOO.Geometry.LineString(points)); |
4979 | | } |
4980 | | if(intersections.length > 0) { |
4981 | | // sort intersections along segment |
4982 | | var xDir = seg.x1 < seg.x2 ? 1 : -1; |
4983 | | var yDir = seg.y1 < seg.y2 ? 1 : -1; |
4984 | | result = { |
4985 | | lines: lines, |
4986 | | points: intersections.sort(function(p1, p2) { |
4987 | | return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y); |
4988 | | }) |
4989 | | }; |
4990 | | } |
4991 | | return result; |
4992 | | }, |
4993 | | /** |
4994 | | * Method: split |
4995 | | * Use this geometry (the source) to attempt to split a target geometry. |
4996 | | * |
4997 | | * Parameters: |
4998 | | * target - {<ZOO.Geometry>} The target geometry. |
4999 | | * options - {Object} Properties of this object will be used to determine |
5000 | | * how the split is conducted. |
5001 | | * |
5002 | | * Valid options: |
5003 | | * mutual - {Boolean} Split the source geometry in addition to the target |
5004 | | * geometry. Default is false. |
5005 | | * edge - {Boolean} Allow splitting when only edges intersect. Default is |
5006 | | * true. If false, a vertex on the source must be within the tolerance |
5007 | | * distance of the intersection to be considered a split. |
5008 | | * tolerance - {Number} If a non-null value is provided, intersections |
5009 | | * within the tolerance distance of an existing vertex on the source |
5010 | | * will be assumed to occur at the vertex. |
5011 | | * |
5012 | | * Returns: |
5013 | | * {Array} A list of geometries (of this same type as the target) that |
5014 | | * result from splitting the target with the source geometry. The |
5015 | | * source and target geometry will remain unmodified. If no split |
5016 | | * results, null will be returned. If mutual is true and a split |
5017 | | * results, return will be an array of two arrays - the first will be |
5018 | | * all geometries that result from splitting the source geometry and |
5019 | | * the second will be all geometries that result from splitting the |
5020 | | * target geometry. |
5021 | | */ |
5022 | | split: function(target, options) { |
5023 | | var results = null; |
5024 | | var mutual = options && options.mutual; |
5025 | | var sourceSplit, targetSplit, sourceParts, targetParts; |
5026 | | if(target instanceof ZOO.Geometry.LineString) { |
5027 | | var verts = this.getVertices(); |
5028 | | var vert1, vert2, seg, splits, lines, point; |
5029 | | var points = []; |
5030 | | sourceParts = []; |
5031 | | for(var i=0, stop=verts.length-2; i<=stop; ++i) { |
5032 | | vert1 = verts[i]; |
5033 | | vert2 = verts[i+1]; |
5034 | | seg = { |
5035 | | x1: vert1.x, y1: vert1.y, |
5036 | | x2: vert2.x, y2: vert2.y |
5037 | | }; |
5038 | | targetParts = targetParts || [target]; |
5039 | | if(mutual) |
5040 | | points.push(vert1.clone()); |
5041 | | for(var j=0; j<targetParts.length; ++j) { |
5042 | | splits = targetParts[j].splitWithSegment(seg, options); |
5043 | | if(splits) { |
5044 | | // splice in new features |
5045 | | lines = splits.lines; |
5046 | | if(lines.length > 0) { |
5047 | | lines.unshift(j, 1); |
5048 | | Array.prototype.splice.apply(targetParts, lines); |
5049 | | j += lines.length - 2; |
5050 | | } |
5051 | | if(mutual) { |
5052 | | for(var k=0, len=splits.points.length; k<len; ++k) { |
5053 | | point = splits.points[k]; |
5054 | | if(!point.equals(vert1)) { |
5055 | | points.push(point); |
5056 | | sourceParts.push(new ZOO.Geometry.LineString(points)); |
5057 | | if(point.equals(vert2)) |
5058 | | points = []; |
5059 | | else |
5060 | | points = [point.clone()]; |
5061 | | } |
5062 | | } |
5063 | | } |
5064 | | } |
5065 | | } |
5066 | | } |
5067 | | if(mutual && sourceParts.length > 0 && points.length > 0) { |
5068 | | points.push(vert2.clone()); |
5069 | | sourceParts.push(new ZOO.Geometry.LineString(points)); |
5070 | | } |
5071 | | } else { |
5072 | | results = target.splitWith(this, options); |
5073 | | } |
5074 | | if(targetParts && targetParts.length > 1) |
5075 | | targetSplit = true; |
5076 | | else |
5077 | | targetParts = []; |
5078 | | if(sourceParts && sourceParts.length > 1) |
5079 | | sourceSplit = true; |
5080 | | else |
5081 | | sourceParts = []; |
5082 | | if(targetSplit || sourceSplit) { |
5083 | | if(mutual) |
5084 | | results = [sourceParts, targetParts]; |
5085 | | else |
5086 | | results = targetParts; |
5087 | | } |
5088 | | return results; |
5089 | | }, |
5090 | | /** |
5091 | | * Method: splitWith |
5092 | | * Split this geometry (the target) with the given geometry (the source). |
5093 | | * |
5094 | | * Parameters: |
5095 | | * geometry - {<ZOO.Geometry>} A geometry used to split this |
5096 | | * geometry (the source). |
5097 | | * options - {Object} Properties of this object will be used to determine |
5098 | | * how the split is conducted. |
5099 | | * |
5100 | | * Valid options: |
5101 | | * mutual - {Boolean} Split the source geometry in addition to the target |
5102 | | * geometry. Default is false. |
5103 | | * edge - {Boolean} Allow splitting when only edges intersect. Default is |
5104 | | * true. If false, a vertex on the source must be within the tolerance |
5105 | | * distance of the intersection to be considered a split. |
5106 | | * tolerance - {Number} If a non-null value is provided, intersections |
5107 | | * within the tolerance distance of an existing vertex on the source |
5108 | | * will be assumed to occur at the vertex. |
5109 | | * |
5110 | | * Returns: |
5111 | | * {Array} A list of geometries (of this same type as the target) that |
5112 | | * result from splitting the target with the source geometry. The |
5113 | | * source and target geometry will remain unmodified. If no split |
5114 | | * results, null will be returned. If mutual is true and a split |
5115 | | * results, return will be an array of two arrays - the first will be |
5116 | | * all geometries that result from splitting the source geometry and |
5117 | | * the second will be all geometries that result from splitting the |
5118 | | * target geometry. |
5119 | | */ |
5120 | | splitWith: function(geometry, options) { |
5121 | | return geometry.split(this, options); |
5122 | | }, |
5123 | | /** |
5124 | | * Method: getVertices |
5125 | | * Return a list of all points in this geometry. |
5126 | | * |
5127 | | * Parameters: |
5128 | | * nodes - {Boolean} For lines, only return vertices that are |
5129 | | * endpoints. If false, for lines, only vertices that are not |
5130 | | * endpoints will be returned. If not provided, all vertices will |
5131 | | * be returned. |
5132 | | * |
5133 | | * Returns: |
5134 | | * {Array} A list of all vertices in the geometry. |
5135 | | */ |
5136 | | getVertices: function(nodes) { |
5137 | | var vertices; |
5138 | | if(nodes === true) |
5139 | | vertices = [ |
5140 | | this.components[0], |
5141 | | this.components[this.components.length-1] |
5142 | | ]; |
5143 | | else if (nodes === false) |
5144 | | vertices = this.components.slice(1, this.components.length-1); |
5145 | | else |
5146 | | vertices = this.components.slice(); |
5147 | | return vertices; |
5148 | | }, |
5149 | | distanceTo: function(geometry, options) { |
5150 | | var edge = !(options && options.edge === false); |
5151 | | var details = edge && options && options.details; |
5152 | | var result, best = {}; |
5153 | | var min = Number.POSITIVE_INFINITY; |
5154 | | if(geometry instanceof ZOO.Geometry.Point) { |
5155 | | var segs = this.getSortedSegments(); |
5156 | | var x = geometry.x; |
5157 | | var y = geometry.y; |
5158 | | var seg; |
5159 | | for(var i=0, len=segs.length; i<len; ++i) { |
5160 | | seg = segs[i]; |
5161 | | result = ZOO.Geometry.distanceToSegment(geometry, seg); |
5162 | | if(result.distance < min) { |
5163 | | min = result.distance; |
5164 | | best = result; |
5165 | | if(min === 0) |
5166 | | break; |
5167 | | } else { |
5168 | | // if distance increases and we cross y0 to the right of x0, no need to keep looking. |
5169 | | if(seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2))) |
5170 | | break; |
5171 | | } |
5172 | | } |
5173 | | if(details) |
5174 | | best = { |
5175 | | distance: best.distance, |
5176 | | x0: best.x, y0: best.y, |
5177 | | x1: x, y1: y |
5178 | | }; |
5179 | | else |
5180 | | best = best.distance; |
5181 | | } else if(geometry instanceof ZOO.Geometry.LineString) { |
5182 | | var segs0 = this.getSortedSegments(); |
5183 | | var segs1 = geometry.getSortedSegments(); |
5184 | | var seg0, seg1, intersection, x0, y0; |
5185 | | var len1 = segs1.length; |
5186 | | var interOptions = {point: true}; |
5187 | | outer: for(var i=0, len=segs0.length; i<len; ++i) { |
5188 | | seg0 = segs0[i]; |
5189 | | x0 = seg0.x1; |
5190 | | y0 = seg0.y1; |
5191 | | for(var j=0; j<len1; ++j) { |
5192 | | seg1 = segs1[j]; |
5193 | | intersection = ZOO.Geometry.segmentsIntersect(seg0, seg1, interOptions); |
5194 | | if(intersection) { |
5195 | | min = 0; |
5196 | | best = { |
5197 | | distance: 0, |
5198 | | x0: intersection.x, y0: intersection.y, |
5199 | | x1: intersection.x, y1: intersection.y |
5200 | | }; |
5201 | | break outer; |
5202 | | } else { |
5203 | | result = ZOO.Geometry.distanceToSegment({x: x0, y: y0}, seg1); |
5204 | | if(result.distance < min) { |
5205 | | min = result.distance; |
5206 | | best = { |
5207 | | distance: min, |
5208 | | x0: x0, y0: y0, |
5209 | | x1: result.x, y1: result.y |
5210 | | }; |
5211 | | } |
5212 | | } |
5213 | | } |
5214 | | } |
5215 | | if(!details) |
5216 | | best = best.distance; |
5217 | | if(min !== 0) { |
5218 | | // check the final vertex in this line's sorted segments |
5219 | | if(seg0) { |
5220 | | result = geometry.distanceTo( |
5221 | | new ZOO.Geometry.Point(seg0.x2, seg0.y2), |
5222 | | options |
5223 | | ); |
5224 | | var dist = details ? result.distance : result; |
5225 | | if(dist < min) { |
5226 | | if(details) |
5227 | | best = { |
5228 | | distance: min, |
5229 | | x0: result.x1, y0: result.y1, |
5230 | | x1: result.x0, y1: result.y0 |
5231 | | }; |
5232 | | else |
5233 | | best = dist; |
5234 | | } |
5235 | | } |
5236 | | } |
5237 | | } else { |
5238 | | best = geometry.distanceTo(this, options); |
5239 | | // swap since target comes from this line |
5240 | | if(details) |
5241 | | best = { |
5242 | | distance: best.distance, |
5243 | | x0: best.x1, y0: best.y1, |
5244 | | x1: best.x0, y1: best.y0 |
5245 | | }; |
5246 | | } |
5247 | | return best; |
5248 | | }, |
5249 | | CLASS_NAME: "ZOO.Geometry.LineString" |
5250 | | }); |
5251 | | /** |
5252 | | * Class: ZOO.Geometry.LinearRing |
5253 | | * |
5254 | | * A Linear Ring is a special LineString which is closed. It closes itself |
5255 | | * automatically on every addPoint/removePoint by adding a copy of the first |
5256 | | * point as the last point. |
5257 | | * |
5258 | | * Also, as it is the first in the line family to close itself, a getArea() |
5259 | | * function is defined to calculate the enclosed area of the linearRing |
5260 | | * |
5261 | | * Inherits: |
5262 | | * - <OpenLayers.Geometry.LineString> |
5263 | | */ |
5264 | | ZOO.Geometry.LinearRing = ZOO.Class( |
5265 | | ZOO.Geometry.LineString, { |
5266 | | /** |
5267 | | * Property: componentTypes |
5268 | | * {Array(String)} An array of class names representing the types of |
5269 | | * components that the collection can include. A null |
5270 | | * value means the component types are not restricted. |
5271 | | */ |
5272 | | componentTypes: ["ZOO.Geometry.Point"], |
5273 | | /** |
5274 | | * Constructor: OpenLayers.Geometry.LinearRing |
5275 | | * Linear rings are constructed with an array of points. This array |
5276 | | * can represent a closed or open ring. If the ring is open (the last |
5277 | | * point does not equal the first point), the constructor will close |
5278 | | * the ring. If the ring is already closed (the last point does equal |
5279 | | * the first point), it will be left closed. |
5280 | | * |
5281 | | * Parameters: |
5282 | | * points - {Array(<ZOO.Geometry.Point>)} points |
5283 | | */ |
5284 | | initialize: function(points) { |
5285 | | ZOO.Geometry.LineString.prototype.initialize.apply(this,arguments); |
5286 | | }, |
5287 | | /** |
5288 | | * Method: addComponent |
5289 | | * Adds a point to geometry components. If the point is to be added to |
5290 | | * the end of the components array and it is the same as the last point |
5291 | | * already in that array, the duplicate point is not added. This has |
5292 | | * the effect of closing the ring if it is not already closed, and |
5293 | | * doing the right thing if it is already closed. This behavior can |
5294 | | * be overridden by calling the method with a non-null index as the |
5295 | | * second argument. |
5296 | | * |
5297 | | * Parameter: |
5298 | | * point - {<ZOO.Geometry.Point>} |
5299 | | * index - {Integer} Index into the array to insert the component |
5300 | | * |
5301 | | * Returns: |
5302 | | * {Boolean} Was the Point successfully added? |
5303 | | */ |
5304 | | addComponent: function(point, index) { |
5305 | | var added = false; |
5306 | | //remove last point |
5307 | | var lastPoint = this.components.pop(); |
5308 | | // given an index, add the point |
5309 | | // without an index only add non-duplicate points |
5310 | | if(index != null || !point.equals(lastPoint)) |
5311 | | added = ZOO.Geometry.Collection.prototype.addComponent.apply(this,arguments); |
5312 | | //append copy of first point |
5313 | | var firstPoint = this.components[0]; |
5314 | | ZOO.Geometry.Collection.prototype.addComponent.apply(this,[firstPoint]); |
5315 | | return added; |
5316 | | }, |
5317 | | /** |
5318 | | * APIMethod: removeComponent |
5319 | | * Removes a point from geometry components. |
5320 | | * |
5321 | | * Parameters: |
5322 | | * point - {<ZOO.Geometry.Point>} |
5323 | | */ |
5324 | | removeComponent: function(point) { |
5325 | | if (this.components.length > 4) { |
5326 | | //remove last point |
5327 | | this.components.pop(); |
5328 | | //remove our point |
5329 | | ZOO.Geometry.Collection.prototype.removeComponent.apply(this,arguments); |
5330 | | //append copy of first point |
5331 | | var firstPoint = this.components[0]; |
5332 | | ZOO.Geometry.Collection.prototype.addComponent.apply(this,[firstPoint]); |
5333 | | } |
5334 | | }, |
5335 | | /** |
5336 | | * Method: move |
5337 | | * Moves a geometry by the given displacement along positive x and y axes. |
5338 | | * This modifies the position of the geometry and clears the cached |
5339 | | * bounds. |
5340 | | * |
5341 | | * Parameters: |
5342 | | * x - {Float} Distance to move geometry in positive x direction. |
5343 | | * y - {Float} Distance to move geometry in positive y direction. |
5344 | | */ |
5345 | | move: function(x, y) { |
5346 | | for(var i = 0, len=this.components.length; i<len - 1; i++) { |
5347 | | this.components[i].move(x, y); |
5348 | | } |
5349 | | }, |
5350 | | /** |
5351 | | * Method: rotate |
5352 | | * Rotate a geometry around some origin |
5353 | | * |
5354 | | * Parameters: |
5355 | | * angle - {Float} Rotation angle in degrees (measured counterclockwise |
5356 | | * from the positive x-axis) |
5357 | | * origin - {<ZOO.Geometry.Point>} Center point for the rotation |
5358 | | */ |
5359 | | rotate: function(angle, origin) { |
5360 | | for(var i=0, len=this.components.length; i<len - 1; ++i) { |
5361 | | this.components[i].rotate(angle, origin); |
5362 | | } |
5363 | | }, |
5364 | | /** |
5365 | | * Method: resize |
5366 | | * Resize a geometry relative to some origin. Use this method to apply |
5367 | | * a uniform scaling to a geometry. |
5368 | | * |
5369 | | * Parameters: |
5370 | | * scale - {Float} Factor by which to scale the geometry. A scale of 2 |
5371 | | * doubles the size of the geometry in each dimension |
5372 | | * (lines, for example, will be twice as long, and polygons |
5373 | | * will have four times the area). |
5374 | | * origin - {<ZOO.Geometry.Point>} Point of origin for resizing |
5375 | | * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. |
5376 | | * |
5377 | | * Returns: |
5378 | | * {ZOO.Geometry} - The current geometry. |
5379 | | */ |
5380 | | resize: function(scale, origin, ratio) { |
5381 | | for(var i=0, len=this.components.length; i<len - 1; ++i) { |
5382 | | this.components[i].resize(scale, origin, ratio); |
5383 | | } |
5384 | | return this; |
5385 | | }, |
5386 | | /** |
5387 | | * Method: transform |
5388 | | * Reproject the components geometry from source to dest. |
5389 | | * |
5390 | | * Parameters: |
5391 | | * source - {<ZOO.Projection>} |
5392 | | * dest - {<ZOO.Projection>} |
5393 | | * |
5394 | | * Returns: |
5395 | | * {<ZOO.Geometry>} |
5396 | | */ |
5397 | | transform: function(source, dest) { |
5398 | | if (source && dest) { |
5399 | | for (var i=0, len=this.components.length; i<len - 1; i++) { |
5400 | | var component = this.components[i]; |
5401 | | component.transform(source, dest); |
5402 | | } |
5403 | | this.bounds = null; |
5404 | | } |
5405 | | return this; |
5406 | | }, |
5407 | | /** |
5408 | | * Method: getCentroid |
5409 | | * |
5410 | | * Returns: |
5411 | | * {<ZOO.Geometry.Point>} The centroid of the ring |
5412 | | */ |
5413 | | getCentroid: function() { |
5414 | | if ( this.components && (this.components.length > 2)) { |
5415 | | var sumX = 0.0; |
5416 | | var sumY = 0.0; |
5417 | | for (var i = 0; i < this.components.length - 1; i++) { |
5418 | | var b = this.components[i]; |
5419 | | var c = this.components[i+1]; |
5420 | | sumX += (b.x + c.x) * (b.x * c.y - c.x * b.y); |
5421 | | sumY += (b.y + c.y) * (b.x * c.y - c.x * b.y); |
5422 | | } |
5423 | | var area = -1 * this.getArea(); |
5424 | | var x = sumX / (6 * area); |
5425 | | var y = sumY / (6 * area); |
5426 | | } |
5427 | | return new ZOO.Geometry.Point(x, y); |
5428 | | }, |
5429 | | /** |
5430 | | * Method: getArea |
5431 | | * Note - The area is positive if the ring is oriented CW, otherwise |
5432 | | * it will be negative. |
5433 | | * |
5434 | | * Returns: |
5435 | | * {Float} The signed area for a ring. |
5436 | | */ |
5437 | | getArea: function() { |
5438 | | var area = 0.0; |
5439 | | if ( this.components && (this.components.length > 2)) { |
5440 | | var sum = 0.0; |
5441 | | for (var i=0, len=this.components.length; i<len - 1; i++) { |
5442 | | var b = this.components[i]; |
5443 | | var c = this.components[i+1]; |
5444 | | sum += (b.x + c.x) * (c.y - b.y); |
5445 | | } |
5446 | | area = - sum / 2.0; |
5447 | | } |
5448 | | return area; |
5449 | | }, |
5450 | | /** |
5451 | | * Method: getGeodesicArea |
5452 | | * Calculate the approximate area of the polygon were it projected onto |
5453 | | * the earth. Note that this area will be positive if ring is oriented |
5454 | | * clockwise, otherwise it will be negative. |
5455 | | * |
5456 | | * Parameters: |
5457 | | * projection - {<ZOO.Projection>} The spatial reference system |
5458 | | * for the geometry coordinates. If not provided, Geographic/WGS84 is |
5459 | | * assumed. |
5460 | | * |
5461 | | * Reference: |
5462 | | * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for |
5463 | | * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion |
5464 | | * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 |
5465 | | * |
5466 | | * Returns: |
5467 | | * {float} The approximate signed geodesic area of the polygon in square |
5468 | | * meters. |
5469 | | */ |
5470 | | getGeodesicArea: function(projection) { |
5471 | | var ring = this; // so we can work with a clone if needed |
5472 | | if(projection) { |
5473 | | var gg = new ZOO.Projection("EPSG:4326"); |
5474 | | if(!gg.equals(projection)) { |
5475 | | ring = this.clone().transform(projection, gg); |
5476 | | } |
5477 | | } |
5478 | | var area = 0.0; |
5479 | | var len = ring.components && ring.components.length; |
5480 | | if(len > 2) { |
5481 | | var p1, p2; |
5482 | | for(var i=0; i<len-1; i++) { |
5483 | | p1 = ring.components[i]; |
5484 | | p2 = ring.components[i+1]; |
5485 | | area += ZOO.rad(p2.x - p1.x) * |
5486 | | (2 + Math.sin(ZOO.rad(p1.y)) + |
5487 | | Math.sin(ZOO.rad(p2.y))); |
5488 | | } |
5489 | | area = area * 6378137.0 * 6378137.0 / 2.0; |
5490 | | } |
5491 | | return area; |
5492 | | }, |
5493 | | /** |
5494 | | * Method: containsPoint |
5495 | | * Test if a point is inside a linear ring. For the case where a point |
5496 | | * is coincident with a linear ring edge, returns 1. Otherwise, |
5497 | | * returns boolean. |
5498 | | * |
5499 | | * Parameters: |
5500 | | * point - {<ZOO.Geometry.Point>} |
5501 | | * |
5502 | | * Returns: |
5503 | | * {Boolean | Number} The point is inside the linear ring. Returns 1 if |
5504 | | * the point is coincident with an edge. Returns boolean otherwise. |
5505 | | */ |
5506 | | containsPoint: function(point) { |
5507 | | var approx = OpenLayers.Number.limitSigDigs; |
5508 | | var digs = 14; |
5509 | | var px = approx(point.x, digs); |
5510 | | var py = approx(point.y, digs); |
5511 | | function getX(y, x1, y1, x2, y2) { |
5512 | | return (((x1 - x2) * y) + ((x2 * y1) - (x1 * y2))) / (y1 - y2); |
5513 | | } |
5514 | | var numSeg = this.components.length - 1; |
5515 | | var start, end, x1, y1, x2, y2, cx, cy; |
5516 | | var crosses = 0; |
5517 | | for(var i=0; i<numSeg; ++i) { |
5518 | | start = this.components[i]; |
5519 | | x1 = approx(start.x, digs); |
5520 | | y1 = approx(start.y, digs); |
5521 | | end = this.components[i + 1]; |
5522 | | x2 = approx(end.x, digs); |
5523 | | y2 = approx(end.y, digs); |
5524 | | |
5525 | | /** |
5526 | | * The following conditions enforce five edge-crossing rules: |
5527 | | * 1. points coincident with edges are considered contained; |
5528 | | * 2. an upward edge includes its starting endpoint, and |
5529 | | * excludes its final endpoint; |
5530 | | * 3. a downward edge excludes its starting endpoint, and |
5531 | | * includes its final endpoint; |
5532 | | * 4. horizontal edges are excluded; and |
5533 | | * 5. the edge-ray intersection point must be strictly right |
5534 | | * of the point P. |
5535 | | */ |
5536 | | if(y1 == y2) { |
5537 | | // horizontal edge |
5538 | | if(py == y1) { |
5539 | | // point on horizontal line |
5540 | | if(x1 <= x2 && (px >= x1 && px <= x2) || // right or vert |
5541 | | x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert |
5542 | | // point on edge |
5543 | | crosses = -1; |
5544 | | break; |
5545 | | } |
5546 | | } |
5547 | | // ignore other horizontal edges |
5548 | | continue; |
5549 | | } |
5550 | | cx = approx(getX(py, x1, y1, x2, y2), digs); |
5551 | | if(cx == px) { |
5552 | | // point on line |
5553 | | if(y1 < y2 && (py >= y1 && py <= y2) || // upward |
5554 | | y1 > y2 && (py <= y1 && py >= y2)) { // downward |
5555 | | // point on edge |
5556 | | crosses = -1; |
5557 | | break; |
5558 | | } |
5559 | | } |
5560 | | if(cx <= px) { |
5561 | | // no crossing to the right |
5562 | | continue; |
5563 | | } |
5564 | | if(x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) { |
5565 | | // no crossing |
5566 | | continue; |
5567 | | } |
5568 | | if(y1 < y2 && (py >= y1 && py < y2) || // upward |
5569 | | y1 > y2 && (py < y1 && py >= y2)) { // downward |
5570 | | ++crosses; |
5571 | | } |
5572 | | } |
5573 | | var contained = (crosses == -1) ? |
5574 | | // on edge |
5575 | | 1 : |
5576 | | // even (out) or odd (in) |
5577 | | !!(crosses & 1); |
5578 | | |
5579 | | return contained; |
5580 | | }, |
5581 | | intersects: function(geometry) { |
5582 | | var intersect = false; |
5583 | | if(geometry.CLASS_NAME == "ZOO.Geometry.Point") |
5584 | | intersect = this.containsPoint(geometry); |
5585 | | else if(geometry.CLASS_NAME == "ZOO.Geometry.LineString") |
5586 | | intersect = geometry.intersects(this); |
5587 | | else if(geometry.CLASS_NAME == "ZOO.Geometry.LinearRing") |
5588 | | intersect = ZOO.Geometry.LineString.prototype.intersects.apply( |
5589 | | this, [geometry] |
5590 | | ); |
5591 | | else |
5592 | | for(var i=0, len=geometry.components.length; i<len; ++ i) { |
5593 | | intersect = geometry.components[i].intersects(this); |
5594 | | if(intersect) |
5595 | | break; |
5596 | | } |
5597 | | return intersect; |
5598 | | }, |
5599 | | getVertices: function(nodes) { |
5600 | | return (nodes === true) ? [] : this.components.slice(0, this.components.length-1); |
5601 | | }, |
5602 | | CLASS_NAME: "ZOO.Geometry.LinearRing" |
5603 | | }); |
5604 | | /** |
5605 | | * Class: ZOO.Geometry.MultiLineString |
5606 | | * A MultiLineString is a geometry with multiple <ZOO.Geometry.LineString> |
5607 | | * components. |
5608 | | * |
5609 | | * Inherits from: |
5610 | | * - <ZOO.Geometry.Collection> |
5611 | | */ |
5612 | | ZOO.Geometry.MultiLineString = ZOO.Class( |
5613 | | ZOO.Geometry.Collection, { |
5614 | | componentTypes: ["ZOO.Geometry.LineString"], |
5615 | | /** |
5616 | | * Constructor: ZOO.Geometry.MultiLineString |
5617 | | * Constructor for a MultiLineString Geometry. |
5618 | | * |
5619 | | * Parameters: |
5620 | | * components - {Array(<ZOO.Geometry.LineString>)} |
5621 | | * |
5622 | | */ |
5623 | | initialize: function(components) { |
5624 | | ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments); |
5625 | | }, |
5626 | | split: function(geometry, options) { |
5627 | | var results = null; |
5628 | | var mutual = options && options.mutual; |
5629 | | var splits, sourceLine, sourceLines, sourceSplit, targetSplit; |
5630 | | var sourceParts = []; |
5631 | | var targetParts = [geometry]; |
5632 | | for(var i=0, len=this.components.length; i<len; ++i) { |
5633 | | sourceLine = this.components[i]; |
5634 | | sourceSplit = false; |
5635 | | for(var j=0; j < targetParts.length; ++j) { |
5636 | | splits = sourceLine.split(targetParts[j], options); |
5637 | | if(splits) { |
5638 | | if(mutual) { |
5639 | | sourceLines = splits[0]; |
5640 | | for(var k=0, klen=sourceLines.length; k<klen; ++k) { |
5641 | | if(k===0 && sourceParts.length) |
5642 | | sourceParts[sourceParts.length-1].addComponent( |
5643 | | sourceLines[k] |
5644 | | ); |
5645 | | else |
5646 | | sourceParts.push( |
5647 | | new ZOO.Geometry.MultiLineString([ |
5648 | | sourceLines[k] |
5649 | | ]) |
5650 | | ); |
5651 | | } |
5652 | | sourceSplit = true; |
5653 | | splits = splits[1]; |
5654 | | } |
5655 | | if(splits.length) { |
5656 | | // splice in new target parts |
5657 | | splits.unshift(j, 1); |
5658 | | Array.prototype.splice.apply(targetParts, splits); |
5659 | | break; |
5660 | | } |
5661 | | } |
5662 | | } |
5663 | | if(!sourceSplit) { |
5664 | | // source line was not hit |
5665 | | if(sourceParts.length) { |
5666 | | // add line to existing multi |
5667 | | sourceParts[sourceParts.length-1].addComponent( |
5668 | | sourceLine.clone() |
5669 | | ); |
5670 | | } else { |
5671 | | // create a fresh multi |
5672 | | sourceParts = [ |
5673 | | new ZOO.Geometry.MultiLineString( |
5674 | | sourceLine.clone() |
5675 | | ) |
5676 | | ]; |
5677 | | } |
5678 | | } |
5679 | | } |
5680 | | if(sourceParts && sourceParts.length > 1) |
5681 | | sourceSplit = true; |
5682 | | else |
5683 | | sourceParts = []; |
5684 | | if(targetParts && targetParts.length > 1) |
5685 | | targetSplit = true; |
5686 | | else |
5687 | | targetParts = []; |
5688 | | if(sourceSplit || targetSplit) { |
5689 | | if(mutual) |
5690 | | results = [sourceParts, targetParts]; |
5691 | | else |
5692 | | results = targetParts; |
5693 | | } |
5694 | | return results; |
5695 | | }, |
5696 | | splitWith: function(geometry, options) { |
5697 | | var results = null; |
5698 | | var mutual = options && options.mutual; |
5699 | | var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts; |
5700 | | if(geometry instanceof ZOO.Geometry.LineString) { |
5701 | | targetParts = []; |
5702 | | sourceParts = [geometry]; |
5703 | | for(var i=0, len=this.components.length; i<len; ++i) { |
5704 | | targetSplit = false; |
5705 | | targetLine = this.components[i]; |
5706 | | for(var j=0; j<sourceParts.length; ++j) { |
5707 | | splits = sourceParts[j].split(targetLine, options); |
5708 | | if(splits) { |
5709 | | if(mutual) { |
5710 | | sourceLines = splits[0]; |
5711 | | if(sourceLines.length) { |
5712 | | // splice in new source parts |
5713 | | sourceLines.unshift(j, 1); |
5714 | | Array.prototype.splice.apply(sourceParts, sourceLines); |
5715 | | j += sourceLines.length - 2; |
5716 | | } |
5717 | | splits = splits[1]; |
5718 | | if(splits.length === 0) { |
5719 | | splits = [targetLine.clone()]; |
5720 | | } |
5721 | | } |
5722 | | for(var k=0, klen=splits.length; k<klen; ++k) { |
5723 | | if(k===0 && targetParts.length) { |
5724 | | targetParts[targetParts.length-1].addComponent( |
5725 | | splits[k] |
5726 | | ); |
5727 | | } else { |
5728 | | targetParts.push( |
5729 | | new ZOO.Geometry.MultiLineString([ |
5730 | | splits[k] |
5731 | | ]) |
5732 | | ); |
5733 | | } |
5734 | | } |
5735 | | targetSplit = true; |
5736 | | } |
5737 | | } |
5738 | | if(!targetSplit) { |
5739 | | // target component was not hit |
5740 | | if(targetParts.length) { |
5741 | | // add it to any existing multi-line |
5742 | | targetParts[targetParts.length-1].addComponent( |
5743 | | targetLine.clone() |
5744 | | ); |
5745 | | } else { |
5746 | | // or start with a fresh multi-line |
5747 | | targetParts = [ |
5748 | | new ZOO.Geometry.MultiLineString([ |
5749 | | targetLine.clone() |
5750 | | ]) |
5751 | | ]; |
5752 | | } |
5753 | | |
5754 | | } |
5755 | | } |
5756 | | } else { |
5757 | | results = geometry.split(this); |
5758 | | } |
5759 | | if(sourceParts && sourceParts.length > 1) |
5760 | | sourceSplit = true; |
5761 | | else |
5762 | | sourceParts = []; |
5763 | | if(targetParts && targetParts.length > 1) |
5764 | | targetSplit = true; |
5765 | | else |
5766 | | targetParts = []; |
5767 | | if(sourceSplit || targetSplit) { |
5768 | | if(mutual) |
5769 | | results = [sourceParts, targetParts]; |
5770 | | else |
5771 | | results = targetParts; |
5772 | | } |
5773 | | return results; |
5774 | | }, |
5775 | | CLASS_NAME: "ZOO.Geometry.MultiLineString" |
5776 | | }); |
5777 | | /** |
5778 | | * Class: ZOO.Geometry.Polygon |
5779 | | * Polygon is a collection of <ZOO.Geometry.LinearRing>. |
5780 | | * |
5781 | | * Inherits from: |
5782 | | * - <ZOO.Geometry.Collection> |
5783 | | */ |
5784 | | ZOO.Geometry.Polygon = ZOO.Class( |
5785 | | ZOO.Geometry.Collection, { |
5786 | | componentTypes: ["ZOO.Geometry.LinearRing"], |
5787 | | /** |
5788 | | * Constructor: OpenLayers.Geometry.Polygon |
5789 | | * Constructor for a Polygon geometry. |
5790 | | * The first ring (this.component[0])is the outer bounds of the polygon and |
5791 | | * all subsequent rings (this.component[1-n]) are internal holes. |
5792 | | * |
5793 | | * |
5794 | | * Parameters: |
5795 | | * components - {Array(<ZOO.Geometry.LinearRing>)} |
5796 | | */ |
5797 | | initialize: function(components) { |
5798 | | ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments); |
5799 | | }, |
5800 | | /** |
5801 | | * Method: getArea |
5802 | | * Calculated by subtracting the areas of the internal holes from the |
5803 | | * area of the outer hole. |
5804 | | * |
5805 | | * Returns: |
5806 | | * {float} The area of the geometry |
5807 | | */ |
5808 | | getArea: function() { |
5809 | | var area = 0.0; |
5810 | | if ( this.components && (this.components.length > 0)) { |
5811 | | area += Math.abs(this.components[0].getArea()); |
5812 | | for (var i=1, len=this.components.length; i<len; i++) { |
5813 | | area -= Math.abs(this.components[i].getArea()); |
5814 | | } |
5815 | | } |
5816 | | return area; |
5817 | | }, |
5818 | | /** |
5819 | | * APIMethod: getGeodesicArea |
5820 | | * Calculate the approximate area of the polygon were it projected onto |
5821 | | * the earth. |
5822 | | * |
5823 | | * Parameters: |
5824 | | * projection - {<ZOO.Projection>} The spatial reference system |
5825 | | * for the geometry coordinates. If not provided, Geographic/WGS84 is |
5826 | | * assumed. |
5827 | | * |
5828 | | * Reference: |
5829 | | * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for |
5830 | | * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion |
5831 | | * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 |
5832 | | * |
5833 | | * Returns: |
5834 | | * {float} The approximate geodesic area of the polygon in square meters. |
5835 | | */ |
5836 | | getGeodesicArea: function(projection) { |
5837 | | var area = 0.0; |
5838 | | if(this.components && (this.components.length > 0)) { |
5839 | | area += Math.abs(this.components[0].getGeodesicArea(projection)); |
5840 | | for(var i=1, len=this.components.length; i<len; i++) { |
5841 | | area -= Math.abs(this.components[i].getGeodesicArea(projection)); |
5842 | | } |
5843 | | } |
5844 | | return area; |
5845 | | }, |
5846 | | /** |
5847 | | * Method: containsPoint |
5848 | | * Test if a point is inside a polygon. Points on a polygon edge are |
5849 | | * considered inside. |
5850 | | * |
5851 | | * Parameters: |
5852 | | * point - {<ZOO.Geometry.Point>} |
5853 | | * |
5854 | | * Returns: |
5855 | | * {Boolean | Number} The point is inside the polygon. Returns 1 if the |
5856 | | * point is on an edge. Returns boolean otherwise. |
5857 | | */ |
5858 | | containsPoint: function(point) { |
5859 | | var numRings = this.components.length; |
5860 | | var contained = false; |
5861 | | if(numRings > 0) { |
5862 | | // check exterior ring - 1 means on edge, boolean otherwise |
5863 | | contained = this.components[0].containsPoint(point); |
5864 | | if(contained !== 1) { |
5865 | | if(contained && numRings > 1) { |
5866 | | // check interior rings |
5867 | | var hole; |
5868 | | for(var i=1; i<numRings; ++i) { |
5869 | | hole = this.components[i].containsPoint(point); |
5870 | | if(hole) { |
5871 | | if(hole === 1) |
5872 | | contained = 1; |
5873 | | else |
5874 | | contained = false; |
5875 | | break; |
5876 | | } |
5877 | | } |
5878 | | } |
5879 | | } |
5880 | | } |
5881 | | return contained; |
5882 | | }, |
5883 | | intersects: function(geometry) { |
5884 | | var intersect = false; |
5885 | | var i, len; |
5886 | | if(geometry.CLASS_NAME == "ZOO.Geometry.Point") { |
5887 | | intersect = this.containsPoint(geometry); |
5888 | | } else if(geometry.CLASS_NAME == "ZOO.Geometry.LineString" || |
5889 | | geometry.CLASS_NAME == "ZOO.Geometry.LinearRing") { |
5890 | | // check if rings/linestrings intersect |
5891 | | for(i=0, len=this.components.length; i<len; ++i) { |
5892 | | intersect = geometry.intersects(this.components[i]); |
5893 | | if(intersect) { |
5894 | | break; |
5895 | | } |
5896 | | } |
5897 | | if(!intersect) { |
5898 | | // check if this poly contains points of the ring/linestring |
5899 | | for(i=0, len=geometry.components.length; i<len; ++i) { |
5900 | | intersect = this.containsPoint(geometry.components[i]); |
5901 | | if(intersect) { |
5902 | | break; |
5903 | | } |
5904 | | } |
5905 | | } |
5906 | | } else { |
5907 | | for(i=0, len=geometry.components.length; i<len; ++ i) { |
5908 | | intersect = this.intersects(geometry.components[i]); |
5909 | | if(intersect) |
5910 | | break; |
5911 | | } |
5912 | | } |
5913 | | // check case where this poly is wholly contained by another |
5914 | | if(!intersect && geometry.CLASS_NAME == "ZOO.Geometry.Polygon") { |
5915 | | // exterior ring points will be contained in the other geometry |
5916 | | var ring = this.components[0]; |
5917 | | for(i=0, len=ring.components.length; i<len; ++i) { |
5918 | | intersect = geometry.containsPoint(ring.components[i]); |
5919 | | if(intersect) |
5920 | | break; |
5921 | | } |
5922 | | } |
5923 | | return intersect; |
5924 | | }, |
5925 | | distanceTo: function(geometry, options) { |
5926 | | var edge = !(options && options.edge === false); |
5927 | | var result; |
5928 | | // this is the case where we might not be looking for distance to edge |
5929 | | if(!edge && this.intersects(geometry)) |
5930 | | result = 0; |
5931 | | else |
5932 | | result = ZOO.Geometry.Collection.prototype.distanceTo.apply( |
5933 | | this, [geometry, options] |
5934 | | ); |
5935 | | return result; |
5936 | | }, |
5937 | | CLASS_NAME: "ZOO.Geometry.Polygon" |
5938 | | }); |
5939 | | /** |
5940 | | * Method: createRegularPolygon |
5941 | | * Create a regular polygon around a radius. Useful for creating circles |
5942 | | * and the like. |
5943 | | * |
5944 | | * Parameters: |
5945 | | * origin - {<ZOO.Geometry.Point>} center of polygon. |
5946 | | * radius - {Float} distance to vertex, in map units. |
5947 | | * sides - {Integer} Number of sides. 20 approximates a circle. |
5948 | | * rotation - {Float} original angle of rotation, in degrees. |
5949 | | */ |
5950 | | OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) { |
5951 | | var angle = Math.PI * ((1/sides) - (1/2)); |
5952 | | if(rotation) { |
5953 | | angle += (rotation / 180) * Math.PI; |
5954 | | } |
5955 | | var rotatedAngle, x, y; |
5956 | | var points = []; |
5957 | | for(var i=0; i<sides; ++i) { |
5958 | | rotatedAngle = angle + (i * 2 * Math.PI / sides); |
5959 | | x = origin.x + (radius * Math.cos(rotatedAngle)); |
5960 | | y = origin.y + (radius * Math.sin(rotatedAngle)); |
5961 | | points.push(new ZOO.Geometry.Point(x, y)); |
5962 | | } |
5963 | | var ring = new ZOO.Geometry.LinearRing(points); |
5964 | | return new ZOO.Geometry.Polygon([ring]); |
5965 | | }; |
5966 | | /** |
5967 | | * Class: ZOO.Geometry.MultiPolygon |
5968 | | * MultiPolygon is a geometry with multiple <ZOO.Geometry.Polygon> |
5969 | | * components. Create a new instance with the <ZOO.Geometry.MultiPolygon> |
5970 | | * constructor. |
5971 | | * |
5972 | | * Inherits from: |
5973 | | * - <ZOO.Geometry.Collection> |
5974 | | */ |
5975 | | ZOO.Geometry.MultiPolygon = ZOO.Class( |
5976 | | ZOO.Geometry.Collection, { |
5977 | | componentTypes: ["ZOO.Geometry.Polygon"], |
5978 | | /** |
5979 | | * Constructor: OpenLayers.Geometry.MultiPolygon |
5980 | | * Create a new MultiPolygon geometry |
5981 | | * |
5982 | | * Parameters: |
5983 | | * components - {Array(<ZOO.Geometry.Polygon>)} An array of polygons |
5984 | | * used to generate the MultiPolygon |
5985 | | * |
5986 | | */ |
5987 | | initialize: function(components) { |
5988 | | ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments); |
5989 | | }, |
5990 | | CLASS_NAME: "ZOO.Geometry.MultiPolygon" |
5991 | | }); |
5992 | | |
5993 | | ZOO.Process = ZOO.Class({ |
5994 | | schemaLocation: "http://www.opengis.net/wps/1.0.0/../wpsExecute_request.xsd", |
5995 | | namespaces: { |
5996 | | ows: "http://www.opengis.net/ows/1.1", |
5997 | | wps: "http://www.opengis.net/wps/1.0.0", |
5998 | | xlink: "http://www.w3.org/1999/xlink", |
5999 | | xsi: "http://www.w3.org/2001/XMLSchema-instance", |
6000 | | }, |
6001 | | url: 'http://localhost/zoo', |
6002 | | identifier: null, |
6003 | | initialize: function(url,identifier) { |
6004 | | this.url = url; |
6005 | | this.identifier = identifier; |
6006 | | }, |
6007 | | Execute: function(inputs) { |
6008 | | if (this.identifier == null) |
6009 | | return null; |
6010 | | var body = new XML('<wps:Execute service="WPS" version="1.0.0" xmlns:wps="'+this.namespaces['wps']+'" xmlns:ows="'+this.namespaces['ows']+'" xmlns:xlink="'+this.namespaces['xlink']+'" xmlns:xsi="'+this.namespaces['xsi']+'" xsi:schemaLocation="'+this.schemaLocation+'"><ows:Identifier>'+this.identifier+'</ows:Identifier>'+this.buildDataInputsNode(inputs)+'</wps:Execute>'); |
6011 | | body = body.toXMLString(); |
6012 | | var response = ZOO.Request.Post(this.url,body,['Content-Type: text/xml; charset=UTF-8']); |
6013 | | return response; |
6014 | | }, |
6015 | | buildInput: { |
6016 | | 'complex': function(identifier,data) { |
6017 | | var input = new XML('<wps:Input xmlns:wps="'+this.namespaces['wps']+'"><ows:Identifier xmlns:ows="'+this.namespaces['ows']+'">'+identifier+'</ows:Identifier><wps:Data><wps:ComplexData>'+data.value+'</wps:ComplexData></wps:Data></wps:Input>'); |
6018 | | input.*::Data.*::ComplexData.@mimeType = data.mimetype ? data.mimetype : 'text/plain'; |
6019 | | if (data.encoding) |
6020 | | input.*::Data.*::ComplexData.@encoding = data.encoding; |
6021 | | if (data.schema) |
6022 | | input.*::Data.*::ComplexData.@schema = data.schema; |
6023 | | input = input.toXMLString(); |
6024 | | return input; |
6025 | | }, |
6026 | | 'reference': function(identifier,data) { |
6027 | | return '<wps:Input xmlns:wps="'+this.namespaces['wps']+'"><ows:Identifier xmlns:ows="'+this.namespaces['ows']+'">'+identifier+'</ows:Identifier><wps:Reference xmlns:xlink="'+this.namespaces['xlink']+'" xlink:href="'+data.value.replace('&','&','gi')+'"/></wps:Input>'; |
6028 | | }, |
6029 | | 'literal': function(identifier,data) { |
6030 | | var input = new XML('<wps:Input xmlns:wps="'+this.namespaces['wps']+'"><ows:Identifier xmlns:ows="'+this.namespaces['ows']+'">'+identifier+'</ows:Identifier><wps:Data><wps:LiteralData>'+data.value+'</wps:LiteralData></wps:Data></wps:Input>'); |
6031 | | if (data.type) |
6032 | | input.*::Data.*::LiteralData.@dataType = data.type; |
6033 | | if (data.uom) |
6034 | | input.*::Data.*::LiteralData.@uom = data.uom; |
6035 | | input = input.toXMLString(); |
6036 | | return input; |
6037 | | } |
6038 | | }, |
6039 | | buildDataInputsNode:function(inputs){ |
6040 | | var data, builder, inputsArray=[]; |
6041 | | for (var attr in inputs) { |
6042 | | data = inputs[attr]; |
6043 | | if (data.mimetype || data.type == 'complex') |
6044 | | builder = this.buildInput['complex']; |
6045 | | else if (data.type == 'reference' || data.type == 'url') |
6046 | | builder = this.buildInput['reference']; |
6047 | | else |
6048 | | builder = this.buildInput['literal']; |
6049 | | inputsArray.push(builder.apply(this,[attr,data])); |
6050 | | } |
6051 | | return '<wps:DataInputs xmlns:wps="'+this.namespaces['wps']+'">'+inputsArray.join('\n')+'</wps:DataInputs>'; |
6052 | | }, |
6053 | | CLASS_NAME: "ZOO.Process" |
6054 | | }); |
| 100 | loadProjCodeSuccess:function(projName){if(Proj4js.Proj[projName].dependsOn){this.loadProjCode(Proj4js.Proj[projName].dependsOn);}else{this.initTransforms();}},loadProjCodeFailure:function(projName){Proj4js.reportError("failed to find projection file for: "+projName);},checkCodeLoaded:function(projName){if(Proj4js.Proj[projName]){return true;}else{return false;}},initTransforms:function(){Proj4js.extend(this,Proj4js.Proj[this.projName]);this.init();this.readyToUse=true;},parseDefs:function(){this.defData=Proj4js.defs[this.srsCode];var paramName,paramVal;if(!this.defData){return;} |
| 101 | var paramArray=this.defData.split("+");for(var prop=0;prop<paramArray.length;prop++){var property=paramArray[prop].split("=");paramName=property[0].toLowerCase();paramVal=property[1];switch(paramName.replace(/\s/gi,"")){case"":break;case"title":this.title=paramVal;break;case"proj":this.projName=paramVal.replace(/\s/gi,"");break;case"units":this.units=paramVal.replace(/\s/gi,"");break;case"datum":this.datumCode=paramVal.replace(/\s/gi,"");break;case"nadgrids":this.nagrids=paramVal.replace(/\s/gi,"");break;case"ellps":this.ellps=paramVal.replace(/\s/gi,"");break;case"a":this.a=parseFloat(paramVal);break;case"b":this.b=parseFloat(paramVal);break;case"rf":this.rf=parseFloat(paramVal);break;case"lat_0":this.lat0=paramVal*Proj4js.common.D2R;break;case"lat_1":this.lat1=paramVal*Proj4js.common.D2R;break;case"lat_2":this.lat2=paramVal*Proj4js.common.D2R;break;case"lat_ts":this.lat_ts=paramVal*Proj4js.common.D2R;break;case"lon_0":this.long0=paramVal*Proj4js.common.D2R;break;case"alpha":this.alpha=parseFloat(paramVal)*Proj4js.common.D2R;break;case"lonc":this.longc=paramVal*Proj4js.common.D2R;break;case"x_0":this.x0=parseFloat(paramVal);break;case"y_0":this.y0=parseFloat(paramVal);break;case"k_0":this.k0=parseFloat(paramVal);break;case"k":this.k0=parseFloat(paramVal);break;case"r_a":this.R_A=true;break;case"zone":this.zone=parseInt(paramVal);break;case"south":this.utmSouth=true;break;case"towgs84":this.datum_params=paramVal.split(",");break;case"to_meter":this.to_meter=parseFloat(paramVal);break;case"from_greenwich":this.from_greenwich=paramVal*Proj4js.common.D2R;break;case"pm":paramVal=paramVal.replace(/\s/gi,"");this.from_greenwich=Proj4js.PrimeMeridian[paramVal]?Proj4js.PrimeMeridian[paramVal]:parseFloat(paramVal);this.from_greenwich*=Proj4js.common.D2R;break;case"no_defs":break;default:}} |
| 102 | this.deriveConstants();},deriveConstants:function(){if(this.nagrids=='@null')this.datumCode='none';if(this.datumCode&&this.datumCode!='none'){var datumDef=Proj4js.Datum[this.datumCode];if(datumDef){this.datum_params=datumDef.towgs84?datumDef.towgs84.split(','):null;this.ellps=datumDef.ellipse;this.datumName=datumDef.datumName?datumDef.datumName:this.datumCode;}} |
| 103 | if(!this.a){var ellipse=Proj4js.Ellipsoid[this.ellps]?Proj4js.Ellipsoid[this.ellps]:Proj4js.Ellipsoid['WGS84'];Proj4js.extend(this,ellipse);} |
| 104 | if(this.rf&&!this.b)this.b=(1.0-1.0/this.rf)*this.a;if(Math.abs(this.a-this.b)<Proj4js.common.EPSLN){this.sphere=true;this.b=this.a;} |
| 105 | this.a2=this.a*this.a;this.b2=this.b*this.b;this.es=(this.a2-this.b2)/this.a2;this.e=Math.sqrt(this.es);if(this.R_A){this.a*=1.-this.es*(Proj4js.common.SIXTH+this.es*(Proj4js.common.RA4+this.es*Proj4js.common.RA6));this.a2=this.a*this.a;this.b2=this.b*this.b;this.es=0.;} |
| 106 | this.ep2=(this.a2-this.b2)/this.b2;if(!this.k0)this.k0=1.0;this.datum=new Proj4js.datum(this);}});Proj4js.Proj.longlat={init:function(){},forward:function(pt){return pt;},inverse:function(pt){return pt;}};Proj4js.defs={'WGS84':"+title=long/lat:WGS84 +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees",'EPSG:4326':"+title=long/lat:WGS84 +proj=longlat +a=6378137.0 +b=6356752.31424518 +ellps=WGS84 +datum=WGS84 +units=degrees",'EPSG:4269':"+title=long/lat:NAD83 +proj=longlat +a=6378137.0 +b=6356752.31414036 +ellps=GRS80 +datum=NAD83 +units=degrees",'EPSG:3785':"+title= Google Mercator +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs"};Proj4js.defs['GOOGLE']=Proj4js.defs['EPSG:3785'];Proj4js.defs['EPSG:900913']=Proj4js.defs['EPSG:3785'];Proj4js.defs['EPSG:102113']=Proj4js.defs['EPSG:3785'];Proj4js.common={PI:3.141592653589793238,HALF_PI:1.570796326794896619,TWO_PI:6.283185307179586477,FORTPI:0.78539816339744833,R2D:57.29577951308232088,D2R:0.01745329251994329577,SEC_TO_RAD:4.84813681109535993589914102357e-6,EPSLN:1.0e-10,MAX_ITER:20,COS_67P5:0.38268343236508977,AD_C:1.0026000,PJD_UNKNOWN:0,PJD_3PARAM:1,PJD_7PARAM:2,PJD_GRIDSHIFT:3,PJD_WGS84:4,PJD_NODATUM:5,SRS_WGS84_SEMIMAJOR:6378137.0,SIXTH:.1666666666666666667,RA4:.04722222222222222222,RA6:.02215608465608465608,RV4:.06944444444444444444,RV6:.04243827160493827160,msfnz:function(eccent,sinphi,cosphi){var con=eccent*sinphi;return cosphi/(Math.sqrt(1.0-con*con));},tsfnz:function(eccent,phi,sinphi){var con=eccent*sinphi;var com=.5*eccent;con=Math.pow(((1.0-con)/(1.0+con)),com);return(Math.tan(.5*(this.HALF_PI-phi))/con);},phi2z:function(eccent,ts){var eccnth=.5*eccent;var con,dphi;var phi=this.HALF_PI-2*Math.atan(ts);for(i=0;i<=15;i++){con=eccent*Math.sin(phi);dphi=this.HALF_PI-2*Math.atan(ts*(Math.pow(((1.0-con)/(1.0+con)),eccnth)))-phi;phi+=dphi;if(Math.abs(dphi)<=.0000000001)return phi;} |
| 107 | alert("phi2z has NoConvergence");return(-9999);},qsfnz:function(eccent,sinphi){var con;if(eccent>1.0e-7){con=eccent*sinphi;return((1.0-eccent*eccent)*(sinphi/(1.0-con*con)-(.5/eccent)*Math.log((1.0-con)/(1.0+con))));}else{return(2.0*sinphi);}},asinz:function(x){if(Math.abs(x)>1.0){x=(x>1.0)?1.0:-1.0;} |
| 108 | return Math.asin(x);},e0fn:function(x){return(1.0-0.25*x*(1.0+x/16.0*(3.0+1.25*x)));},e1fn:function(x){return(0.375*x*(1.0+0.25*x*(1.0+0.46875*x)));},e2fn:function(x){return(0.05859375*x*x*(1.0+0.75*x));},e3fn:function(x){return(x*x*x*(35.0/3072.0));},mlfn:function(e0,e1,e2,e3,phi){return(e0*phi-e1*Math.sin(2.0*phi)+e2*Math.sin(4.0*phi)-e3*Math.sin(6.0*phi));},srat:function(esinp,exp){return(Math.pow((1.0-esinp)/(1.0+esinp),exp));},sign:function(x){if(x<0.0)return(-1);else return(1);},adjust_lon:function(x){x=(Math.abs(x)<this.PI)?x:(x-(this.sign(x)*this.TWO_PI));return x;},adjust_lat:function(x){x=(Math.abs(x)<this.HALF_PI)?x:(x-(this.sign(x)*this.PI));return x;},latiso:function(eccent,phi,sinphi){if(Math.abs(phi)>this.HALF_PI)return+Number.NaN;if(phi==this.HALF_PI)return Number.POSITIVE_INFINITY;if(phi==-1.0*this.HALF_PI)return-1.0*Number.POSITIVE_INFINITY;var con=eccent*sinphi;return Math.log(Math.tan((this.HALF_PI+phi)/2.0))+eccent*Math.log((1.0-con)/(1.0+con))/2.0;},fL:function(x,L){return 2.0*Math.atan(x*Math.exp(L))-this.HALF_PI;},invlatiso:function(eccent,ts){var phi=this.fL(1.0,ts);var Iphi=0.0;var con=0.0;do{Iphi=phi;con=eccent*Math.sin(Iphi);phi=this.fL(Math.exp(eccent*Math.log((1.0+con)/(1.0-con))/2.0),ts)}while(Math.abs(phi-Iphi)>1.0e-12);return phi;},sinh:function(x) |
| 109 | {var r=Math.exp(x);r=(r-1.0/r)/2.0;return r;},cosh:function(x) |
| 110 | {var r=Math.exp(x);r=(r+1.0/r)/2.0;return r;},tanh:function(x) |
| 111 | {var r=Math.exp(x);r=(r-1.0/r)/(r+1.0/r);return r;},asinh:function(x) |
| 112 | {var s=(x>=0?1.0:-1.0);return s*(Math.log(Math.abs(x)+Math.sqrt(x*x+1.0)));},acosh:function(x) |
| 113 | {return 2.0*Math.log(Math.sqrt((x+1.0)/2.0)+Math.sqrt((x-1.0)/2.0));},atanh:function(x) |
| 114 | {return Math.log((x-1.0)/(x+1.0))/2.0;},gN:function(a,e,sinphi) |
| 115 | {var temp=e*sinphi;return a/Math.sqrt(1.0-temp*temp);}};Proj4js.datum=Proj4js.Class({initialize:function(proj){this.datum_type=Proj4js.common.PJD_WGS84;if(proj.datumCode&&proj.datumCode=='none'){this.datum_type=Proj4js.common.PJD_NODATUM;} |
| 116 | if(proj&&proj.datum_params){for(var i=0;i<proj.datum_params.length;i++){proj.datum_params[i]=parseFloat(proj.datum_params[i]);} |
| 117 | if(proj.datum_params[0]!=0||proj.datum_params[1]!=0||proj.datum_params[2]!=0){this.datum_type=Proj4js.common.PJD_3PARAM;} |
| 118 | if(proj.datum_params.length>3){if(proj.datum_params[3]!=0||proj.datum_params[4]!=0||proj.datum_params[5]!=0||proj.datum_params[6]!=0){this.datum_type=Proj4js.common.PJD_7PARAM;proj.datum_params[3]*=Proj4js.common.SEC_TO_RAD;proj.datum_params[4]*=Proj4js.common.SEC_TO_RAD;proj.datum_params[5]*=Proj4js.common.SEC_TO_RAD;proj.datum_params[6]=(proj.datum_params[6]/1000000.0)+1.0;}}} |
| 119 | if(proj){this.a=proj.a;this.b=proj.b;this.es=proj.es;this.ep2=proj.ep2;this.datum_params=proj.datum_params;}},compare_datums:function(dest){if(this.datum_type!=dest.datum_type){return false;}else if(this.a!=dest.a||Math.abs(this.es-dest.es)>0.000000000050){return false;}else if(this.datum_type==Proj4js.common.PJD_3PARAM){return(this.datum_params[0]==dest.datum_params[0]&&this.datum_params[1]==dest.datum_params[1]&&this.datum_params[2]==dest.datum_params[2]);}else if(this.datum_type==Proj4js.common.PJD_7PARAM){return(this.datum_params[0]==dest.datum_params[0]&&this.datum_params[1]==dest.datum_params[1]&&this.datum_params[2]==dest.datum_params[2]&&this.datum_params[3]==dest.datum_params[3]&&this.datum_params[4]==dest.datum_params[4]&&this.datum_params[5]==dest.datum_params[5]&&this.datum_params[6]==dest.datum_params[6]);}else if(this.datum_type==Proj4js.common.PJD_GRIDSHIFT){return strcmp(pj_param(this.params,"snadgrids").s,pj_param(dest.params,"snadgrids").s)==0;}else{return true;}},geodetic_to_geocentric:function(p){var Longitude=p.x;var Latitude=p.y;var Height=p.z?p.z:0;var X;var Y;var Z;var Error_Code=0;var Rn;var Sin_Lat;var Sin2_Lat;var Cos_Lat;if(Latitude<-Proj4js.common.HALF_PI&&Latitude>-1.001*Proj4js.common.HALF_PI){Latitude=-Proj4js.common.HALF_PI;}else if(Latitude>Proj4js.common.HALF_PI&&Latitude<1.001*Proj4js.common.HALF_PI){Latitude=Proj4js.common.HALF_PI;}else if((Latitude<-Proj4js.common.HALF_PI)||(Latitude>Proj4js.common.HALF_PI)){Proj4js.reportError('geocent:lat out of range:'+Latitude);return null;} |
| 120 | if(Longitude>Proj4js.common.PI)Longitude-=(2*Proj4js.common.PI);Sin_Lat=Math.sin(Latitude);Cos_Lat=Math.cos(Latitude);Sin2_Lat=Sin_Lat*Sin_Lat;Rn=this.a/(Math.sqrt(1.0e0-this.es*Sin2_Lat));X=(Rn+Height)*Cos_Lat*Math.cos(Longitude);Y=(Rn+Height)*Cos_Lat*Math.sin(Longitude);Z=((Rn*(1-this.es))+Height)*Sin_Lat;p.x=X;p.y=Y;p.z=Z;return Error_Code;},geocentric_to_geodetic:function(p){var genau=1.E-12;var genau2=(genau*genau);var maxiter=30;var P;var RR;var CT;var ST;var RX;var RK;var RN;var CPHI0;var SPHI0;var CPHI;var SPHI;var SDPHI;var At_Pole;var iter;var X=p.x;var Y=p.y;var Z=p.z?p.z:0.0;var Longitude;var Latitude;var Height;At_Pole=false;P=Math.sqrt(X*X+Y*Y);RR=Math.sqrt(X*X+Y*Y+Z*Z);if(P/this.a<genau){At_Pole=true;Longitude=0.0;if(RR/this.a<genau){Latitude=Proj4js.common.HALF_PI;Height=-this.b;return;}}else{Longitude=Math.atan2(Y,X);} |
| 121 | CT=Z/RR;ST=P/RR;RX=1.0/Math.sqrt(1.0-this.es*(2.0-this.es)*ST*ST);CPHI0=ST*(1.0-this.es)*RX;SPHI0=CT*RX;iter=0;do |
| 122 | {iter++;RN=this.a/Math.sqrt(1.0-this.es*SPHI0*SPHI0);Height=P*CPHI0+Z*SPHI0-RN*(1.0-this.es*SPHI0*SPHI0);RK=this.es*RN/(RN+Height);RX=1.0/Math.sqrt(1.0-RK*(2.0-RK)*ST*ST);CPHI=ST*(1.0-RK)*RX;SPHI=CT*RX;SDPHI=SPHI*CPHI0-CPHI*SPHI0;CPHI0=CPHI;SPHI0=SPHI;} |
| 123 | while(SDPHI*SDPHI>genau2&&iter<maxiter);Latitude=Math.atan(SPHI/Math.abs(CPHI));p.x=Longitude;p.y=Latitude;p.z=Height;return p;},geocentric_to_geodetic_noniter:function(p){var X=p.x;var Y=p.y;var Z=p.z?p.z:0;var Longitude;var Latitude;var Height;var W;var W2;var T0;var T1;var S0;var S1;var Sin_B0;var Sin3_B0;var Cos_B0;var Sin_p1;var Cos_p1;var Rn;var Sum;var At_Pole;X=parseFloat(X);Y=parseFloat(Y);Z=parseFloat(Z);At_Pole=false;if(X!=0.0) |
| 124 | {Longitude=Math.atan2(Y,X);} |
| 125 | else |
| 126 | {if(Y>0) |
| 127 | {Longitude=Proj4js.common.HALF_PI;} |
| 128 | else if(Y<0) |
| 129 | {Longitude=-Proj4js.common.HALF_PI;} |
| 130 | else |
| 131 | {At_Pole=true;Longitude=0.0;if(Z>0.0) |
| 132 | {Latitude=Proj4js.common.HALF_PI;} |
| 133 | else if(Z<0.0) |
| 134 | {Latitude=-Proj4js.common.HALF_PI;} |
| 135 | else |
| 136 | {Latitude=Proj4js.common.HALF_PI;Height=-this.b;return;}}} |
| 137 | W2=X*X+Y*Y;W=Math.sqrt(W2);T0=Z*Proj4js.common.AD_C;S0=Math.sqrt(T0*T0+W2);Sin_B0=T0/S0;Cos_B0=W/S0;Sin3_B0=Sin_B0*Sin_B0*Sin_B0;T1=Z+this.b*this.ep2*Sin3_B0;Sum=W-this.a*this.es*Cos_B0*Cos_B0*Cos_B0;S1=Math.sqrt(T1*T1+Sum*Sum);Sin_p1=T1/S1;Cos_p1=Sum/S1;Rn=this.a/Math.sqrt(1.0-this.es*Sin_p1*Sin_p1);if(Cos_p1>=Proj4js.common.COS_67P5) |
| 138 | {Height=W/Cos_p1-Rn;} |
| 139 | else if(Cos_p1<=-Proj4js.common.COS_67P5) |
| 140 | {Height=W/-Cos_p1-Rn;} |
| 141 | else |
| 142 | {Height=Z/Sin_p1+Rn*(this.es-1.0);} |
| 143 | if(At_Pole==false) |
| 144 | {Latitude=Math.atan(Sin_p1/Cos_p1);} |
| 145 | p.x=Longitude;p.y=Latitude;p.z=Height;return p;},geocentric_to_wgs84:function(p){if(this.datum_type==Proj4js.common.PJD_3PARAM) |
| 146 | {p.x+=this.datum_params[0];p.y+=this.datum_params[1];p.z+=this.datum_params[2];} |
| 147 | else if(this.datum_type==Proj4js.common.PJD_7PARAM) |
| 148 | {var Dx_BF=this.datum_params[0];var Dy_BF=this.datum_params[1];var Dz_BF=this.datum_params[2];var Rx_BF=this.datum_params[3];var Ry_BF=this.datum_params[4];var Rz_BF=this.datum_params[5];var M_BF=this.datum_params[6];var x_out=M_BF*(p.x-Rz_BF*p.y+Ry_BF*p.z)+Dx_BF;var y_out=M_BF*(Rz_BF*p.x+p.y-Rx_BF*p.z)+Dy_BF;var z_out=M_BF*(-Ry_BF*p.x+Rx_BF*p.y+p.z)+Dz_BF;p.x=x_out;p.y=y_out;p.z=z_out;}},geocentric_from_wgs84:function(p){if(this.datum_type==Proj4js.common.PJD_3PARAM) |
| 149 | {p.x-=this.datum_params[0];p.y-=this.datum_params[1];p.z-=this.datum_params[2];} |
| 150 | else if(this.datum_type==Proj4js.common.PJD_7PARAM) |
| 151 | {var Dx_BF=this.datum_params[0];var Dy_BF=this.datum_params[1];var Dz_BF=this.datum_params[2];var Rx_BF=this.datum_params[3];var Ry_BF=this.datum_params[4];var Rz_BF=this.datum_params[5];var M_BF=this.datum_params[6];var x_tmp=(p.x-Dx_BF)/M_BF;var y_tmp=(p.y-Dy_BF)/M_BF;var z_tmp=(p.z-Dz_BF)/M_BF;p.x=x_tmp+Rz_BF*y_tmp-Ry_BF*z_tmp;p.y=-Rz_BF*x_tmp+y_tmp+Rx_BF*z_tmp;p.z=Ry_BF*x_tmp-Rx_BF*y_tmp+z_tmp;}}});Proj4js.Point=Proj4js.Class({initialize:function(x,y,z){if(typeof x=='object'){this.x=x[0];this.y=x[1];this.z=x[2]||0.0;}else if(typeof x=='string'){var coords=x.split(',');this.x=parseFloat(coords[0]);this.y=parseFloat(coords[1]);this.z=parseFloat(coords[2])||0.0;}else{this.x=x;this.y=y;this.z=z||0.0;}},clone:function(){return new Proj4js.Point(this.x,this.y,this.z);},toString:function(){return("x="+this.x+",y="+this.y);},toShortString:function(){return(this.x+", "+this.y);}});Proj4js.PrimeMeridian={"greenwich":0.0,"lisbon":-9.131906111111,"paris":2.337229166667,"bogota":-74.080916666667,"madrid":-3.687938888889,"rome":12.452333333333,"bern":7.439583333333,"jakarta":106.807719444444,"ferro":-17.666666666667,"brussels":4.367975,"stockholm":18.058277777778,"athens":23.7163375,"oslo":10.722916666667};Proj4js.Ellipsoid={"MERIT":{a:6378137.0,rf:298.257,ellipseName:"MERIT 1983"},"SGS85":{a:6378136.0,rf:298.257,ellipseName:"Soviet Geodetic System 85"},"GRS80":{a:6378137.0,rf:298.257222101,ellipseName:"GRS 1980(IUGG, 1980)"},"IAU76":{a:6378140.0,rf:298.257,ellipseName:"IAU 1976"},"airy":{a:6377563.396,b:6356256.910,ellipseName:"Airy 1830"},"APL4.":{a:6378137,rf:298.25,ellipseName:"Appl. Physics. 1965"},"NWL9D":{a:6378145.0,rf:298.25,ellipseName:"Naval Weapons Lab., 1965"},"mod_airy":{a:6377340.189,b:6356034.446,ellipseName:"Modified Airy"},"andrae":{a:6377104.43,rf:300.0,ellipseName:"Andrae 1876 (Den., Iclnd.)"},"aust_SA":{a:6378160.0,rf:298.25,ellipseName:"Australian Natl & S. Amer. 1969"},"GRS67":{a:6378160.0,rf:298.2471674270,ellipseName:"GRS 67(IUGG 1967)"},"bessel":{a:6377397.155,rf:299.1528128,ellipseName:"Bessel 1841"},"bess_nam":{a:6377483.865,rf:299.1528128,ellipseName:"Bessel 1841 (Namibia)"},"clrk66":{a:6378206.4,b:6356583.8,ellipseName:"Clarke 1866"},"clrk80":{a:6378249.145,rf:293.4663,ellipseName:"Clarke 1880 mod."},"CPM":{a:6375738.7,rf:334.29,ellipseName:"Comm. des Poids et Mesures 1799"},"delmbr":{a:6376428.0,rf:311.5,ellipseName:"Delambre 1810 (Belgium)"},"engelis":{a:6378136.05,rf:298.2566,ellipseName:"Engelis 1985"},"evrst30":{a:6377276.345,rf:300.8017,ellipseName:"Everest 1830"},"evrst48":{a:6377304.063,rf:300.8017,ellipseName:"Everest 1948"},"evrst56":{a:6377301.243,rf:300.8017,ellipseName:"Everest 1956"},"evrst69":{a:6377295.664,rf:300.8017,ellipseName:"Everest 1969"},"evrstSS":{a:6377298.556,rf:300.8017,ellipseName:"Everest (Sabah & Sarawak)"},"fschr60":{a:6378166.0,rf:298.3,ellipseName:"Fischer (Mercury Datum) 1960"},"fschr60m":{a:6378155.0,rf:298.3,ellipseName:"Fischer 1960"},"fschr68":{a:6378150.0,rf:298.3,ellipseName:"Fischer 1968"},"helmert":{a:6378200.0,rf:298.3,ellipseName:"Helmert 1906"},"hough":{a:6378270.0,rf:297.0,ellipseName:"Hough"},"intl":{a:6378388.0,rf:297.0,ellipseName:"International 1909 (Hayford)"},"kaula":{a:6378163.0,rf:298.24,ellipseName:"Kaula 1961"},"lerch":{a:6378139.0,rf:298.257,ellipseName:"Lerch 1979"},"mprts":{a:6397300.0,rf:191.0,ellipseName:"Maupertius 1738"},"new_intl":{a:6378157.5,b:6356772.2,ellipseName:"New International 1967"},"plessis":{a:6376523.0,rf:6355863.0,ellipseName:"Plessis 1817 (France)"},"krass":{a:6378245.0,rf:298.3,ellipseName:"Krassovsky, 1942"},"SEasia":{a:6378155.0,b:6356773.3205,ellipseName:"Southeast Asia"},"walbeck":{a:6376896.0,b:6355834.8467,ellipseName:"Walbeck"},"WGS60":{a:6378165.0,rf:298.3,ellipseName:"WGS 60"},"WGS66":{a:6378145.0,rf:298.25,ellipseName:"WGS 66"},"WGS72":{a:6378135.0,rf:298.26,ellipseName:"WGS 72"},"WGS84":{a:6378137.0,rf:298.257223563,ellipseName:"WGS 84"},"sphere":{a:6370997.0,b:6370997.0,ellipseName:"Normal Sphere (r=6370997)"}};Proj4js.Datum={"WGS84":{towgs84:"0,0,0",ellipse:"WGS84",datumName:"WGS84"},"GGRS87":{towgs84:"-199.87,74.79,246.62",ellipse:"GRS80",datumName:"Greek_Geodetic_Reference_System_1987"},"NAD83":{towgs84:"0,0,0",ellipse:"GRS80",datumName:"North_American_Datum_1983"},"NAD27":{nadgrids:"@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat",ellipse:"clrk66",datumName:"North_American_Datum_1927"},"potsdam":{towgs84:"606.0,23.0,413.0",ellipse:"bessel",datumName:"Potsdam Rauenberg 1950 DHDN"},"carthage":{towgs84:"-263.0,6.0,431.0",ellipse:"clark80",datumName:"Carthage 1934 Tunisia"},"hermannskogel":{towgs84:"653.0,-212.0,449.0",ellipse:"bessel",datumName:"Hermannskogel"},"ire65":{towgs84:"482.530,-130.596,564.557,-1.042,-0.214,-0.631,8.15",ellipse:"mod_airy",datumName:"Ireland 1965"},"nzgd49":{towgs84:"59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993",ellipse:"intl",datumName:"New Zealand Geodetic Datum 1949"},"OSGB36":{towgs84:"446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894",ellipse:"airy",datumName:"Airy 1830"}};Proj4js.WGS84=new Proj4js.Proj('WGS84');Proj4js.Datum['OSB36']=Proj4js.Datum['OSGB36'];Proj4js.Proj.aea={init:function(){if(Math.abs(this.lat1+this.lat2)<Proj4js.common.EPSLN){Proj4js.reportError("aeaInitEqualLatitudes");return;} |
| 152 | this.temp=this.b/this.a;this.es=1.0-Math.pow(this.temp,2);this.e3=Math.sqrt(this.es);this.sin_po=Math.sin(this.lat1);this.cos_po=Math.cos(this.lat1);this.t1=this.sin_po;this.con=this.sin_po;this.ms1=Proj4js.common.msfnz(this.e3,this.sin_po,this.cos_po);this.qs1=Proj4js.common.qsfnz(this.e3,this.sin_po,this.cos_po);this.sin_po=Math.sin(this.lat2);this.cos_po=Math.cos(this.lat2);this.t2=this.sin_po;this.ms2=Proj4js.common.msfnz(this.e3,this.sin_po,this.cos_po);this.qs2=Proj4js.common.qsfnz(this.e3,this.sin_po,this.cos_po);this.sin_po=Math.sin(this.lat0);this.cos_po=Math.cos(this.lat0);this.t3=this.sin_po;this.qs0=Proj4js.common.qsfnz(this.e3,this.sin_po,this.cos_po);if(Math.abs(this.lat1-this.lat2)>Proj4js.common.EPSLN){this.ns0=(this.ms1*this.ms1-this.ms2*this.ms2)/(this.qs2-this.qs1);}else{this.ns0=this.con;} |
| 153 | this.c=this.ms1*this.ms1+this.ns0*this.qs1;this.rh=this.a*Math.sqrt(this.c-this.ns0*this.qs0)/this.ns0;},forward:function(p){var lon=p.x;var lat=p.y;this.sin_phi=Math.sin(lat);this.cos_phi=Math.cos(lat);var qs=Proj4js.common.qsfnz(this.e3,this.sin_phi,this.cos_phi);var rh1=this.a*Math.sqrt(this.c-this.ns0*qs)/this.ns0;var theta=this.ns0*Proj4js.common.adjust_lon(lon-this.long0);var x=rh1*Math.sin(theta)+this.x0;var y=this.rh-rh1*Math.cos(theta)+this.y0;p.x=x;p.y=y;return p;},inverse:function(p){var rh1,qs,con,theta,lon,lat;p.x-=this.x0;p.y=this.rh-p.y+this.y0;if(this.ns0>=0){rh1=Math.sqrt(p.x*p.x+p.y*p.y);con=1.0;}else{rh1=-Math.sqrt(p.x*p.x+p.y*p.y);con=-1.0;} |
| 154 | theta=0.0;if(rh1!=0.0){theta=Math.atan2(con*p.x,con*p.y);} |
| 155 | con=rh1*this.ns0/this.a;qs=(this.c-con*con)/this.ns0;if(this.e3>=1e-10){con=1-.5*(1.0-this.es)*Math.log((1.0-this.e3)/(1.0+this.e3))/this.e3;if(Math.abs(Math.abs(con)-Math.abs(qs))>.0000000001){lat=this.phi1z(this.e3,qs);}else{if(qs>=0){lat=.5*PI;}else{lat=-.5*PI;}}}else{lat=this.phi1z(e3,qs);} |
| 156 | lon=Proj4js.common.adjust_lon(theta/this.ns0+this.long0);p.x=lon;p.y=lat;return p;},phi1z:function(eccent,qs){var con,com,dphi;var phi=Proj4js.common.asinz(.5*qs);if(eccent<Proj4js.common.EPSLN)return phi;var eccnts=eccent*eccent;for(var i=1;i<=25;i++){sinphi=Math.sin(phi);cosphi=Math.cos(phi);con=eccent*sinphi;com=1.0-con*con;dphi=.5*com*com/cosphi*(qs/(1.0-eccnts)-sinphi/com+.5/eccent*Math.log((1.0-con)/(1.0+con)));phi=phi+dphi;if(Math.abs(dphi)<=1e-7)return phi;} |
| 157 | Proj4js.reportError("aea:phi1z:Convergence error");return null;}};Proj4js.Proj.sterea={dependsOn:'gauss',init:function(){Proj4js.Proj['gauss'].init.apply(this);if(!this.rc){Proj4js.reportError("sterea:init:E_ERROR_0");return;} |
| 158 | this.sinc0=Math.sin(this.phic0);this.cosc0=Math.cos(this.phic0);this.R2=2.0*this.rc;if(!this.title)this.title="Oblique Stereographic Alternative";},forward:function(p){p.x=Proj4js.common.adjust_lon(p.x-this.long0);Proj4js.Proj['gauss'].forward.apply(this,[p]);sinc=Math.sin(p.y);cosc=Math.cos(p.y);cosl=Math.cos(p.x);k=this.k0*this.R2/(1.0+this.sinc0*sinc+this.cosc0*cosc*cosl);p.x=k*cosc*Math.sin(p.x);p.y=k*(this.cosc0*sinc-this.sinc0*cosc*cosl);p.x=this.a*p.x+this.x0;p.y=this.a*p.y+this.y0;return p;},inverse:function(p){var lon,lat;p.x=(p.x-this.x0)/this.a;p.y=(p.y-this.y0)/this.a;p.x/=this.k0;p.y/=this.k0;if((rho=Math.sqrt(p.x*p.x+p.y*p.y))){c=2.0*Math.atan2(rho,this.R2);sinc=Math.sin(c);cosc=Math.cos(c);lat=Math.asin(cosc*this.sinc0+p.y*sinc*this.cosc0/rho);lon=Math.atan2(p.x*sinc,rho*this.cosc0*cosc-p.y*this.sinc0*sinc);}else{lat=this.phic0;lon=0.;} |
| 159 | p.x=lon;p.y=lat;Proj4js.Proj['gauss'].inverse.apply(this,[p]);p.x=Proj4js.common.adjust_lon(p.x+this.long0);return p;}};function phi4z(eccent,e0,e1,e2,e3,a,b,c,phi){var sinphi,sin2ph,tanph,ml,mlp,con1,con2,con3,dphi,i;phi=a;for(i=1;i<=15;i++){sinphi=Math.sin(phi);tanphi=Math.tan(phi);c=tanphi*Math.sqrt(1.0-eccent*sinphi*sinphi);sin2ph=Math.sin(2.0*phi);ml=e0*phi-e1*sin2ph+e2*Math.sin(4.0*phi)-e3*Math.sin(6.0*phi);mlp=e0-2.0*e1*Math.cos(2.0*phi)+4.0*e2*Math.cos(4.0*phi)-6.0*e3*Math.cos(6.0*phi);con1=2.0*ml+c*(ml*ml+b)-2.0*a*(c*ml+1.0);con2=eccent*sin2ph*(ml*ml+b-2.0*a*ml)/(2.0*c);con3=2.0*(a-ml)*(c*mlp-2.0/sin2ph)-2.0*mlp;dphi=con1/(con2+con3);phi+=dphi;if(Math.abs(dphi)<=.0000000001)return(phi);} |
| 160 | Proj4js.reportError("phi4z: No convergence");return null;} |
| 161 | function e4fn(x){var con,com;con=1.0+x;com=1.0-x;return(Math.sqrt((Math.pow(con,con))*(Math.pow(com,com))));} |
| 162 | Proj4js.Proj.poly={init:function(){var temp;if(this.lat0=0)this.lat0=90;this.temp=this.b/this.a;this.es=1.0-Math.pow(this.temp,2);this.e=Math.sqrt(this.es);this.e0=Proj4js.common.e0fn(this.es);this.e1=Proj4js.common.e1fn(this.es);this.e2=Proj4js.common.e2fn(this.es);this.e3=Proj4js.common.e3fn(this.es);this.ml0=Proj4js.common.mlfn(this.e0,this.e1,this.e2,this.e3,this.lat0);},forward:function(p){var sinphi,cosphi;var al;var c;var con,ml;var ms;var x,y;var lon=p.x;var lat=p.y;con=Proj4js.common.adjust_lon(lon-this.long0);if(Math.abs(lat)<=.0000001){x=this.x0+this.a*con;y=this.y0-this.a*this.ml0;}else{sinphi=Math.sin(lat);cosphi=Math.cos(lat);ml=Proj4js.common.mlfn(this.e0,this.e1,this.e2,this.e3,lat);ms=Proj4js.common.msfnz(this.e,sinphi,cosphi);con=sinphi;x=this.x0+this.a*ms*Math.sin(con)/sinphi;y=this.y0+this.a*(ml-this.ml0+ms*(1.0-Math.cos(con))/sinphi);} |
| 163 | p.x=x;p.y=y;return p;},inverse:function(p){var sin_phi,cos_phi;var al;var b;var c;var con,ml;var iflg;var lon,lat;p.x-=this.x0;p.y-=this.y0;al=this.ml0+p.y/this.a;iflg=0;if(Math.abs(al)<=.0000001){lon=p.x/this.a+this.long0;lat=0.0;}else{b=al*al+(p.x/this.a)*(p.x/this.a);iflg=phi4z(this.es,this.e0,this.e1,this.e2,this.e3,this.al,b,c,lat);if(iflg!=1)return(iflg);lon=Proj4js.common.adjust_lon((Proj4js.common.asinz(p.x*c/this.a)/Math.sin(lat))+this.long0);} |
| 164 | p.x=lon;p.y=lat;return p;}};Proj4js.Proj.equi={init:function(){if(!this.x0)this.x0=0;if(!this.y0)this.y0=0;if(!this.lat0)this.lat0=0;if(!this.long0)this.long0=0;},forward:function(p){var lon=p.x;var lat=p.y;var dlon=Proj4js.common.adjust_lon(lon-this.long0);var x=this.x0+this.a*dlon*Math.cos(this.lat0);var y=this.y0+this.a*lat;this.t1=x;this.t2=Math.cos(this.lat0);p.x=x;p.y=y;return p;},inverse:function(p){p.x-=this.x0;p.y-=this.y0;var lat=p.y/this.a;if(Math.abs(lat)>Proj4js.common.HALF_PI){Proj4js.reportError("equi:Inv:DataError");} |
| 165 | var lon=Proj4js.common.adjust_lon(this.long0+p.x/(this.a*Math.cos(this.lat0)));p.x=lon;p.y=lat;}};Proj4js.Proj.merc={init:function(){if(this.lat_ts){if(this.sphere){this.k0=Math.cos(this.lat_ts);}else{this.k0=Proj4js.common.msfnz(this.es,Math.sin(this.lat_ts),Math.cos(this.lat_ts));}}},forward:function(p){var lon=p.x;var lat=p.y;if(lat*Proj4js.common.R2D>90.0&&lat*Proj4js.common.R2D<-90.0&&lon*Proj4js.common.R2D>180.0&&lon*Proj4js.common.R2D<-180.0){Proj4js.reportError("merc:forward: llInputOutOfRange: "+lon+" : "+lat);return null;} |
| 166 | var x,y;if(Math.abs(Math.abs(lat)-Proj4js.common.HALF_PI)<=Proj4js.common.EPSLN){Proj4js.reportError("merc:forward: ll2mAtPoles");return null;}else{if(this.sphere){x=this.x0+this.a*this.k0*Proj4js.common.adjust_lon(lon-this.long0);y=this.y0+this.a*this.k0*Math.log(Math.tan(Proj4js.common.FORTPI+0.5*lat));}else{var sinphi=Math.sin(lat);var ts=Proj4js.common.tsfnz(this.e,lat,sinphi);x=this.x0+this.a*this.k0*Proj4js.common.adjust_lon(lon-this.long0);y=this.y0-this.a*this.k0*Math.log(ts);} |
| 167 | p.x=x;p.y=y;return p;}},inverse:function(p){var x=p.x-this.x0;var y=p.y-this.y0;var lon,lat;if(this.sphere){lat=Proj4js.common.HALF_PI-2.0*Math.atan(Math.exp(-y/this.a*this.k0));}else{var ts=Math.exp(-y/(this.a*this.k0));lat=Proj4js.common.phi2z(this.e,ts);if(lat==-9999){Proj4js.reportError("merc:inverse: lat = -9999");return null;}} |
| 168 | lon=Proj4js.common.adjust_lon(this.long0+x/(this.a*this.k0));p.x=lon;p.y=lat;return p;}};Proj4js.Proj.utm={dependsOn:'tmerc',init:function(){if(!this.zone){Proj4js.reportError("utm:init: zone must be specified for UTM");return;} |
| 169 | this.lat0=0.0;this.long0=((6*Math.abs(this.zone))-183)*Proj4js.common.D2R;this.x0=500000.0;this.y0=this.utmSouth?10000000.0:0.0;this.k0=0.9996;Proj4js.Proj['tmerc'].init.apply(this);this.forward=Proj4js.Proj['tmerc'].forward;this.inverse=Proj4js.Proj['tmerc'].inverse;}};Proj4js.Proj.eqdc={init:function(){if(!this.mode)this.mode=0;this.temp=this.b/this.a;this.es=1.0-Math.pow(this.temp,2);this.e=Math.sqrt(this.es);this.e0=Proj4js.common.e0fn(this.es);this.e1=Proj4js.common.e1fn(this.es);this.e2=Proj4js.common.e2fn(this.es);this.e3=Proj4js.common.e3fn(this.es);this.sinphi=Math.sin(this.lat1);this.cosphi=Math.cos(this.lat1);this.ms1=Proj4js.common.msfnz(this.e,this.sinphi,this.cosphi);this.ml1=Proj4js.common.mlfn(this.e0,this.e1,this.e2,this.e3,this.lat1);if(this.mode!=0){if(Math.abs(this.lat1+this.lat2)<Proj4js.common.EPSLN){Proj4js.reportError("eqdc:Init:EqualLatitudes");} |
| 170 | this.sinphi=Math.sin(this.lat2);this.cosphi=Math.cos(this.lat2);this.ms2=Proj4js.common.msfnz(this.e,this.sinphi,this.cosphi);this.ml2=Proj4js.common.mlfn(this.e0,this.e1,this.e2,this.e3,this.lat2);if(Math.abs(this.lat1-this.lat2)>=Proj4js.common.EPSLN){this.ns=(this.ms1-this.ms2)/(this.ml2-this.ml1);}else{this.ns=this.sinphi;}}else{this.ns=this.sinphi;} |
| 171 | this.g=this.ml1+this.ms1/this.ns;this.ml0=Proj4js.common.mlfn(this.e0,this.e1,this.e2,this.e3,this.lat0);this.rh=this.a*(this.g-this.ml0);},forward:function(p){var lon=p.x;var lat=p.y;var ml=Proj4js.common.mlfn(this.e0,this.e1,this.e2,this.e3,lat);var rh1=this.a*(this.g-ml);var theta=this.ns*Proj4js.common.adjust_lon(lon-this.long0);var x=this.x0+rh1*Math.sin(theta);var y=this.y0+this.rh-rh1*Math.cos(theta);p.x=x;p.y=y;return p;},inverse:function(p){p.x-=this.x0;p.y=this.rh-p.y+this.y0;var con,rh1;if(this.ns>=0){var rh1=Math.sqrt(p.x*p.x+p.y*p.y);var con=1.0;}else{rh1=-Math.sqrt(p.x*p.x+p.y*p.y);con=-1.0;} |
| 172 | var theta=0.0;if(rh1!=0.0)theta=Math.atan2(con*p.x,con*p.y);var ml=this.g-rh1/this.a;var lat=this.phi3z(this.ml,this.e0,this.e1,this.e2,this.e3);var lon=Proj4js.common.adjust_lon(this.long0+theta/this.ns);p.x=lon;p.y=lat;return p;},phi3z:function(ml,e0,e1,e2,e3){var phi;var dphi;phi=ml;for(var i=0;i<15;i++){dphi=(ml+e1*Math.sin(2.0*phi)-e2*Math.sin(4.0*phi)+e3*Math.sin(6.0*phi))/e0-phi;phi+=dphi;if(Math.abs(dphi)<=.0000000001){return phi;}} |
| 173 | Proj4js.reportError("PHI3Z-CONV:Latitude failed to converge after 15 iterations");return null;}};Proj4js.Proj.tmerc={init:function(){this.e0=Proj4js.common.e0fn(this.es);this.e1=Proj4js.common.e1fn(this.es);this.e2=Proj4js.common.e2fn(this.es);this.e3=Proj4js.common.e3fn(this.es);this.ml0=this.a*Proj4js.common.mlfn(this.e0,this.e1,this.e2,this.e3,this.lat0);},forward:function(p){var lon=p.x;var lat=p.y;var delta_lon=Proj4js.common.adjust_lon(lon-this.long0);var con;var x,y;var sin_phi=Math.sin(lat);var cos_phi=Math.cos(lat);if(this.sphere){var b=cos_phi*Math.sin(delta_lon);if((Math.abs(Math.abs(b)-1.0))<.0000000001){Proj4js.reportError("tmerc:forward: Point projects into infinity");return(93);}else{x=.5*this.a*this.k0*Math.log((1.0+b)/(1.0-b));con=Math.acos(cos_phi*Math.cos(delta_lon)/Math.sqrt(1.0-b*b));if(lat<0)con=-con;y=this.a*this.k0*(con-this.lat0);}}else{var al=cos_phi*delta_lon;var als=Math.pow(al,2);var c=this.ep2*Math.pow(cos_phi,2);var tq=Math.tan(lat);var t=Math.pow(tq,2);con=1.0-this.es*Math.pow(sin_phi,2);var n=this.a/Math.sqrt(con);var ml=this.a*Proj4js.common.mlfn(this.e0,this.e1,this.e2,this.e3,lat);x=this.k0*n*al*(1.0+als/6.0*(1.0-t+c+als/20.0*(5.0-18.0*t+Math.pow(t,2)+72.0*c-58.0*this.ep2)))+this.x0;y=this.k0*(ml-this.ml0+n*tq*(als*(0.5+als/24.0*(5.0-t+9.0*c+4.0*Math.pow(c,2)+als/30.0*(61.0-58.0*t+Math.pow(t,2)+600.0*c-330.0*this.ep2)))))+this.y0;} |
| 174 | p.x=x;p.y=y;return p;},inverse:function(p){var con,phi;var delta_phi;var i;var max_iter=6;var lat,lon;if(this.sphere){var f=Math.exp(p.x/(this.a*this.k0));var g=.5*(f-1/f);var temp=this.lat0+p.y/(this.a*this.k0);var h=Math.cos(temp);con=Math.sqrt((1.0-h*h)/(1.0+g*g));lat=Proj4js.common.asinz(con);if(temp<0) |
| 175 | lat=-lat;if((g==0)&&(h==0)){lon=this.long0;}else{lon=Proj4js.common.adjust_lon(Math.atan2(g,h)+this.long0);}}else{var x=p.x-this.x0;var y=p.y-this.y0;con=(this.ml0+y/this.k0)/this.a;phi=con;for(i=0;true;i++){delta_phi=((con+this.e1*Math.sin(2.0*phi)-this.e2*Math.sin(4.0*phi)+this.e3*Math.sin(6.0*phi))/this.e0)-phi;phi+=delta_phi;if(Math.abs(delta_phi)<=Proj4js.common.EPSLN)break;if(i>=max_iter){Proj4js.reportError("tmerc:inverse: Latitude failed to converge");return(95);}} |
| 176 | if(Math.abs(phi)<Proj4js.common.HALF_PI){var sin_phi=Math.sin(phi);var cos_phi=Math.cos(phi);var tan_phi=Math.tan(phi);var c=this.ep2*Math.pow(cos_phi,2);var cs=Math.pow(c,2);var t=Math.pow(tan_phi,2);var ts=Math.pow(t,2);con=1.0-this.es*Math.pow(sin_phi,2);var n=this.a/Math.sqrt(con);var r=n*(1.0-this.es)/con;var d=x/(n*this.k0);var ds=Math.pow(d,2);lat=phi-(n*tan_phi*ds/r)*(0.5-ds/24.0*(5.0+3.0*t+10.0*c-4.0*cs-9.0*this.ep2-ds/30.0*(61.0+90.0*t+298.0*c+45.0*ts-252.0*this.ep2-3.0*cs)));lon=Proj4js.common.adjust_lon(this.long0+(d*(1.0-ds/6.0*(1.0+2.0*t+c-ds/20.0*(5.0-2.0*c+28.0*t-3.0*cs+8.0*this.ep2+24.0*ts)))/cos_phi));}else{lat=Proj4js.common.HALF_PI*Proj4js.common.sign(y);lon=this.long0;}} |
| 177 | p.x=lon;p.y=lat;return p;}};Proj4js.defs["GOOGLE"]="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs";Proj4js.defs["EPSG:900913"]=Proj4js.defs["GOOGLE"];Proj4js.Proj.gstmerc={init:function(){var temp=this.b/this.a;this.e=Math.sqrt(1.0-temp*temp);this.lc=this.long0;this.rs=Math.sqrt(1.0+this.e*this.e*Math.pow(Math.cos(this.lat0),4.0)/(1.0-this.e*this.e));var sinz=Math.sin(this.lat0);var pc=Math.asin(sinz/this.rs);var sinzpc=Math.sin(pc);this.cp=Proj4js.common.latiso(0.0,pc,sinzpc)-this.rs*Proj4js.common.latiso(this.e,this.lat0,sinz);this.n2=this.k0*this.a*Math.sqrt(1.0-this.e*this.e)/(1.0-this.e*this.e*sinz*sinz);this.xs=this.x0;this.ys=this.y0-this.n2*pc;if(!this.title)this.title="Gauss Schreiber transverse mercator";},forward:function(p){var lon=p.x;var lat=p.y;var L=this.rs*(lon-this.lc);var Ls=this.cp+(this.rs*Proj4js.common.latiso(this.e,lat,Math.sin(lat)));var lat1=Math.asin(Math.sin(L)/Proj4js.common.cosh(Ls));var Ls1=Proj4js.common.latiso(0.0,lat1,Math.sin(lat1));p.x=this.xs+(this.n2*Ls1);p.y=this.ys+(this.n2*Math.atan(Proj4js.common.sinh(Ls)/Math.cos(L)));return p;},inverse:function(p){var x=p.x;var y=p.y;var L=Math.atan(Proj4js.common.sinh((x-this.xs)/this.n2)/Math.cos((y-this.ys)/this.n2));var lat1=Math.asin(Math.sin((y-this.ys)/this.n2)/Proj4js.common.cosh((x-this.xs)/this.n2));var LC=Proj4js.common.latiso(0.0,lat1,Math.sin(lat1));p.x=this.lc+L/this.rs;p.y=Proj4js.common.invlatiso(this.e,(LC-this.cp)/this.rs);return p;}};Proj4js.Proj.ortho={init:function(def){;this.sin_p14=Math.sin(this.lat0);this.cos_p14=Math.cos(this.lat0);},forward:function(p){var sinphi,cosphi;var dlon;var coslon;var ksp;var g;var lon=p.x;var lat=p.y;dlon=Proj4js.common.adjust_lon(lon-this.long0);sinphi=Math.sin(lat);cosphi=Math.cos(lat);coslon=Math.cos(dlon);g=this.sin_p14*sinphi+this.cos_p14*cosphi*coslon;ksp=1.0;if((g>0)||(Math.abs(g)<=Proj4js.common.EPSLN)){var x=this.a*ksp*cosphi*Math.sin(dlon);var y=this.y0+this.a*ksp*(this.cos_p14*sinphi-this.sin_p14*cosphi*coslon);}else{Proj4js.reportError("orthoFwdPointError");} |
| 178 | p.x=x;p.y=y;return p;},inverse:function(p){var rh;var z;var sinz,cosz;var temp;var con;var lon,lat;p.x-=this.x0;p.y-=this.y0;rh=Math.sqrt(p.x*p.x+p.y*p.y);if(rh>this.a+.0000001){Proj4js.reportError("orthoInvDataError");} |
| 179 | z=Proj4js.common.asinz(rh/this.a);sinz=Math.sin(z);cosz=Math.cos(z);lon=this.long0;if(Math.abs(rh)<=Proj4js.common.EPSLN){lat=this.lat0;} |
| 180 | lat=Proj4js.common.asinz(cosz*this.sin_p14+(p.y*sinz*this.cos_p14)/rh);con=Math.abs(lat0)-Proj4js.common.HALF_PI;if(Math.abs(con)<=Proj4js.common.EPSLN){if(this.lat0>=0){lon=Proj4js.common.adjust_lon(this.long0+Math.atan2(p.x,-p.y));}else{lon=Proj4js.common.adjust_lon(this.long0-Math.atan2(-p.x,p.y));}} |
| 181 | con=cosz-this.sin_p14*Math.sin(lat);if((Math.abs(con)>=Proj4js.common.EPSLN)||(Math.abs(x)>=Proj4js.common.EPSLN)){lon=Proj4js.common.adjust_lon(this.long0+Math.atan2((p.x*sinz*this.cos_p14),(con*rh)));} |
| 182 | p.x=lon;p.y=lat;return p;}};Proj4js.Proj.somerc={init:function(){var phy0=this.lat0;this.lambda0=this.long0;var sinPhy0=Math.sin(phy0);var semiMajorAxis=this.a;var invF=this.rf;var flattening=1/invF;var e2=2*flattening-Math.pow(flattening,2);var e=this.e=Math.sqrt(e2);this.R=semiMajorAxis*Math.sqrt(1-e2)/(1-e2*Math.pow(sinPhy0,2.0));this.alpha=Math.sqrt(1+e2/(1-e2)*Math.pow(Math.cos(phy0),4.0));this.b0=Math.asin(sinPhy0/this.alpha);this.K=Math.log(Math.tan(Math.PI/4.0+this.b0/2.0)) |
| 183 | -this.alpha*Math.log(Math.tan(Math.PI/4.0+phy0/2.0)) |
| 184 | +this.alpha*e/2*Math.log((1+e*sinPhy0)/(1-e*sinPhy0));},forward:function(p){var Sa1=Math.log(Math.tan(Math.PI/4.0-p.y/2.0));var Sa2=this.e/2.0*Math.log((1+this.e*Math.sin(p.y))/(1-this.e*Math.sin(p.y)));var S=-this.alpha*(Sa1+Sa2)+this.K;var b=2.0*(Math.atan(Math.exp(S))-Math.PI/4.0);var I=this.alpha*(p.x-this.lambda0);var rotI=Math.atan(Math.sin(I)/(Math.sin(this.b0)*Math.tan(b)+ |
| 185 | Math.cos(this.b0)*Math.cos(I)));var rotB=Math.asin(Math.cos(this.b0)*Math.sin(b)- |
| 186 | Math.sin(this.b0)*Math.cos(b)*Math.cos(I));p.y=this.R/2.0*Math.log((1+Math.sin(rotB))/(1-Math.sin(rotB))) |
| 187 | +this.y0;p.x=this.R*rotI+this.x0;return p;},inverse:function(p){var Y=p.x-this.x0;var X=p.y-this.y0;var rotI=Y/this.R;var rotB=2*(Math.atan(Math.exp(X/this.R))-Math.PI/4.0);var b=Math.asin(Math.cos(this.b0)*Math.sin(rotB) |
| 188 | +Math.sin(this.b0)*Math.cos(rotB)*Math.cos(rotI));var I=Math.atan(Math.sin(rotI)/(Math.cos(this.b0)*Math.cos(rotI)-Math.sin(this.b0)*Math.tan(rotB)));var lambda=this.lambda0+I/this.alpha;var S=0.0;var phy=b;var prevPhy=-1000.0;var iteration=0;while(Math.abs(phy-prevPhy)>0.0000001) |
| 189 | {if(++iteration>20) |
| 190 | {Proj4js.reportError("omercFwdInfinity");return;} |
| 191 | S=1.0/this.alpha*(Math.log(Math.tan(Math.PI/4.0+b/2.0))-this.K) |
| 192 | +this.e*Math.log(Math.tan(Math.PI/4.0 |
| 193 | +Math.asin(this.e*Math.sin(phy))/2.0));prevPhy=phy;phy=2.0*Math.atan(Math.exp(S))-Math.PI/2.0;} |
| 194 | p.x=lambda;p.y=phy;return p;}};Proj4js.Proj.stere={ssfn_:function(phit,sinphi,eccen){sinphi*=eccen;return(Math.tan(.5*(Proj4js.common.HALF_PI+phit))*Math.pow((1.-sinphi)/(1.+sinphi),.5*eccen));},TOL:1.e-8,NITER:8,CONV:1.e-10,S_POLE:0,N_POLE:1,OBLIQ:2,EQUIT:3,init:function(){this.phits=this.lat_ts?this.lat_ts:Proj4js.common.HALF_PI;var t=Math.abs(this.lat0);if((Math.abs(t)-Proj4js.common.HALF_PI)<Proj4js.common.EPSLN){this.mode=this.lat0<0.?this.S_POLE:this.N_POLE;}else{this.mode=t>Proj4js.common.EPSLN?this.OBLIQ:this.EQUIT;} |
| 195 | this.phits=Math.abs(this.phits);if(this.es){var X;switch(this.mode){case this.N_POLE:case this.S_POLE:if(Math.abs(this.phits-Proj4js.common.HALF_PI)<Proj4js.common.EPSLN){this.akm1=2.*this.k0/Math.sqrt(Math.pow(1+this.e,1+this.e)*Math.pow(1-this.e,1-this.e));}else{t=Math.sin(this.phits);this.akm1=Math.cos(this.phits)/Proj4js.common.tsfnz(this.e,this.phits,t);t*=this.e;this.akm1/=Math.sqrt(1.-t*t);} |
| 196 | break;case this.EQUIT:this.akm1=2.*this.k0;break;case this.OBLIQ:t=Math.sin(this.lat0);X=2.*Math.atan(this.ssfn_(this.lat0,t,this.e))-Proj4js.common.HALF_PI;t*=this.e;this.akm1=2.*this.k0*Math.cos(this.lat0)/Math.sqrt(1.-t*t);this.sinX1=Math.sin(X);this.cosX1=Math.cos(X);break;}}else{switch(this.mode){case this.OBLIQ:this.sinph0=Math.sin(this.lat0);this.cosph0=Math.cos(this.lat0);case this.EQUIT:this.akm1=2.*this.k0;break;case this.S_POLE:case this.N_POLE:this.akm1=Math.abs(this.phits-Proj4js.common.HALF_PI)>=Proj4js.common.EPSLN?Math.cos(this.phits)/Math.tan(Proj4js.common.FORTPI-.5*this.phits):2.*this.k0;break;}}},forward:function(p){var lon=p.x;lon=Proj4js.common.adjust_lon(lon-this.long0);var lat=p.y;var x,y;if(this.sphere){var sinphi,cosphi,coslam,sinlam;sinphi=Math.sin(lat);cosphi=Math.cos(lat);coslam=Math.cos(lon);sinlam=Math.sin(lon);switch(this.mode){case this.EQUIT:y=1.+cosphi*coslam;if(y<=Proj4js.common.EPSLN){F_ERROR;} |
| 197 | y=this.akm1/y;x=y*cosphi*sinlam;y*=sinphi;break;case this.OBLIQ:y=1.+this.sinph0*sinphi+this.cosph0*cosphi*coslam;if(y<=Proj4js.common.EPSLN){F_ERROR;} |
| 198 | y=this.akm1/y;x=y*cosphi*sinlam;y*=this.cosph0*sinphi-this.sinph0*cosphi*coslam;break;case this.N_POLE:coslam=-coslam;lat=-lat;case this.S_POLE:if(Math.abs(lat-Proj4js.common.HALF_PI)<this.TOL){F_ERROR;} |
| 199 | y=this.akm1*Math.tan(Proj4js.common.FORTPI+.5*lat);x=sinlam*y;y*=coslam;break;}}else{coslam=Math.cos(lon);sinlam=Math.sin(lon);sinphi=Math.sin(lat);if(this.mode==this.OBLIQ||this.mode==this.EQUIT){X=2.*Math.atan(this.ssfn_(lat,sinphi,this.e));sinX=Math.sin(X-Proj4js.common.HALF_PI);cosX=Math.cos(X);} |
| 200 | switch(this.mode){case this.OBLIQ:A=this.akm1/(this.cosX1*(1.+this.sinX1*sinX+this.cosX1*cosX*coslam));y=A*(this.cosX1*sinX-this.sinX1*cosX*coslam);x=A*cosX;break;case this.EQUIT:A=2.*this.akm1/(1.+cosX*coslam);y=A*sinX;x=A*cosX;break;case this.S_POLE:lat=-lat;coslam=-coslam;sinphi=-sinphi;case this.N_POLE:x=this.akm1*Proj4js.common.tsfnz(this.e,lat,sinphi);y=-x*cos |