diff --git a/pacotes/wicket/README.md b/pacotes/wicket/README.md new file mode 100644 index 0000000..4b142b6 --- /dev/null +++ b/pacotes/wicket/README.md @@ -0,0 +1,123 @@ +########## +# Wicket # +########## + +Updated **April 8, 2012** by K. Arthur Endsley + +################ +## Motivation ## +################ + +Wicket was created out of the need for a lightweight Javascript library that can translate Well-Known Text (WKT) strings into geographic features. This problem arose in the context of [OpenClimateGIS](https://github.com/arthur-e/OpenClimateGIS), a web framework for accessing and subsetting online climate data. + +OpenClimateGIS emits WKT representations of user-defined geometry. Our [API explorer](http://www.openclimategis.org/builder/) allows users to define arbitrary areas-of-interest (AOIs) and view predefined AOIs on a Google Maps API instance. So, initially, the problem was converting between WKT strings and Google Maps API features. While other mapping libraries, such as [OpenLayers](http://www.openlayers.org), have very nice WKT libraries built-in, the Google Maps API, as of this writing, does not. In the (apparent) absence of lightweight, easy-to-use WKT library in Javascript, I set out to create one. + +That is what Wicket aspires to be: lightweight, framework-agnostic, and useful. I hope it achieves these goals. If you find it isn't living up to that and you have ideas on how to improve it, please fork the code or [drop me a line](mailto:kaendsle@mtu.edu). + +############## +## Colophon ## +############## + +######################## +### Acknowledgements ### +######################## + +The following open sources were borrowed from; they retain all their original rights: + +* The OpenLayers 2.7 WKT module (OpenLayers.Format.WKT) +* Chris Pietshmann's [article on converting Bing Maps shapes (VEShape) to WKT](http://pietschsoft.com/post/2009/04/04/Virtual-Earth-Shapes-%28VEShape%29-to-WKT-%28Well-Known-Text%29-and-Back-using-JavaScript.aspx) +* Charles R. Schmidt's and the Python Spatial Analysis Laboratory's (PySAL) WKT writer + +################### +### Conventions ### +################### + +The conventions I've adopted in writing this library: + +* The Crockford-ian module pattern with a single global (namespace) variable +* The most un-Crockford-ian use of new to instantiate new Objects (when this is required, the Object begins with a capital letter e.g. new Wkt()) +* The namespace is the only name beginning with a capital letter that doesn't need to and shouldn't be preceded by new +* The namespace is the result of a function allowing for private members +* Tricky operators (++ and --) and type-coercing operators (== and !=) are not used + +The base library, wicket.js, contains the Wkt.Wkt base object. This object doesn't do anything on its own except read in WKT strings, allow the underlying geometry to be manipulated programmatically, and write WKT strings. By loading additional libraries, such as wicket-gmap3.js, users can transform between between WKT and the features of a given framework (e.g. google.maps.Polygon instances). The intent is to add support for new frameworks as additional Javascript files that alter the Wkt.Wkt prototype. + +############## +## Concepts ## +############## + +WKT geometries are stored internally using the following convention. The atomic unit of geometry is the coordinate pair (e.g. latitude and longitude) which is represented by an Object with x and y properties. An Array with a single coordinate pair represents a a single point (i.e. POINT feature) + + [ {x: -83.123, y: 42.123} ] + +An Array of multiple points (an Array of Arrays) specifies a "collection" of points (i.e. a MULTIPOINT feature): + + [ + [ {x: -83.123, y: 42.123} ], + [ {x: -83.234, y: 42.234} ] + ] + +An Array of multiple coordinates specifies a collection of connected points in an ordered sequence (i.e. LINESTRING feature): + + [ + {x: -83.12, y: 42.12}, + {x: -83.23, y: 42.23}, + {x: -83.34, y: 42.34} + ] + +An Array can also contain other Arrays. In these cases, the contained Array(s) can each represent one of two geometry types. The contained Array might reprsent a single polygon (i.e. POLYGON feature): + + [ + [ + {x: -83, y: 42}, + {x: -83, y: 43}, + {x: -82, y: 43}, + {x: -82, y: 42}, + {x: -83, y: 42} + ] + ] + +It might also represent a LINESTRING feature. Both POLYGON and LINESTRING features are internally represented the same way. The difference between the two is specified elsewhere (in the Wkt instance's type) and must be retained. In this particular example (above), we can see that the first coordinate in the Array is repeated at the end, meaning that the geometry is closed. We can therefore infer it represents a POLYGON and not a LINESTRING even before we plot it. Wicket retains the *type* of the feature and will always remember which it is. + +Similarly, multiple nested Arrays might reprsent a MULTIPOLYGON feature: + + [ + [ + [ + {x: -83, y: 42}, + {x: -83, y: 43}, + {x: -82, y: 43}, + {x: -82, y: 42}, + {x: -83, y: 42} + ] + ], + [ + [ + {x: -70, y: 40}, + {x: -70, y: 41}, + {x: -69, y: 41}, + {x: -69, y: 40}, + {x: -70, y: 40} + ] + ] + ] + +Or a POLYGON with inner rings (holes) in it where the outer ring is the polygon envelope and comes first; subsequent Arrays are inner rings (holes): + + [ + [ + {x: 35, y: 10}, + {x: 10, y: 20}, + {x: 15, y: 40}, + {x: 45, y: 45}, + {x: 35, y: 10} + ], + [ + {x: 20, y: 30}, + {x: 35, y: 35}, + {x: 30, y: 20}, + {x: 20, y: 30} + ] + ] + +Or they might represent a MULTILINESTRING where each nested Array is a different LINESTRING in the collection. Again, Wicket remembers the correct *type* of feature even though the internal representation is ambiguous. diff --git a/pacotes/wicket/doc/dot_shadow.png b/pacotes/wicket/doc/dot_shadow.png new file mode 100644 index 0000000..788f45c Binary files /dev/null and b/pacotes/wicket/doc/dot_shadow.png differ diff --git a/pacotes/wicket/doc/index.css b/pacotes/wicket/doc/index.css new file mode 100644 index 0000000..00a8bb2 --- /dev/null +++ b/pacotes/wicket/doc/index.css @@ -0,0 +1,81 @@ +body {margin:0;padding:0;background:#CF6;border-top:5px solid #000;font-family:Helvetica,Arial,sans-serif;font-size:14px;} +img {border:none;} +label {color:#666;} +label:hover {color:#333;} +a:link, +a:visited {color:#460;text-decoration:none;} + +.attribute {float:right;padding:10px 0;} +.menu {float:left;margin:0;padding:0;list-style:none;} +.menu a {display:inline-block;margin:0;padding:5px 0 10px;margin:0 25px 0 0;border-top:5px solid transparent;} +.menu a:hover, +.menu a:active {border-top:5px solid #999;color:#000;text-decoration:none;} +.text {color:#333;} +.wrapper {width:1000px;height:100%;margin:0 auto;} + +#canvas {width:698px;height:100%;background:#AAA;border-left:1px solid #999;border-right:1px solid #999;} +#canvas, +#controls {float:left;} +#controls {width:290px;height:100%;padding:0 0 0 10px;text-align:justify;background:transparent;position:relative;top:-80px;} +#controls .text {margin:90px 0 0;} +#controls a:hover, +#controls a:active {text-decoration:underline;} +#forkme {width:141px;height:141px;z-index:9999;position:absolute;left:0;top:0;background:transparent url(/static/ForkMe_Blk.png) no-repeat left top;} +#form {margin:10px 0 0;font-size:14px;color:#666;font-family:CabinItalic,sans-serif;} +#form #wkt {width:100%;height:150px;border:1px solid #999;padding:3px;resize:none;} +#form #urlify {vertical-align:baseline;margin:10px 5px 0 0;} +#form #reset {margin:10px 10px 0 0; + filter: progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc'); + -ms-filter: "progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc')"; + background-image: -moz-linear-gradient(top, #eeeeee, #cccccc); + background-image: -ms-linear-gradient(top, #eeeeee, #cccccc); + background-image: -o-linear-gradient(top, #eeeeee, #cccccc); + background-image: -webkit-gradient(linear, center top, center bottom, from(#eeeeee), to(#cccccc)); + background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc); + background-image: linear-gradient(top, #eeeeee, #cccccc); + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; + } +#form #reset, +#form #submit {float:right;height:30px;margin-top:10px;padding:0 5px 2px 5px;font-family:CabinItalic,sans-serif;font-size:16px;color:#666; + border: 1px solid #999999; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + } +#form #submit {background-color: #EF9; + filter: progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeffcc', endColorstr = '#ddff99'); + -ms-filter: "progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeffcc', endColorstr = '#ddff99')"; + background-image: -moz-linear-gradient(top, #eeffcc, #ddff99); + background-image: -ms-linear-gradient(top, #eeffcc, #ddff99); + background-image: -o-linear-gradient(top, #eeffcc, #ddff99); + background-image: -webkit-gradient(linear, center top, center bottom, from(#eeffcc), to(#ddff99)); + background-image: -webkit-linear-gradient(top, #eeffcc, #ddff99); + background-image: linear-gradient(top, #eeffcc, #ddff99); + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; + } +#form #reset:hover, +#form #submit:hover {color:#333;border-color:#666;text-shadow:1px 1px 0px #FFF; + -moz-box-shadow: 0px 0px 2px #999999; + -webkit-box-shadow: 0px 0px 2px #999999; + box-shadow: 0px 0px 2px #999999; + } +#form #reset:active, +#form #submit:active { + -moz-box-shadow:inset 0px 0px 2px #999999; + -webkit-box-shadow:inset 0px 0px 2px #999999; + box-shadow:inset 0px 0px 2px #999999; + } +#foot {background:transparent url(white_spacer.gif) repeat-x top left;} +#foot, +#head {width:100%;height:20%;color:#333;} +#head {background:transparent url(white_spacer.gif) repeat-x bottom left;} +#nav {width:698px;text-align:left;} +#ribbon {width:100%;height:60%;background:#EEE;} +#ribbon .wrapper {} +#ribbon .wrapper .title {float:right;width:300px;height:80px;font-family:CabinRegular,sans-serif;font-size:16px;color:#333;} +#ribbon .wrapper .title .brand {font-family:CabinMediumItalic,sans-serif;font-size:36px;color:#000;text-shadow:1px 1px 1px #FFF;} + diff --git a/pacotes/wicket/doc/index.html b/pacotes/wicket/doc/index.html new file mode 100644 index 0000000..fd2961c --- /dev/null +++ b/pacotes/wicket/doc/index.html @@ -0,0 +1,272 @@ + + + + + + + + + + + + + +Wicket - Lightweight Javascript for WKT [Sandbox] + + + + + + + + +
  +
+
+ +
+
+
+
+
+
+
Wicket
+  It whispers WKT in your application's ear. +
+
+ Wicket is a lightweight Javascript library that reads and writes Well-Known Text (WKT) strings. It can also be extended to parse and to create geometric objects from various mapping frameworks, such as the Google Maps API. +
+
+ + + + +
+
+
+
+ + + diff --git a/pacotes/wicket/doc/index.ie.css b/pacotes/wicket/doc/index.ie.css new file mode 100644 index 0000000..00a8bb2 --- /dev/null +++ b/pacotes/wicket/doc/index.ie.css @@ -0,0 +1,81 @@ +body {margin:0;padding:0;background:#CF6;border-top:5px solid #000;font-family:Helvetica,Arial,sans-serif;font-size:14px;} +img {border:none;} +label {color:#666;} +label:hover {color:#333;} +a:link, +a:visited {color:#460;text-decoration:none;} + +.attribute {float:right;padding:10px 0;} +.menu {float:left;margin:0;padding:0;list-style:none;} +.menu a {display:inline-block;margin:0;padding:5px 0 10px;margin:0 25px 0 0;border-top:5px solid transparent;} +.menu a:hover, +.menu a:active {border-top:5px solid #999;color:#000;text-decoration:none;} +.text {color:#333;} +.wrapper {width:1000px;height:100%;margin:0 auto;} + +#canvas {width:698px;height:100%;background:#AAA;border-left:1px solid #999;border-right:1px solid #999;} +#canvas, +#controls {float:left;} +#controls {width:290px;height:100%;padding:0 0 0 10px;text-align:justify;background:transparent;position:relative;top:-80px;} +#controls .text {margin:90px 0 0;} +#controls a:hover, +#controls a:active {text-decoration:underline;} +#forkme {width:141px;height:141px;z-index:9999;position:absolute;left:0;top:0;background:transparent url(/static/ForkMe_Blk.png) no-repeat left top;} +#form {margin:10px 0 0;font-size:14px;color:#666;font-family:CabinItalic,sans-serif;} +#form #wkt {width:100%;height:150px;border:1px solid #999;padding:3px;resize:none;} +#form #urlify {vertical-align:baseline;margin:10px 5px 0 0;} +#form #reset {margin:10px 10px 0 0; + filter: progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc'); + -ms-filter: "progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc')"; + background-image: -moz-linear-gradient(top, #eeeeee, #cccccc); + background-image: -ms-linear-gradient(top, #eeeeee, #cccccc); + background-image: -o-linear-gradient(top, #eeeeee, #cccccc); + background-image: -webkit-gradient(linear, center top, center bottom, from(#eeeeee), to(#cccccc)); + background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc); + background-image: linear-gradient(top, #eeeeee, #cccccc); + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; + } +#form #reset, +#form #submit {float:right;height:30px;margin-top:10px;padding:0 5px 2px 5px;font-family:CabinItalic,sans-serif;font-size:16px;color:#666; + border: 1px solid #999999; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + } +#form #submit {background-color: #EF9; + filter: progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeffcc', endColorstr = '#ddff99'); + -ms-filter: "progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeffcc', endColorstr = '#ddff99')"; + background-image: -moz-linear-gradient(top, #eeffcc, #ddff99); + background-image: -ms-linear-gradient(top, #eeffcc, #ddff99); + background-image: -o-linear-gradient(top, #eeffcc, #ddff99); + background-image: -webkit-gradient(linear, center top, center bottom, from(#eeffcc), to(#ddff99)); + background-image: -webkit-linear-gradient(top, #eeffcc, #ddff99); + background-image: linear-gradient(top, #eeffcc, #ddff99); + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; + } +#form #reset:hover, +#form #submit:hover {color:#333;border-color:#666;text-shadow:1px 1px 0px #FFF; + -moz-box-shadow: 0px 0px 2px #999999; + -webkit-box-shadow: 0px 0px 2px #999999; + box-shadow: 0px 0px 2px #999999; + } +#form #reset:active, +#form #submit:active { + -moz-box-shadow:inset 0px 0px 2px #999999; + -webkit-box-shadow:inset 0px 0px 2px #999999; + box-shadow:inset 0px 0px 2px #999999; + } +#foot {background:transparent url(white_spacer.gif) repeat-x top left;} +#foot, +#head {width:100%;height:20%;color:#333;} +#head {background:transparent url(white_spacer.gif) repeat-x bottom left;} +#nav {width:698px;text-align:left;} +#ribbon {width:100%;height:60%;background:#EEE;} +#ribbon .wrapper {} +#ribbon .wrapper .title {float:right;width:300px;height:80px;font-family:CabinRegular,sans-serif;font-size:16px;color:#333;} +#ribbon .wrapper .title .brand {font-family:CabinMediumItalic,sans-serif;font-size:36px;color:#000;text-shadow:1px 1px 1px #FFF;} + diff --git a/pacotes/wicket/doc/red_dot.png b/pacotes/wicket/doc/red_dot.png new file mode 100644 index 0000000..b4389c6 Binary files /dev/null and b/pacotes/wicket/doc/red_dot.png differ diff --git a/pacotes/wicket/doc/white_spacer.gif b/pacotes/wicket/doc/white_spacer.gif new file mode 100644 index 0000000..c621c98 Binary files /dev/null and b/pacotes/wicket/doc/white_spacer.gif differ diff --git a/pacotes/wicket/wicket-gmap3.js b/pacotes/wicket/wicket-gmap3.js new file mode 100644 index 0000000..ba14b83 --- /dev/null +++ b/pacotes/wicket/wicket-gmap3.js @@ -0,0 +1,8 @@ +google.maps.Marker.prototype.type="marker";google.maps.Polyline.prototype.type="polyline";google.maps.Polygon.prototype.type="polygon";google.maps.Rectangle.prototype.type="rectangle";google.maps.Circle.prototype.type="circle"; +Wkt.Wkt.prototype.construct={point:function(config,component){var c=component||this.components;config=config||{};config.position=new google.maps.LatLng(c[0].y,c[0].x);return new google.maps.Marker(config)},multipoint:function(config){var i,c,arr;c=this.components;config=config||{};arr=[];for(i=0;i1)verts=[verts];rings.push(verts)}return{type:"polygon",components:rings}}else if(obj.getBounds&&!obj.getRadius){tmp=obj.getBounds();return{type:"polygon",isRectangle:true,components:[[{x:tmp.getSouthWest().lng(),y:tmp.getNorthEast().lat()},{x:tmp.getNorthEast().lng(),y:tmp.getNorthEast().lat()},{x:tmp.getNorthEast().lng(), +y:tmp.getSouthWest().lat()},{x:tmp.getSouthWest().lng(),y:tmp.getSouthWest().lat()},{x:tmp.getSouthWest().lng(),y:tmp.getNorthEast().lat()}]]}}else if(obj.getBounds&&obj.getRadius)console.log("Deconstruction of google.maps.Circle objects is not yet supported");else console.log("The passed object does not have any recognizable properties.")};Wkt.Wkt.prototype.isRectangle=false; diff --git a/pacotes/wicket/wicket-gmap3.src.js b/pacotes/wicket/wicket-gmap3.src.js new file mode 100644 index 0000000..c502953 --- /dev/null +++ b/pacotes/wicket/wicket-gmap3.src.js @@ -0,0 +1,313 @@ +/*global Wkt, google, document, window, console*/ +google.maps.Marker.prototype.type = 'marker'; +google.maps.Polyline.prototype.type = 'polyline'; +google.maps.Polygon.prototype.type = 'polygon'; +google.maps.Rectangle.prototype.type = 'rectangle'; +google.maps.Circle.prototype.type = 'circle'; + +/** + * An object of framework-dependent construction methods used to generate + * objects belonging to the various geometry classes of the framework. + */ +Wkt.Wkt.prototype.construct = { + /** + * Creates the framework's equivalent point geometry object. + * @param config {Object} An optional properties hash the object should use + * @param component {Object} An optional component to build from + * @return {google.maps.Marker} + */ + 'point': function (config, component) { + var c = component || this.components; + + config = config || {}; + + config.position = new google.maps.LatLng(c[0].y, c[0].x); + + return new google.maps.Marker(config); + }, + + /** + * Creates the framework's equivalent multipoint geometry object. + * @param config {Object} An optional properties hash the object should use + * @return {Array} Array containing multiple google.maps.Marker + */ + 'multipoint': function (config) { + var i, c, arr; + + c = this.components; + + config = config || {}; + + arr = []; + + for (i = 0; i < c.length; i += 1) { + arr.push(this.construct.point(config, c[i])); + } + + return arr; + }, + + /** + * Creates the framework's equivalent multipoint geometry object. + * @param config {Object} An optional properties hash the object should use + * @param component {Object} An optional component to build from + * @return {google.maps.Polyline} + */ + 'linestring': function (config, component) { + var i, c; + + c = component || this.components; + + config = config || { + editable: false + }; + + config.path = []; + + for (i = 0; i < c.length; i += 1) { + config.path.push(new google.maps.LatLng(c[i].y, c[i].x)); + } + + return new google.maps.Polyline(config); + }, + + /** + * Creates the framework's equivalent multilinestring geometry object. + * @param config {Object} An optional properties hash the object should use + * @return {Array} Array containing multiple google.maps.Polyline instances + */ + 'multilinestring': function (config) { + var i, c, arr; + + c = this.components; + + config = config || { + editable: false + }; + + config.path = []; + + arr = []; + + for (i = 0; i < c.length; i += 1) { + arr.push(this.construct.linestring(config, c[i])); + } + + return arr; + }, + + /** + * Creates the framework's equivalent polygon geometry object. + * @param config {Object} An optional properties hash the object should use + * @return {google.maps.Polygon} + */ + 'polygon': function (config) { + var j, k, c, rings, verts; + + c = this.components; + + config = config || { + editable: false // Editable geometry off by default + }; + + config.paths = []; + + rings = []; + for (j = 0; j < c.length; j += 1) { // For each ring... + + verts = []; + for (k = 0; k < c[j].length; k += 1) { // For each vertex... + verts.push(new google.maps.LatLng(c[j][k].y, c[j][k].x)); + + } // eo for each vertex + + if (j !== 0) { // Reverse the order of coordinates in inner rings + if (config.reverseInnerPolygons == null || config.reverseInnerPolygons) { + verts.reverse(); + } + } + + rings.push(verts); + } // eo for each ring + + config.paths = config.paths.concat(rings); + + if (this.isRectangle) { + console.log('Rectangles are not yet supported; set the isRectangle property to false (default).'); + } else { + return new google.maps.Polygon(config); + } + }, + + /** + * Creates the framework's equivalent multipolygon geometry object. + * @param config {Object} An optional properties hash the object should use + * @return {Array} Array containing multiple google.maps.Polygon + */ + 'multipolygon': function (config) { + var i, j, k, c, rings, verts; + + c = this.components; + + config = config || { + editable: false // Editable geometry off by default + }; + + config.paths = []; // Must ensure this property is available + + for (i = 0; i < c.length; i += 1) { // For each polygon... + + rings = []; + for (j = 0; j < c[i].length; j += 1) { // For each ring... + + verts = []; + for (k = 0; k < c[i][j].length; k += 1) { // For each vertex... + verts.push(new google.maps.LatLng(c[i][j][k].y, c[i][j][k].x)); + + } // eo for each vertex + +/* // This is apparently not needed in multipolygon cases + if (j !== 0) { // Reverse the order of coordinates in inner rings + verts.reverse(); + } +*/ + rings.push(verts); + } // eo for each ring + + config.paths = config.paths.concat(rings); + + } // eo for each polygon + + return new google.maps.Polygon(config); + } + +}; + +/** + * A framework-dependent deconstruction method used to generate internal + * geometric representations from instances of framework geometry. This method + * uses object detection to attempt to classify members of framework geometry + * classes into the standard WKT types. + * @param obj {Object} An instance of one of the framework's geometry classes + * @return {Object} A hash of the 'type' and 'components' thus derived + */ +Wkt.Wkt.prototype.deconstruct = function (obj) { + var i, j, verts, rings, tmp; + + // google.maps.Marker ////////////////////////////////////////////////////// + if (obj.getPosition && typeof obj.getPosition === 'function') { + // Only Markers, among all overlays, have the getPosition property + + return { + type: 'point', + components: [{ + x: obj.getPosition().lng(), + y: obj.getPosition().lat() + }] + }; + + // google.maps.Polyline //////////////////////////////////////////////////// + } else if (obj.getPath && !obj.getPaths) { + // Polylines have a single path (getPath) not paths (getPaths) + + verts = []; + for (i = 0; i < obj.getPath().length; i += 1) { + tmp = obj.getPath().getAt(i); + verts.push({ + x: tmp.lng(), + y: tmp.lat() + }); + } + + return { + type: 'linestring', + components: verts + }; + + // google.maps.Polygon ///////////////////////////////////////////////////// + } else if (obj.getPaths) { + // Polygon is the only class with the getPaths property + + // TODO Polygons with holes cannot be distinguished from multipolygons + rings = []; + for (i = 0; i < obj.getPaths().length; i += 1) { // For each polygon (ring)... + tmp = obj.getPaths().getAt(i); + + verts = []; + for (j = 0; j < obj.getPaths().getAt(i).length; j += 1) { // For each vertex... + verts.push({ + x: tmp.getAt(j).lng(), + y: tmp.getAt(j).lat() + }); + } + + verts.push({ // Add the first coordinate again for closure + x: tmp.getAt(0).lng(), + y: tmp.getAt(0).lat() + }); + + // Since we can't distinguish between single polygons with holes + // and multipolygons, we always create multipolygons + if (obj.getPaths().length > 1) { + verts = [verts]; // Wrap multipolygons once more (collection) + } + + rings.push(verts); + } + + return { + type: 'polygon', + components: rings + }; + + // google.maps.Rectangle /////////////////////////////////////////////////// + } else if (obj.getBounds && !obj.getRadius) { + // Rectangle is only overlay class with getBounds property and not getRadius property + + tmp = obj.getBounds(); + return { + type: 'polygon', + isRectangle: true, + components: [ + [ + { // NW corner + x: tmp.getSouthWest().lng(), + y: tmp.getNorthEast().lat() + }, + { // NE corner + x: tmp.getNorthEast().lng(), + y: tmp.getNorthEast().lat() + }, + { // SE corner + x: tmp.getNorthEast().lng(), + y: tmp.getSouthWest().lat() + }, + { // SW corner + x: tmp.getSouthWest().lng(), + y: tmp.getSouthWest().lat() + }, + { // NW corner (again, for closure) + x: tmp.getSouthWest().lng(), + y: tmp.getNorthEast().lat() + } + ] + ] + }; + + // google.maps.Circle ////////////////////////////////////////////////////// + } else if (obj.getBounds && obj.getRadius) { + // Circle is the only overlay class with both the getBounds and getRadius properties + + console.log('Deconstruction of google.maps.Circle objects is not yet supported'); + + } else { + console.log('The passed object does not have any recognizable properties.'); + } + +}; + +/** + * A framework-dependent flag, set for each Wkt.Wkt() instance, that indicates + * whether or not a closed polygon geometry should be interpreted as a rectangle. + */ +Wkt.Wkt.prototype.isRectangle = false; diff --git a/pacotes/wicket/wicket-leaflet.js b/pacotes/wicket/wicket-leaflet.js new file mode 100644 index 0000000..9d446ed --- /dev/null +++ b/pacotes/wicket/wicket-leaflet.js @@ -0,0 +1,4 @@ +Wkt.Wkt.prototype.isRectangle=false; +Wkt.Wkt.prototype.construct={point:function(config,component){var coord=component||this.components;if(coord instanceof Array)coord=coord[0];return L.marker(this.coordsToLatLng(coord),config)},multipoint:function(config){var layers=[],coords=this.components,latlng;for(var i=0,len=coords.length;i0)pieces.push(",");if(!this.extract[this.type])return null;data=this.extract[this.type].apply(this,[components[i]]);if(this.isCollection())pieces.push("("+data+")");else{pieces.push(data);if(i!==components.length-1)pieces.push(",")}}pieces.push(")");return pieces.join("")};this.extract={point:function(point){return point.x+this.delimiter+point.y},multipoint:function(multipoint){var i, +parts=[];for(i=0;i + */ +var Wkt = (function () { // Execute function immediately + + return { + // The default delimiter for separating components of atomic geometry (coordinates) + delimiter: ' ', + + isArray: function (obj) { + return !!(obj && obj.constructor == Array); + }, + + /** + * An object for reading WKT strings and writing geographic features + * @param {String} An optional WKT string for immediate read + * @param {Wkt.Wkt} A WKT object + */ + Wkt: function (initializer) { + var beginsWith, endsWith, trim; + + /** + * @private + */ + beginsWith = function (str, sub) { + return str.substring(0, sub.length) === sub; + }; + + /** + * @private + */ + endsWith = function (str, sub) { + return str.substring(str.length - sub.length) === sub; + }; + + /** + * @private + */ + trim = function (str, sub) { + sub = sub || ' '; // Defaults to trimming spaces + // Trim beginning spaces + while (beginsWith(str, sub)) { + str = str.substring(1); + } + // Trim ending spaces + while (endsWith(str, sub)) { + str = str.substring(0, str.length - 1); + } + return str; + }; + + /** + * The default delimiter between X and Y coordinates. + */ + this.delimiter = Wkt.delimiter; + + /** + * Some regular expressions copied from OpenLayers.Format.WKT.js + */ + this.regExes = { + 'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/, + 'spaces': /\s+|\+/, // Matches the '+' or the empty space + 'numeric': /-*\d+\.*\d+/, + 'comma': /\s*,\s*/, + 'parenComma': /\)\s*,\s*\(/, + 'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, + 'trimParens': /^\s*\(?(.*?)\)?\s*$/ + }; + + /** + * Returns true if the internal geometry is a collection of geometries. + * @return {Boolean} Returns true when it is a collection + */ + this.isCollection = function () { + switch (this.type.slice(0, 5)) { + case 'multi': + // Trivial; any multi-geometry is a collection + return true; + case 'polyg': + // Polygons with holes are "collections" of rings + return true; + default: + // Any other geometry is not a collection + return false; + } + }; + + /** + * The internal representation of geometry--the "components" of geometry. + */ + this.components = undefined; + + /** + * Sets internal geometry (components) from framework geometry (e.g. + * Google Polygon objects or google.maps.Polygon). + * @param obj {Object} The framework-dependent geometry representation + * @return {Wkt.Wkt} The object itself + */ + this.fromObject = function (obj) { + var result = this.deconstruct.call(this, obj); + this.components = result.components; + this.isRectangle = result.isRectangle || false; + this.type = result.type; + return this; + }; + + /** + * Creates external geometry objects based on a plug-in framework's + * construction methods and available geometry classes. + * @param config {Object} An optional framework-dependent properties specification + * @return {Object} The framework-dependent geometry representation + */ + this.toObject = function (config) { + return this.construct[this.type].call(this, config); + }; + + /** + * Reads a WKT string, validating and incorporating it. + * @param wkt {String} A WKT string + * @return {Array} An Array of internal geometry objects + */ + this.read = function (wkt) { + var matches; + matches = this.regExes.typeStr.exec(wkt); + if (matches) { + this.type = matches[1].toLowerCase(); + this.base = matches[2]; + if (this.ingest[this.type]) { + this.components = this.ingest[this.type].apply(this, [this.base]); + } + } else { + console.log("Invalid WKT string provided to read()"); + throw { + name: "WKTError", + message: "Invalid WKT string provided to read()" + } + } + return this.components; + }; // eo readWkt + + /** + * Writes a WKT string. + * @param components {Array} An Array of internal geometry objects + * @return {String} The corresponding WKT representation + */ + this.write = function (components) { + var i, pieces, data; + + components = components || this.components; + + pieces = []; + + pieces.push(this.type.toUpperCase() + '('); + + for (i = 0; i < components.length; i += 1) { + if (this.isCollection() && i > 0) { + pieces.push(','); + } + + // There should be an extract function for the named type + if (!this.extract[this.type]) { + return null; + } + + data = this.extract[this.type].apply(this, [components[i]]); + if (this.isCollection()) { + pieces.push('(' + data + ')'); + } else { + pieces.push(data); + // If not at the end of the components, add a comma + if (i !== components.length - 1) { + pieces.push(','); + } + } + } + + pieces.push(')'); + + return pieces.join(''); + }; + + /** + * This object contains functions as property names that extract WKT + * strings from the internal representation. + */ + this.extract = { + /** + * Return a WKT string representing atomic (point) geometry + * @param point {Object} An object with x and y properties + * @return {String} The WKT representation + */ + 'point': function (point) { + return point.x + this.delimiter + point.y; + }, + /** + * Return a WKT string representing multiple atoms (points) + * @param point {Array} Multiple x-and-y objects + * @return {String} The WKT representation + */ + 'multipoint': function (multipoint) { + var i, parts = []; + for (i = 0; i < multipoint.length; i += 1) { + parts.push(this.extract.point.apply(this, [multipoint[i]])); + } + return parts.join(','); + }, + /** + * Return a WKT string representing a chain (linestring) of atoms + * @param point {Array} Multiple x-and-y objects + * @return {String} The WKT representation + */ + 'linestring': function (linestring) { + // Extraction of linestrings is the same as for points + return this.extract.point.apply(this, [linestring]); + }, + /** + * Return a WKT string representing multiple chains (multilinestring) of atoms + * @param point {Array} Multiple of multiple x-and-y objects + * @return {String} The WKT representation + */ + 'multilinestring': function (multilinestring) { + var i, parts = []; + for (i = 0; i < multilinestring.length; i += 1) { + parts.push('(' + this.extract.linestring.apply(this, [multilinestring[i]]) + ')'); + } + return parts.join(','); + }, + /** + * Return a WKT string representing multiple atoms in closed series (polygon) + * @param point {Array} Collection of ordered x-and-y objects + * @return {String} The WKT representation + */ + 'polygon': function (polygon) { + // Extraction of polygons is the same as for multipoints + return this.extract.multipoint.apply(this, [polygon]); + }, + /** + * Return a WKT string representing multiple closed series (multipolygons) of multiple atoms + * @param point {Array} Collection of ordered x-and-y objects + * @return {String} The WKT representation + */ + 'multipolygon': function (multipolygon) { + var i, parts = []; + for (i = 0; i < multipolygon.length; i += 1) { + parts.push('(' + this.extract.polygon.apply(this, [multipolygon[i]]) + ')'); + } + return parts.join(','); + } + }; + + /** + * This object contains functions as property names that ingest WKT + * strings into the internal representation. + */ + this.ingest = { + + /** + * Return point feature given a point WKT fragment. + * @param str {String} A WKT fragment representing the point + */ + 'point': function (str) { + var coords = trim(str).split(this.regExes.spaces); + // In case a parenthetical group of coordinates is passed... + return [{ // ...Search for numeric substrings + x: parseFloat(this.regExes.numeric.exec(coords[0])[0]), + y: parseFloat(this.regExes.numeric.exec(coords[1])[0]) + }]; + }, + + /** + * Return a multipoint feature given a multipoint WKT fragment. + * @param str {String} A WKT fragment representing the multipoint + */ + 'multipoint': function (str) { + var i, components, points; + components = []; + points = trim(str).split(this.regExes.comma); + for (i = 0; i < points.length; i += 1) { + components.push(this.ingest.point.apply(this, [points[i]])); + } + return components; + }, + + /** + * Return a linestring feature given a linestring WKT fragment. + * @param str {String} A WKT fragment representing the linestring + */ + 'linestring': function (str) { + var i, multipoints, components; + + // In our x-and-y representation of components, parsing + // multipoints is the same as parsing linestrings + multipoints = this.ingest.multipoint.apply(this, [str]); + + // However, the points need to be joined + components = []; + for (i = 0; i < multipoints.length; i += 1) { + components = components.concat(multipoints[i]); + } + return components; + }, + + /** + * Return a multilinestring feature given a multilinestring WKT fragment. + * @param str {String} A WKT fragment representing the multilinestring + */ + 'multilinestring': function (str) { + var i, components, line, lines; + components = []; + lines = trim(str).split(this.regExes.parenComma); + for (i = 0; i < lines.length; i += 1) { + line = lines[i].replace(this.regExes.trimParens, '$1'); + components.push(this.ingest.linestring.apply(this, [line])); + } + return components; + }, + + /** + * Return a polygon feature given a polygon WKT fragment. + * @param str {String} A WKT fragment representing the polygon + */ + 'polygon': function (str) { + var i, j, components, subcomponents, ring, rings; + rings = trim(str).split(this.regExes.parenComma); + components = []; // Holds one or more rings + for (i = 0; i < rings.length; i += 1) { + ring = rings[i].replace(this.regExes.trimParens, '$1').split(this.regExes.comma); + subcomponents = []; // Holds the outer ring and any inner rings (holes) + for (j = 0; j < ring.length; j += 1) { + // Split on the empty space or '+' character (between coordinates) + subcomponents.push({ + x: parseFloat(ring[j].split(this.regExes.spaces)[0]), + y: parseFloat(ring[j].split(this.regExes.spaces)[1]) + }); + } + components.push(subcomponents); + } + return components; + }, + + /** + * Return a multipolygon feature given a multipolygon WKT fragment. + * @param str {String} A WKT fragment representing the multipolygon + */ + 'multipolygon': function (str) { + var i, components, polygon, polygons; + components = []; + polygons = trim(str).split(this.regExes.doubleParenComma); + for (i = 0; i < polygons.length; i += 1) { + polygon = polygons[i].replace(this.regExes.trimParens, '$1'); + components.push(this.ingest.polygon.apply(this, [polygon])); + } + return components; + }, + + /** + * Return an array of features given a geometrycollection WKT fragment. + * @param str {String} A WKT fragment representing the geometry collection + */ + 'geometrycollection': function (str) { + console.log('The geometrycollection WKT type is not yet supported.'); + } + + }; // eo ingest + + // An initial WKT string may be provided + if (initializer && typeof initializer === 'string') { + this.read(initializer); + } else if (this.fromGeometry) { // Or, an initial geometry object to be read + this.fromGeometry(initializer); + } + + } // eo WKt.Wkt + + }; // eo return + +}()); // eo Wkt -- libgit2 0.21.2