Commit 5ef9f97502ab9534661b2aecbedd35fb29143348
1 parent
bc67f98a
Exists in
master
and in
7 other branches
Inclusão da biblioteca wicket em pacotes
Showing
14 changed files
with
1366 additions
and
0 deletions
Show diff stats
... | ... | @@ -0,0 +1,123 @@ |
1 | +########## | |
2 | +# Wicket # | |
3 | +########## | |
4 | + | |
5 | +Updated **April 8, 2012** by K. Arthur Endsley | |
6 | + | |
7 | +################ | |
8 | +## Motivation ## | |
9 | +################ | |
10 | + | |
11 | +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. | |
12 | + | |
13 | +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. | |
14 | + | |
15 | +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). | |
16 | + | |
17 | +############## | |
18 | +## Colophon ## | |
19 | +############## | |
20 | + | |
21 | +######################## | |
22 | +### Acknowledgements ### | |
23 | +######################## | |
24 | + | |
25 | +The following open sources were borrowed from; they retain all their original rights: | |
26 | + | |
27 | +* The OpenLayers 2.7 WKT module (OpenLayers.Format.WKT) | |
28 | +* 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) | |
29 | +* Charles R. Schmidt's and the Python Spatial Analysis Laboratory's (PySAL) WKT writer | |
30 | + | |
31 | +################### | |
32 | +### Conventions ### | |
33 | +################### | |
34 | + | |
35 | +The conventions I've adopted in writing this library: | |
36 | + | |
37 | +* The Crockford-ian module pattern with a single global (namespace) variable | |
38 | +* 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()) | |
39 | +* The namespace is the only name beginning with a capital letter that doesn't need to and shouldn't be preceded by new | |
40 | +* The namespace is the result of a function allowing for private members | |
41 | +* Tricky operators (++ and --) and type-coercing operators (== and !=) are not used | |
42 | + | |
43 | +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. | |
44 | + | |
45 | +############## | |
46 | +## Concepts ## | |
47 | +############## | |
48 | + | |
49 | +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) | |
50 | + | |
51 | + [ {x: -83.123, y: 42.123} ] | |
52 | + | |
53 | +An Array of multiple points (an Array of Arrays) specifies a "collection" of points (i.e. a MULTIPOINT feature): | |
54 | + | |
55 | + [ | |
56 | + [ {x: -83.123, y: 42.123} ], | |
57 | + [ {x: -83.234, y: 42.234} ] | |
58 | + ] | |
59 | + | |
60 | +An Array of multiple coordinates specifies a collection of connected points in an ordered sequence (i.e. LINESTRING feature): | |
61 | + | |
62 | + [ | |
63 | + {x: -83.12, y: 42.12}, | |
64 | + {x: -83.23, y: 42.23}, | |
65 | + {x: -83.34, y: 42.34} | |
66 | + ] | |
67 | + | |
68 | +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): | |
69 | + | |
70 | + [ | |
71 | + [ | |
72 | + {x: -83, y: 42}, | |
73 | + {x: -83, y: 43}, | |
74 | + {x: -82, y: 43}, | |
75 | + {x: -82, y: 42}, | |
76 | + {x: -83, y: 42} | |
77 | + ] | |
78 | + ] | |
79 | + | |
80 | +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. | |
81 | + | |
82 | +Similarly, multiple nested Arrays might reprsent a MULTIPOLYGON feature: | |
83 | + | |
84 | + [ | |
85 | + [ | |
86 | + [ | |
87 | + {x: -83, y: 42}, | |
88 | + {x: -83, y: 43}, | |
89 | + {x: -82, y: 43}, | |
90 | + {x: -82, y: 42}, | |
91 | + {x: -83, y: 42} | |
92 | + ] | |
93 | + ], | |
94 | + [ | |
95 | + [ | |
96 | + {x: -70, y: 40}, | |
97 | + {x: -70, y: 41}, | |
98 | + {x: -69, y: 41}, | |
99 | + {x: -69, y: 40}, | |
100 | + {x: -70, y: 40} | |
101 | + ] | |
102 | + ] | |
103 | + ] | |
104 | + | |
105 | +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): | |
106 | + | |
107 | + [ | |
108 | + [ | |
109 | + {x: 35, y: 10}, | |
110 | + {x: 10, y: 20}, | |
111 | + {x: 15, y: 40}, | |
112 | + {x: 45, y: 45}, | |
113 | + {x: 35, y: 10} | |
114 | + ], | |
115 | + [ | |
116 | + {x: 20, y: 30}, | |
117 | + {x: 35, y: 35}, | |
118 | + {x: 30, y: 20}, | |
119 | + {x: 20, y: 30} | |
120 | + ] | |
121 | + ] | |
122 | + | |
123 | +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. | ... | ... |
1.27 KB
... | ... | @@ -0,0 +1,81 @@ |
1 | +body {margin:0;padding:0;background:#CF6;border-top:5px solid #000;font-family:Helvetica,Arial,sans-serif;font-size:14px;} | |
2 | +img {border:none;} | |
3 | +label {color:#666;} | |
4 | +label:hover {color:#333;} | |
5 | +a:link, | |
6 | +a:visited {color:#460;text-decoration:none;} | |
7 | + | |
8 | +.attribute {float:right;padding:10px 0;} | |
9 | +.menu {float:left;margin:0;padding:0;list-style:none;} | |
10 | +.menu a {display:inline-block;margin:0;padding:5px 0 10px;margin:0 25px 0 0;border-top:5px solid transparent;} | |
11 | +.menu a:hover, | |
12 | +.menu a:active {border-top:5px solid #999;color:#000;text-decoration:none;} | |
13 | +.text {color:#333;} | |
14 | +.wrapper {width:1000px;height:100%;margin:0 auto;} | |
15 | + | |
16 | +#canvas {width:698px;height:100%;background:#AAA;border-left:1px solid #999;border-right:1px solid #999;} | |
17 | +#canvas, | |
18 | +#controls {float:left;} | |
19 | +#controls {width:290px;height:100%;padding:0 0 0 10px;text-align:justify;background:transparent;position:relative;top:-80px;} | |
20 | +#controls .text {margin:90px 0 0;} | |
21 | +#controls a:hover, | |
22 | +#controls a:active {text-decoration:underline;} | |
23 | +#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;} | |
24 | +#form {margin:10px 0 0;font-size:14px;color:#666;font-family:CabinItalic,sans-serif;} | |
25 | +#form #wkt {width:100%;height:150px;border:1px solid #999;padding:3px;resize:none;} | |
26 | +#form #urlify {vertical-align:baseline;margin:10px 5px 0 0;} | |
27 | +#form #reset {margin:10px 10px 0 0; | |
28 | + filter: progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc'); | |
29 | + -ms-filter: "progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc')"; | |
30 | + background-image: -moz-linear-gradient(top, #eeeeee, #cccccc); | |
31 | + background-image: -ms-linear-gradient(top, #eeeeee, #cccccc); | |
32 | + background-image: -o-linear-gradient(top, #eeeeee, #cccccc); | |
33 | + background-image: -webkit-gradient(linear, center top, center bottom, from(#eeeeee), to(#cccccc)); | |
34 | + background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc); | |
35 | + background-image: linear-gradient(top, #eeeeee, #cccccc); | |
36 | + -moz-background-clip: padding; | |
37 | + -webkit-background-clip: padding-box; | |
38 | + background-clip: padding-box; | |
39 | + } | |
40 | +#form #reset, | |
41 | +#form #submit {float:right;height:30px;margin-top:10px;padding:0 5px 2px 5px;font-family:CabinItalic,sans-serif;font-size:16px;color:#666; | |
42 | + border: 1px solid #999999; | |
43 | + -moz-border-radius: 5px; | |
44 | + -webkit-border-radius: 5px; | |
45 | + border-radius: 5px; | |
46 | + } | |
47 | +#form #submit {background-color: #EF9; | |
48 | + filter: progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeffcc', endColorstr = '#ddff99'); | |
49 | + -ms-filter: "progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeffcc', endColorstr = '#ddff99')"; | |
50 | + background-image: -moz-linear-gradient(top, #eeffcc, #ddff99); | |
51 | + background-image: -ms-linear-gradient(top, #eeffcc, #ddff99); | |
52 | + background-image: -o-linear-gradient(top, #eeffcc, #ddff99); | |
53 | + background-image: -webkit-gradient(linear, center top, center bottom, from(#eeffcc), to(#ddff99)); | |
54 | + background-image: -webkit-linear-gradient(top, #eeffcc, #ddff99); | |
55 | + background-image: linear-gradient(top, #eeffcc, #ddff99); | |
56 | + -moz-background-clip: padding; | |
57 | + -webkit-background-clip: padding-box; | |
58 | + background-clip: padding-box; | |
59 | + } | |
60 | +#form #reset:hover, | |
61 | +#form #submit:hover {color:#333;border-color:#666;text-shadow:1px 1px 0px #FFF; | |
62 | + -moz-box-shadow: 0px 0px 2px #999999; | |
63 | + -webkit-box-shadow: 0px 0px 2px #999999; | |
64 | + box-shadow: 0px 0px 2px #999999; | |
65 | + } | |
66 | +#form #reset:active, | |
67 | +#form #submit:active { | |
68 | + -moz-box-shadow:inset 0px 0px 2px #999999; | |
69 | + -webkit-box-shadow:inset 0px 0px 2px #999999; | |
70 | + box-shadow:inset 0px 0px 2px #999999; | |
71 | + } | |
72 | +#foot {background:transparent url(white_spacer.gif) repeat-x top left;} | |
73 | +#foot, | |
74 | +#head {width:100%;height:20%;color:#333;} | |
75 | +#head {background:transparent url(white_spacer.gif) repeat-x bottom left;} | |
76 | +#nav {width:698px;text-align:left;} | |
77 | +#ribbon {width:100%;height:60%;background:#EEE;} | |
78 | +#ribbon .wrapper {} | |
79 | +#ribbon .wrapper .title {float:right;width:300px;height:80px;font-family:CabinRegular,sans-serif;font-size:16px;color:#333;} | |
80 | +#ribbon .wrapper .title .brand {font-family:CabinMediumItalic,sans-serif;font-size:36px;color:#000;text-shadow:1px 1px 1px #FFF;} | |
81 | + | ... | ... |
... | ... | @@ -0,0 +1,272 @@ |
1 | +<html> | |
2 | +<head> | |
3 | + <link rel="stylesheet" type="text/css" href="/static/font/Cabin.css" /> | |
4 | + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | |
5 | + <!-- Conditional commenting for non-IE browsers --> | |
6 | + <!--[if !IE]><!--> | |
7 | + <link rel="stylesheet" type="text/css" href="index.css" /> | |
8 | + <!--<![endif]--> | |
9 | + <!-- Conditional commenting for IE 6.x --> | |
10 | + <!--[if IE]> | |
11 | + <link rel="stylesheet" type="text/css" href="index.ie.css" /> | |
12 | + <![endif]--> | |
13 | + <!--<link rel="stylesheet" href="../lib/leaflet.css" />--> | |
14 | + <!--[if lte IE 8]> | |
15 | + <link rel="stylesheet" href="../lib/leaflet.ie.css" /> | |
16 | + <![endif]--> | |
17 | +</head> | |
18 | +<title>Wicket - Lightweight Javascript for WKT [Sandbox]</title> | |
19 | +<!--<script src="../lib/leaflet.js"></script>--> | |
20 | +<script src="http://maps.google.com/maps/api/js?sensor=false&libraries=drawing" type="text/javascript"></script> | |
21 | +<script src="../wicket.js" type="text/javascript"></script> | |
22 | +<script src="../wicket-gmap3.js" type="text/javascript"></script> | |
23 | +<script type="text/javascript"> | |
24 | +var app = (function () { | |
25 | + return { | |
26 | + features: [], | |
27 | + /** | |
28 | + * Clears the map contents. | |
29 | + */ | |
30 | + clearMap: function () { | |
31 | + var i; | |
32 | + for (i in this.features) { | |
33 | + if (this.features.hasOwnProperty(i)) { | |
34 | + this.features[i].setMap(null); | |
35 | + } | |
36 | + } | |
37 | + this.features.length = 0; | |
38 | + }, | |
39 | + /** | |
40 | + * Clears the current contents of the textarea. | |
41 | + */ | |
42 | + clearText: function () { | |
43 | + document.getElementById('wkt').value = ''; | |
44 | + }, | |
45 | + /** | |
46 | + * Maps the current contents of the textarea. | |
47 | + * @return {Object} Some sort of geometry object | |
48 | + */ | |
49 | + mapIt: function () { | |
50 | + var el, obj, wkt; | |
51 | + | |
52 | + el = document.getElementById('wkt'); | |
53 | + wkt = new Wkt.Wkt(); | |
54 | + | |
55 | + if (el.last === el.value) { // Remember the last string | |
56 | + return; // Do nothing if the WKT string hasn't changed | |
57 | + } else { | |
58 | + el.last = el.value; | |
59 | + } | |
60 | + | |
61 | + try { // Catch any malformed WKT strings | |
62 | + wkt.read(el.value); | |
63 | + } catch (e1) { | |
64 | + try { | |
65 | + wkt.read(el.value.replace('\n', '').replace('\r', '').replace('\t', '')); | |
66 | + } catch (e2) { | |
67 | + if (e2.name === 'WKTError') { | |
68 | + alert('Wicket could not understand the WKT string you entered. Check that you have parentheses balanced, and try removing tabs and newline characters.'); | |
69 | + return; | |
70 | + } | |
71 | + } | |
72 | + } | |
73 | + | |
74 | + obj = wkt.toObject(this.gmap.defaults); // Make an object | |
75 | + | |
76 | + // Add listeners for overlay editing events | |
77 | + if (wkt.type === 'polygon' || wkt.type === 'linestring') { | |
78 | + // New vertex is inserted | |
79 | + google.maps.event.addListener(obj.getPath(), 'insert_at', function (n) { | |
80 | + app.updateText(); | |
81 | + }); | |
82 | + // Existing vertex is removed (insertion is undone) | |
83 | + google.maps.event.addListener(obj.getPath(), 'remove_at', function (n) { | |
84 | + app.updateText(); | |
85 | + }); | |
86 | + // Existing vertex is moved (set elsewhere) | |
87 | + google.maps.event.addListener(obj.getPath(), 'set_at', function (n) { | |
88 | + app.updateText(); | |
89 | + }); | |
90 | + } else { | |
91 | + if (obj.setEditable) {obj.setEditable(false);} | |
92 | + } | |
93 | + | |
94 | + if (Wkt.isArray(obj)) { // Distinguish multigeometries (Arrays) from objects | |
95 | + for (i in obj) { | |
96 | + if (obj.hasOwnProperty(i) && !Wkt.isArray(obj[i])) { | |
97 | + obj[i].setMap(this.gmap); | |
98 | + this.features.push(obj[i]); | |
99 | + } | |
100 | + } | |
101 | + } else { | |
102 | + obj.setMap(this.gmap); // Add it to the map | |
103 | + this.features.push(obj); | |
104 | + } | |
105 | + return obj; | |
106 | + }, | |
107 | + /** | |
108 | + * Updates the textarea based on the first available feature. | |
109 | + */ | |
110 | + updateText: function () { | |
111 | + var wkt = new Wkt.Wkt(); | |
112 | + wkt.fromObject(this.features[0]); | |
113 | + document.getElementById('wkt').value = wkt.write(); | |
114 | + }, | |
115 | + /** | |
116 | + * Formats the textarea contents for a URL. | |
117 | + * @param checked {Boolean} The check state of the associated checkbox | |
118 | + */ | |
119 | + urlify: function (checked) { | |
120 | + var wkt = new Wkt.Wkt(); | |
121 | + wkt.read(document.getElementById('wkt').value); | |
122 | + wkt.delimiter = (checked) ? '+' : ' '; | |
123 | + document.getElementById('wkt').value = wkt.write(); | |
124 | + return wkt; | |
125 | + }, | |
126 | + /** | |
127 | + * Application entry point. | |
128 | + * @return {<google.maps.Map>} The Google Maps API instance | |
129 | + */ | |
130 | + init: function () { | |
131 | + var gmap; | |
132 | + | |
133 | + gmap = new google.maps.Map(document.getElementById('canvas'), { | |
134 | + center: new google.maps.LatLng(30, 10), | |
135 | + defaults: { | |
136 | + icon: 'red_dot.png', | |
137 | + shadow: 'dot_shadow.png', | |
138 | + editable: true, | |
139 | + strokeColor: '#990000', | |
140 | + fillColor: '#EEFFCC', | |
141 | + fillOpacity: 0.6 | |
142 | + }, | |
143 | + disableDefaultUI: true, | |
144 | + mapTypeControl: true, | |
145 | + mapTypeId: google.maps.MapTypeId.ROADMAP, | |
146 | + mapTypeControlOptions: { | |
147 | + position: google.maps.ControlPosition.TOP_LEFT, | |
148 | + style: google.maps.MapTypeControlStyle.DROPDOWN_MENU | |
149 | + }, | |
150 | + panControl: false, | |
151 | + streetViewControl: false, | |
152 | + zoom: 2, | |
153 | + zoomControl: true, | |
154 | + zoomControlOptions: { | |
155 | + position: google.maps.ControlPosition.LEFT_TOP, | |
156 | + style: google.maps.ZoomControlStyle.SMALL | |
157 | + } | |
158 | + }); | |
159 | + | |
160 | + google.maps.event.addListener(gmap, 'tilesloaded', function () { | |
161 | + if (!this.loaded) { | |
162 | + this.loaded = true; | |
163 | + document.getElementById('wkt').value = 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 45 20, 30 5, 10 10, 10 30, 20 35), (30 20, 20 25, 20 15, 30 20)))'; | |
164 | + app.mapIt(); | |
165 | + } | |
166 | + }); | |
167 | + | |
168 | + gmap.drawingManager = new google.maps.drawing.DrawingManager({ | |
169 | + drawingControlOptions: { | |
170 | + position: google.maps.ControlPosition.TOP_CENTER, | |
171 | + drawingModes: [ | |
172 | + google.maps.drawing.OverlayType.MARKER, | |
173 | + google.maps.drawing.OverlayType.POLYLINE, | |
174 | + google.maps.drawing.OverlayType.POLYGON, | |
175 | + google.maps.drawing.OverlayType.RECTANGLE | |
176 | + ] | |
177 | + }, | |
178 | + markerOptions: gmap.defaults, | |
179 | + polygonOptions: gmap.defaults, | |
180 | + polylineOptions: gmap.defaults, | |
181 | + rectangleOptions: gmap.defaults | |
182 | + }); | |
183 | + gmap.drawingManager.setMap(gmap); | |
184 | + | |
185 | + google.maps.event.addListener(gmap.drawingManager, 'overlaycomplete', function (event) { | |
186 | + var wkt; | |
187 | + | |
188 | + app.clearText(); | |
189 | + app.clearMap(); | |
190 | + | |
191 | + // Set the drawing mode to "pan" (the hand) so users can immediately edit | |
192 | + this.setDrawingMode(null); | |
193 | + | |
194 | + // Polygon drawn | |
195 | + if (event.type === google.maps.drawing.OverlayType.POLYGON || event.type === google.maps.drawing.OverlayType.POLYLINE) { | |
196 | + // New vertex is inserted | |
197 | + google.maps.event.addListener(event.overlay.getPath(), 'insert_at', function (n) { | |
198 | + app.updateText(); | |
199 | + }); | |
200 | + | |
201 | + // Existing vertex is removed (insertion is undone) | |
202 | + google.maps.event.addListener(event.overlay.getPath(), 'remove_at', function (n) { | |
203 | + app.updateText(); | |
204 | + }); | |
205 | + | |
206 | + // Existing vertex is moved (set elsewhere) | |
207 | + google.maps.event.addListener(event.overlay.getPath(), 'set_at', function (n) { | |
208 | + app.updateText(); | |
209 | + }); | |
210 | + } else if (event.type === google.maps.drawing.OverlayType.RECTANGLE) { // Rectangle drawn | |
211 | + // Listen for the 'bounds_changed' event and update the geometry | |
212 | + google.maps.event.addListener(event.overlay, 'bounds_changed', function () { | |
213 | + app.updateText(); | |
214 | + }); | |
215 | + } | |
216 | + | |
217 | + app.features.push(event.overlay); | |
218 | + wkt = new Wkt.Wkt(); | |
219 | + wkt.fromObject(event.overlay); | |
220 | + document.getElementById('wkt').value = wkt.write(); | |
221 | + }); | |
222 | + | |
223 | + return gmap; | |
224 | + } | |
225 | + }; | |
226 | +}()); // Execute immediately | |
227 | +</script> | |
228 | +<body onload="app.gmap=app.init();"> | |
229 | +<body> | |
230 | + <a href="http://github.com/arthur-e/Wicket"> | |
231 | + <div id="forkme"> | |
232 | + </div> | |
233 | + </a> | |
234 | + <div id="head"> | |
235 | + <div class="wrapper"> | |
236 | + </div> | |
237 | + </div> | |
238 | + <div id="ribbon"> | |
239 | + <div class="wrapper"> | |
240 | + <div id="canvas"> | |
241 | + </div> | |
242 | + <div id="controls"> | |
243 | + <div class="title"> | |
244 | + <div class="brand">Wicket</div> | |
245 | + It whispers WKT in your application's ear. | |
246 | + </div> | |
247 | + <div class="text"> | |
248 | + Wicket is a lightweight Javascript library that reads and writes <a href="http://en.wikipedia.org/wiki/Well-known_text#Geometric_objects" target="_blaknk">Well-Known Text (WKT)</a> strings. It can also be extended to parse and to create geometric objects from various mapping frameworks, such as the Google Maps API. | |
249 | + </div> | |
250 | + <div id="form"> | |
251 | + <textarea type="text" name="wkt" id="wkt"></textarea> | |
252 | + <label><input type="checkbox" name="urlify" id="urlify" onchange="app.urlify(this.checked);" />Format for URLs</label> | |
253 | + <input type="submit" id="submit" value="Map It!" onclick="app.clearMap();app.mapIt();" /> | |
254 | + <input type="reset" id="reset" value="Clear Map" onclick="app.clearText();app.clearMap();" /> | |
255 | + </div> | |
256 | + </div> | |
257 | + </div> | |
258 | + </div> | |
259 | + <div id="foot"> | |
260 | + <div class="wrapper"> | |
261 | + <div class="menu" id="nav"> | |
262 | + <a href="/">Home</a> | |
263 | + <a href="mailto:kaendsle@mtu.edu">Contact Me</a> | |
264 | + <a href="http://github.com/arthur-e/Wicket">"Fork me on GitHub!"</a> | |
265 | + </div> | |
266 | + <div class="attribute"> | |
267 | + © 2012 K. Arthur Endsley | |
268 | + </div> | |
269 | + </div> | |
270 | + </div> | |
271 | +</body> | |
272 | +</html> | ... | ... |
... | ... | @@ -0,0 +1,81 @@ |
1 | +body {margin:0;padding:0;background:#CF6;border-top:5px solid #000;font-family:Helvetica,Arial,sans-serif;font-size:14px;} | |
2 | +img {border:none;} | |
3 | +label {color:#666;} | |
4 | +label:hover {color:#333;} | |
5 | +a:link, | |
6 | +a:visited {color:#460;text-decoration:none;} | |
7 | + | |
8 | +.attribute {float:right;padding:10px 0;} | |
9 | +.menu {float:left;margin:0;padding:0;list-style:none;} | |
10 | +.menu a {display:inline-block;margin:0;padding:5px 0 10px;margin:0 25px 0 0;border-top:5px solid transparent;} | |
11 | +.menu a:hover, | |
12 | +.menu a:active {border-top:5px solid #999;color:#000;text-decoration:none;} | |
13 | +.text {color:#333;} | |
14 | +.wrapper {width:1000px;height:100%;margin:0 auto;} | |
15 | + | |
16 | +#canvas {width:698px;height:100%;background:#AAA;border-left:1px solid #999;border-right:1px solid #999;} | |
17 | +#canvas, | |
18 | +#controls {float:left;} | |
19 | +#controls {width:290px;height:100%;padding:0 0 0 10px;text-align:justify;background:transparent;position:relative;top:-80px;} | |
20 | +#controls .text {margin:90px 0 0;} | |
21 | +#controls a:hover, | |
22 | +#controls a:active {text-decoration:underline;} | |
23 | +#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;} | |
24 | +#form {margin:10px 0 0;font-size:14px;color:#666;font-family:CabinItalic,sans-serif;} | |
25 | +#form #wkt {width:100%;height:150px;border:1px solid #999;padding:3px;resize:none;} | |
26 | +#form #urlify {vertical-align:baseline;margin:10px 5px 0 0;} | |
27 | +#form #reset {margin:10px 10px 0 0; | |
28 | + filter: progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc'); | |
29 | + -ms-filter: "progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc')"; | |
30 | + background-image: -moz-linear-gradient(top, #eeeeee, #cccccc); | |
31 | + background-image: -ms-linear-gradient(top, #eeeeee, #cccccc); | |
32 | + background-image: -o-linear-gradient(top, #eeeeee, #cccccc); | |
33 | + background-image: -webkit-gradient(linear, center top, center bottom, from(#eeeeee), to(#cccccc)); | |
34 | + background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc); | |
35 | + background-image: linear-gradient(top, #eeeeee, #cccccc); | |
36 | + -moz-background-clip: padding; | |
37 | + -webkit-background-clip: padding-box; | |
38 | + background-clip: padding-box; | |
39 | + } | |
40 | +#form #reset, | |
41 | +#form #submit {float:right;height:30px;margin-top:10px;padding:0 5px 2px 5px;font-family:CabinItalic,sans-serif;font-size:16px;color:#666; | |
42 | + border: 1px solid #999999; | |
43 | + -moz-border-radius: 5px; | |
44 | + -webkit-border-radius: 5px; | |
45 | + border-radius: 5px; | |
46 | + } | |
47 | +#form #submit {background-color: #EF9; | |
48 | + filter: progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeffcc', endColorstr = '#ddff99'); | |
49 | + -ms-filter: "progid: DXImageTransform.Microsoft.gradient(startColorstr = '#eeffcc', endColorstr = '#ddff99')"; | |
50 | + background-image: -moz-linear-gradient(top, #eeffcc, #ddff99); | |
51 | + background-image: -ms-linear-gradient(top, #eeffcc, #ddff99); | |
52 | + background-image: -o-linear-gradient(top, #eeffcc, #ddff99); | |
53 | + background-image: -webkit-gradient(linear, center top, center bottom, from(#eeffcc), to(#ddff99)); | |
54 | + background-image: -webkit-linear-gradient(top, #eeffcc, #ddff99); | |
55 | + background-image: linear-gradient(top, #eeffcc, #ddff99); | |
56 | + -moz-background-clip: padding; | |
57 | + -webkit-background-clip: padding-box; | |
58 | + background-clip: padding-box; | |
59 | + } | |
60 | +#form #reset:hover, | |
61 | +#form #submit:hover {color:#333;border-color:#666;text-shadow:1px 1px 0px #FFF; | |
62 | + -moz-box-shadow: 0px 0px 2px #999999; | |
63 | + -webkit-box-shadow: 0px 0px 2px #999999; | |
64 | + box-shadow: 0px 0px 2px #999999; | |
65 | + } | |
66 | +#form #reset:active, | |
67 | +#form #submit:active { | |
68 | + -moz-box-shadow:inset 0px 0px 2px #999999; | |
69 | + -webkit-box-shadow:inset 0px 0px 2px #999999; | |
70 | + box-shadow:inset 0px 0px 2px #999999; | |
71 | + } | |
72 | +#foot {background:transparent url(white_spacer.gif) repeat-x top left;} | |
73 | +#foot, | |
74 | +#head {width:100%;height:20%;color:#333;} | |
75 | +#head {background:transparent url(white_spacer.gif) repeat-x bottom left;} | |
76 | +#nav {width:698px;text-align:left;} | |
77 | +#ribbon {width:100%;height:60%;background:#EEE;} | |
78 | +#ribbon .wrapper {} | |
79 | +#ribbon .wrapper .title {float:right;width:300px;height:80px;font-family:CabinRegular,sans-serif;font-size:16px;color:#333;} | |
80 | +#ribbon .wrapper .title .brand {font-family:CabinMediumItalic,sans-serif;font-size:36px;color:#000;text-shadow:1px 1px 1px #FFF;} | |
81 | + | ... | ... |
1.16 KB
35 Bytes
... | ... | @@ -0,0 +1,8 @@ |
1 | +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"; | |
2 | +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;i<c.length;i+=1)arr.push(this.construct.point(config,c[i]));return arr},linestring:function(config,component){var i,c;c=component||this.components;config=config||{editable:false};config.path=[];for(i=0;i< | |
3 | +c.length;i+=1)config.path.push(new google.maps.LatLng(c[i].y,c[i].x));return new google.maps.Polyline(config)},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},polygon:function(config){var j,k,c,rings,verts;c=this.components;config=config||{editable:false};config.paths=[];rings=[];for(j=0;j<c.length;j+=1){verts=[];for(k=0;k<c[j].length;k+=1)verts.push(new google.maps.LatLng(c[j][k].y, | |
4 | +c[j][k].x));if(j!==0)verts.reverse();rings.push(verts)}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)},multipolygon:function(config){var i,j,k,c,rings,verts;c=this.components;config=config||{editable:false};config.paths=[];for(i=0;i<c.length;i+=1){rings=[];for(j=0;j<c[i].length;j+=1){verts=[];for(k=0;k<c[i][j].length;k+=1)verts.push(new google.maps.LatLng(c[i][j][k].y, | |
5 | +c[i][j][k].x));rings.push(verts)}config.paths=config.paths.concat(rings)}return new google.maps.Polygon(config)}}; | |
6 | +Wkt.Wkt.prototype.deconstruct=function(obj){var i,j,verts,rings,tmp;if(obj.getPosition&&typeof obj.getPosition==="function")return{type:"point",components:[{x:obj.getPosition().lng(),y:obj.getPosition().lat()}]};else if(obj.getPath&&!obj.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}}else if(obj.getPaths){rings=[];for(i=0;i<obj.getPaths().length;i+=1){tmp=obj.getPaths().getAt(i);verts= | |
7 | +[];for(j=0;j<obj.getPaths().getAt(i).length;j+=1)verts.push({x:tmp.getAt(j).lng(),y:tmp.getAt(j).lat()});verts.push({x:tmp.getAt(0).lng(),y:tmp.getAt(0).lat()});if(obj.getPaths().length>1)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(), | |
8 | +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; | ... | ... |
... | ... | @@ -0,0 +1,313 @@ |
1 | +/*global Wkt, google, document, window, console*/ | |
2 | +google.maps.Marker.prototype.type = 'marker'; | |
3 | +google.maps.Polyline.prototype.type = 'polyline'; | |
4 | +google.maps.Polygon.prototype.type = 'polygon'; | |
5 | +google.maps.Rectangle.prototype.type = 'rectangle'; | |
6 | +google.maps.Circle.prototype.type = 'circle'; | |
7 | + | |
8 | +/** | |
9 | + * An object of framework-dependent construction methods used to generate | |
10 | + * objects belonging to the various geometry classes of the framework. | |
11 | + */ | |
12 | +Wkt.Wkt.prototype.construct = { | |
13 | + /** | |
14 | + * Creates the framework's equivalent point geometry object. | |
15 | + * @param config {Object} An optional properties hash the object should use | |
16 | + * @param component {Object} An optional component to build from | |
17 | + * @return {google.maps.Marker} | |
18 | + */ | |
19 | + 'point': function (config, component) { | |
20 | + var c = component || this.components; | |
21 | + | |
22 | + config = config || {}; | |
23 | + | |
24 | + config.position = new google.maps.LatLng(c[0].y, c[0].x); | |
25 | + | |
26 | + return new google.maps.Marker(config); | |
27 | + }, | |
28 | + | |
29 | + /** | |
30 | + * Creates the framework's equivalent multipoint geometry object. | |
31 | + * @param config {Object} An optional properties hash the object should use | |
32 | + * @return {Array} Array containing multiple google.maps.Marker | |
33 | + */ | |
34 | + 'multipoint': function (config) { | |
35 | + var i, c, arr; | |
36 | + | |
37 | + c = this.components; | |
38 | + | |
39 | + config = config || {}; | |
40 | + | |
41 | + arr = []; | |
42 | + | |
43 | + for (i = 0; i < c.length; i += 1) { | |
44 | + arr.push(this.construct.point(config, c[i])); | |
45 | + } | |
46 | + | |
47 | + return arr; | |
48 | + }, | |
49 | + | |
50 | + /** | |
51 | + * Creates the framework's equivalent multipoint geometry object. | |
52 | + * @param config {Object} An optional properties hash the object should use | |
53 | + * @param component {Object} An optional component to build from | |
54 | + * @return {google.maps.Polyline} | |
55 | + */ | |
56 | + 'linestring': function (config, component) { | |
57 | + var i, c; | |
58 | + | |
59 | + c = component || this.components; | |
60 | + | |
61 | + config = config || { | |
62 | + editable: false | |
63 | + }; | |
64 | + | |
65 | + config.path = []; | |
66 | + | |
67 | + for (i = 0; i < c.length; i += 1) { | |
68 | + config.path.push(new google.maps.LatLng(c[i].y, c[i].x)); | |
69 | + } | |
70 | + | |
71 | + return new google.maps.Polyline(config); | |
72 | + }, | |
73 | + | |
74 | + /** | |
75 | + * Creates the framework's equivalent multilinestring geometry object. | |
76 | + * @param config {Object} An optional properties hash the object should use | |
77 | + * @return {Array} Array containing multiple google.maps.Polyline instances | |
78 | + */ | |
79 | + 'multilinestring': function (config) { | |
80 | + var i, c, arr; | |
81 | + | |
82 | + c = this.components; | |
83 | + | |
84 | + config = config || { | |
85 | + editable: false | |
86 | + }; | |
87 | + | |
88 | + config.path = []; | |
89 | + | |
90 | + arr = []; | |
91 | + | |
92 | + for (i = 0; i < c.length; i += 1) { | |
93 | + arr.push(this.construct.linestring(config, c[i])); | |
94 | + } | |
95 | + | |
96 | + return arr; | |
97 | + }, | |
98 | + | |
99 | + /** | |
100 | + * Creates the framework's equivalent polygon geometry object. | |
101 | + * @param config {Object} An optional properties hash the object should use | |
102 | + * @return {google.maps.Polygon} | |
103 | + */ | |
104 | + 'polygon': function (config) { | |
105 | + var j, k, c, rings, verts; | |
106 | + | |
107 | + c = this.components; | |
108 | + | |
109 | + config = config || { | |
110 | + editable: false // Editable geometry off by default | |
111 | + }; | |
112 | + | |
113 | + config.paths = []; | |
114 | + | |
115 | + rings = []; | |
116 | + for (j = 0; j < c.length; j += 1) { // For each ring... | |
117 | + | |
118 | + verts = []; | |
119 | + for (k = 0; k < c[j].length; k += 1) { // For each vertex... | |
120 | + verts.push(new google.maps.LatLng(c[j][k].y, c[j][k].x)); | |
121 | + | |
122 | + } // eo for each vertex | |
123 | + | |
124 | + if (j !== 0) { // Reverse the order of coordinates in inner rings | |
125 | + if (config.reverseInnerPolygons == null || config.reverseInnerPolygons) { | |
126 | + verts.reverse(); | |
127 | + } | |
128 | + } | |
129 | + | |
130 | + rings.push(verts); | |
131 | + } // eo for each ring | |
132 | + | |
133 | + config.paths = config.paths.concat(rings); | |
134 | + | |
135 | + if (this.isRectangle) { | |
136 | + console.log('Rectangles are not yet supported; set the isRectangle property to false (default).'); | |
137 | + } else { | |
138 | + return new google.maps.Polygon(config); | |
139 | + } | |
140 | + }, | |
141 | + | |
142 | + /** | |
143 | + * Creates the framework's equivalent multipolygon geometry object. | |
144 | + * @param config {Object} An optional properties hash the object should use | |
145 | + * @return {Array} Array containing multiple google.maps.Polygon | |
146 | + */ | |
147 | + 'multipolygon': function (config) { | |
148 | + var i, j, k, c, rings, verts; | |
149 | + | |
150 | + c = this.components; | |
151 | + | |
152 | + config = config || { | |
153 | + editable: false // Editable geometry off by default | |
154 | + }; | |
155 | + | |
156 | + config.paths = []; // Must ensure this property is available | |
157 | + | |
158 | + for (i = 0; i < c.length; i += 1) { // For each polygon... | |
159 | + | |
160 | + rings = []; | |
161 | + for (j = 0; j < c[i].length; j += 1) { // For each ring... | |
162 | + | |
163 | + verts = []; | |
164 | + for (k = 0; k < c[i][j].length; k += 1) { // For each vertex... | |
165 | + verts.push(new google.maps.LatLng(c[i][j][k].y, c[i][j][k].x)); | |
166 | + | |
167 | + } // eo for each vertex | |
168 | + | |
169 | +/* // This is apparently not needed in multipolygon cases | |
170 | + if (j !== 0) { // Reverse the order of coordinates in inner rings | |
171 | + verts.reverse(); | |
172 | + } | |
173 | +*/ | |
174 | + rings.push(verts); | |
175 | + } // eo for each ring | |
176 | + | |
177 | + config.paths = config.paths.concat(rings); | |
178 | + | |
179 | + } // eo for each polygon | |
180 | + | |
181 | + return new google.maps.Polygon(config); | |
182 | + } | |
183 | + | |
184 | +}; | |
185 | + | |
186 | +/** | |
187 | + * A framework-dependent deconstruction method used to generate internal | |
188 | + * geometric representations from instances of framework geometry. This method | |
189 | + * uses object detection to attempt to classify members of framework geometry | |
190 | + * classes into the standard WKT types. | |
191 | + * @param obj {Object} An instance of one of the framework's geometry classes | |
192 | + * @return {Object} A hash of the 'type' and 'components' thus derived | |
193 | + */ | |
194 | +Wkt.Wkt.prototype.deconstruct = function (obj) { | |
195 | + var i, j, verts, rings, tmp; | |
196 | + | |
197 | + // google.maps.Marker ////////////////////////////////////////////////////// | |
198 | + if (obj.getPosition && typeof obj.getPosition === 'function') { | |
199 | + // Only Markers, among all overlays, have the getPosition property | |
200 | + | |
201 | + return { | |
202 | + type: 'point', | |
203 | + components: [{ | |
204 | + x: obj.getPosition().lng(), | |
205 | + y: obj.getPosition().lat() | |
206 | + }] | |
207 | + }; | |
208 | + | |
209 | + // google.maps.Polyline //////////////////////////////////////////////////// | |
210 | + } else if (obj.getPath && !obj.getPaths) { | |
211 | + // Polylines have a single path (getPath) not paths (getPaths) | |
212 | + | |
213 | + verts = []; | |
214 | + for (i = 0; i < obj.getPath().length; i += 1) { | |
215 | + tmp = obj.getPath().getAt(i); | |
216 | + verts.push({ | |
217 | + x: tmp.lng(), | |
218 | + y: tmp.lat() | |
219 | + }); | |
220 | + } | |
221 | + | |
222 | + return { | |
223 | + type: 'linestring', | |
224 | + components: verts | |
225 | + }; | |
226 | + | |
227 | + // google.maps.Polygon ///////////////////////////////////////////////////// | |
228 | + } else if (obj.getPaths) { | |
229 | + // Polygon is the only class with the getPaths property | |
230 | + | |
231 | + // TODO Polygons with holes cannot be distinguished from multipolygons | |
232 | + rings = []; | |
233 | + for (i = 0; i < obj.getPaths().length; i += 1) { // For each polygon (ring)... | |
234 | + tmp = obj.getPaths().getAt(i); | |
235 | + | |
236 | + verts = []; | |
237 | + for (j = 0; j < obj.getPaths().getAt(i).length; j += 1) { // For each vertex... | |
238 | + verts.push({ | |
239 | + x: tmp.getAt(j).lng(), | |
240 | + y: tmp.getAt(j).lat() | |
241 | + }); | |
242 | + } | |
243 | + | |
244 | + verts.push({ // Add the first coordinate again for closure | |
245 | + x: tmp.getAt(0).lng(), | |
246 | + y: tmp.getAt(0).lat() | |
247 | + }); | |
248 | + | |
249 | + // Since we can't distinguish between single polygons with holes | |
250 | + // and multipolygons, we always create multipolygons | |
251 | + if (obj.getPaths().length > 1) { | |
252 | + verts = [verts]; // Wrap multipolygons once more (collection) | |
253 | + } | |
254 | + | |
255 | + rings.push(verts); | |
256 | + } | |
257 | + | |
258 | + return { | |
259 | + type: 'polygon', | |
260 | + components: rings | |
261 | + }; | |
262 | + | |
263 | + // google.maps.Rectangle /////////////////////////////////////////////////// | |
264 | + } else if (obj.getBounds && !obj.getRadius) { | |
265 | + // Rectangle is only overlay class with getBounds property and not getRadius property | |
266 | + | |
267 | + tmp = obj.getBounds(); | |
268 | + return { | |
269 | + type: 'polygon', | |
270 | + isRectangle: true, | |
271 | + components: [ | |
272 | + [ | |
273 | + { // NW corner | |
274 | + x: tmp.getSouthWest().lng(), | |
275 | + y: tmp.getNorthEast().lat() | |
276 | + }, | |
277 | + { // NE corner | |
278 | + x: tmp.getNorthEast().lng(), | |
279 | + y: tmp.getNorthEast().lat() | |
280 | + }, | |
281 | + { // SE corner | |
282 | + x: tmp.getNorthEast().lng(), | |
283 | + y: tmp.getSouthWest().lat() | |
284 | + }, | |
285 | + { // SW corner | |
286 | + x: tmp.getSouthWest().lng(), | |
287 | + y: tmp.getSouthWest().lat() | |
288 | + }, | |
289 | + { // NW corner (again, for closure) | |
290 | + x: tmp.getSouthWest().lng(), | |
291 | + y: tmp.getNorthEast().lat() | |
292 | + } | |
293 | + ] | |
294 | + ] | |
295 | + }; | |
296 | + | |
297 | + // google.maps.Circle ////////////////////////////////////////////////////// | |
298 | + } else if (obj.getBounds && obj.getRadius) { | |
299 | + // Circle is the only overlay class with both the getBounds and getRadius properties | |
300 | + | |
301 | + console.log('Deconstruction of google.maps.Circle objects is not yet supported'); | |
302 | + | |
303 | + } else { | |
304 | + console.log('The passed object does not have any recognizable properties.'); | |
305 | + } | |
306 | + | |
307 | +}; | |
308 | + | |
309 | +/** | |
310 | + * A framework-dependent flag, set for each Wkt.Wkt() instance, that indicates | |
311 | + * whether or not a closed polygon geometry should be interpreted as a rectangle. | |
312 | + */ | |
313 | +Wkt.Wkt.prototype.isRectangle = false; | ... | ... |
... | ... | @@ -0,0 +1,4 @@ |
1 | +Wkt.Wkt.prototype.isRectangle=false; | |
2 | +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;i<len;i++)layers.push(this.construct.point.call(this,config,coords[i]));return L.featureGroup(layers,config)},linestring:function(config,component){var coords=component||this.components,latlngs=this.coordsToLatLngs(coords); | |
3 | +return L.polyLine(latlngs)},multilinestring:function(config){var coords=this.components,latlngs=this.coordsToLatLngs(coords,1);return L.multiPolyline(latlngs)},polygon:function(config){var coords=this.components,latlngs=this.coordsToLatLngs(coords,1);return L.polygon(latlngs)},multipolygon:function(config){var coords=this.components,latlngs=this.coordsToLatLngs(coords,2);return L.multiPolygon(latlngs)}}; | |
4 | +L.Util.extend(Wkt.Wkt.prototype,{coordsToLatLngs:L.GeoJSON.coordsToLatLngs,coordsToLatLng:function(coords,reverse){var lat=reverse?coords.x:coords.y,lng=reverse?coords.y:coords.x;return L.latLng(lat,lng,true)}}); | ... | ... |
... | ... | @@ -0,0 +1,61 @@ |
1 | +Wkt.Wkt.prototype.isRectangle = false; | |
2 | + | |
3 | +Wkt.Wkt.prototype.construct = { | |
4 | + point: function (config, component) { | |
5 | + var coord = component || this.components; | |
6 | + if (coord instanceof Array) { | |
7 | + coord = coord[0]; | |
8 | + } | |
9 | + | |
10 | + return L.marker(this.coordsToLatLng(coord), config); | |
11 | + }, | |
12 | + | |
13 | + multipoint: function (config) { | |
14 | + var layers = [], | |
15 | + coords = this.components, | |
16 | + latlng; | |
17 | + | |
18 | + for (var i = 0, len = coords.length; i < len; i++) { | |
19 | + layers.push(this.construct.point.call(this, config, coords[i])); | |
20 | + } | |
21 | + | |
22 | + return L.featureGroup(layers, config); | |
23 | + }, | |
24 | + | |
25 | + linestring: function (config, component) { | |
26 | + var coords = component || this.components, | |
27 | + latlngs = this.coordsToLatLngs(coords); | |
28 | + | |
29 | + return L.polyLine(latlngs); | |
30 | + }, | |
31 | + | |
32 | + multilinestring: function (config) { | |
33 | + var coords = this.components, | |
34 | + latlngs = this.coordsToLatLngs(coords, 1); | |
35 | + | |
36 | + return L.multiPolyline(latlngs); | |
37 | + }, | |
38 | + | |
39 | + polygon: function (config) { | |
40 | + var coords = this.components, | |
41 | + latlngs = this.coordsToLatLngs(coords, 1); | |
42 | + return L.polygon(latlngs); | |
43 | + }, | |
44 | + | |
45 | + multipolygon: function (config) { | |
46 | + var coords = this.components, | |
47 | + latlngs = this.coordsToLatLngs(coords, 2); | |
48 | + | |
49 | + return L.multiPolygon(latlngs); | |
50 | + } | |
51 | +}; | |
52 | + | |
53 | +L.Util.extend(Wkt.Wkt.prototype, { | |
54 | + coordsToLatLngs: L.GeoJSON.coordsToLatLngs, | |
55 | + coordsToLatLng: function (coords, reverse) { | |
56 | + var lat = reverse ? coords.x : coords.y, | |
57 | + lng = reverse ? coords.y : coords.x; | |
58 | + | |
59 | + return L.latLng(lat, lng, true); | |
60 | + } | |
61 | +}); | ... | ... |
... | ... | @@ -0,0 +1,36 @@ |
1 | +// Examples are modified from: http://en.wikipedia.org/wiki/Well-known_text#Geometric_objects | |
2 | +var point, linestring, polygon, polygon2, multipoint, multipoint2, multilinestring, multipolygon, multipolygon2, runTests; | |
3 | + | |
4 | +point = 'POINT (30 10)'; | |
5 | + | |
6 | +linestring = 'LINESTRING (30 10, 10 30, 40 40)'; | |
7 | + | |
8 | +// A closed polygon without any holes | |
9 | +polygon = 'POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))'; | |
10 | + | |
11 | +// A polygon formatted for a URL (no spaces) | |
12 | +polygon2 = 'POLYGON((30+10,10+20,20+40,40+40,30+10))'; | |
13 | + | |
14 | +// A polygon with a hole in it | |
15 | +polygon3 = 'POLYGON ((35 10, 10 20, 15 40, 45 45, 35 10), (20 30, 35 35, 30 20, 20 30))'; | |
16 | + | |
17 | +// One way of describing multiple points | |
18 | +multipoint = 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'; | |
19 | + | |
20 | +// Another way of describing the same multiple points | |
21 | +multipoint2 = 'MULTIPOINT (10 40, 40 30, 20 20, 30 10)'; | |
22 | + | |
23 | +multilinestring = 'MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))'; | |
24 | + | |
25 | +// Two polygons without holes | |
26 | +multipolygon = 'MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))'; | |
27 | + | |
28 | +// A multipolygon formatted for a URL (no spaces) | |
29 | +multipolygon2 = 'MULTIPOLYGON(((30+20,10+40,45+40,30+20)),((15+5,40+10,10+20,5+10,15+5)))'; | |
30 | + | |
31 | +// One polygon with a hole, one without | |
32 | +multipolygon3 = 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 45 20, 30 5, 10 10, 10 30, 20 35), (30 20, 20 25, 20 15, 30 20)))'; | |
33 | + | |
34 | +runTests = function() { | |
35 | + var cases = [point, linestring, polygon, polygon2, multipoint, multipoint2, multilinestring, multipolygon, multipolygon2]; | |
36 | +} | ... | ... |
... | ... | @@ -0,0 +1,9 @@ |
1 | +var Wkt=function(){return{delimiter:" ",isArray:function(obj){return!!(obj&&obj.constructor==Array)},Wkt:function(initializer){var beginsWith,endsWith,trim;beginsWith=function(str,sub){return str.substring(0,sub.length)===sub};endsWith=function(str,sub){return str.substring(str.length-sub.length)===sub};trim=function(str,sub){sub=sub||" ";while(beginsWith(str,sub))str=str.substring(1);while(endsWith(str,sub))str=str.substring(0,str.length-1);return str};this.delimiter=Wkt.delimiter;this.regExes={typeStr:/^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/, | |
2 | +spaces:/\s+|\+/,numeric:/-*\d+\.*\d+/,comma:/\s*,\s*/,parenComma:/\)\s*,\s*\(/,doubleParenComma:/\)\s*\)\s*,\s*\(\s*\(/,trimParens:/^\s*\(?(.*?)\)?\s*$/};this.isCollection=function(){switch(this.type.slice(0,5)){case "multi":return true;case "polyg":return true;default:return false}};this.components=undefined;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};this.toObject= | |
3 | +function(config){return this.construct[this.type].call(this,config)};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};this.write=function(components){var i,pieces,data; | |
4 | +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(",");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, | |
5 | +parts=[];for(i=0;i<multipoint.length;i+=1)parts.push(this.extract.point.apply(this,[multipoint[i]]));return parts.join(",")},linestring:function(linestring){return this.extract.point.apply(this,[linestring])},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(",")},polygon:function(polygon){return this.extract.multipoint.apply(this,[polygon])},multipolygon:function(multipolygon){var i, | |
6 | +parts=[];for(i=0;i<multipolygon.length;i+=1)parts.push("("+this.extract.polygon.apply(this,[multipolygon[i]])+")");return parts.join(",")}};this.ingest={point:function(str){var coords=trim(str).split(this.regExes.spaces);return[{x:parseFloat(this.regExes.numeric.exec(coords[0])[0]),y:parseFloat(this.regExes.numeric.exec(coords[1])[0])}]},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, | |
7 | +[points[i]]));return components},linestring:function(str){var i,multipoints,components;multipoints=this.ingest.multipoint.apply(this,[str]);components=[];for(i=0;i<multipoints.length;i+=1)components=components.concat(multipoints[i]);return components},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, | |
8 | +[line]))}return components},polygon:function(str){var i,j,components,subcomponents,ring,rings;rings=trim(str).split(this.regExes.parenComma);components=[];for(i=0;i<rings.length;i+=1){ring=rings[i].replace(this.regExes.trimParens,"$1").split(this.regExes.comma);subcomponents=[];for(j=0;j<ring.length;j+=1)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},multipolygon:function(str){var i, | |
9 | +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},geometrycollection:function(str){console.log("The geometrycollection WKT type is not yet supported.")}};if(initializer&&typeof initializer==="string")this.read(initializer);else if(this.fromGeometry)this.fromGeometry(initializer)}}}(); | ... | ... |
... | ... | @@ -0,0 +1,378 @@ |
1 | +/*global console, document, window*/ | |
2 | +/** | |
3 | + * @author K. Arthur Endsley <arthur.endsley@gmail.com> | |
4 | + */ | |
5 | +var Wkt = (function () { // Execute function immediately | |
6 | + | |
7 | + return { | |
8 | + // The default delimiter for separating components of atomic geometry (coordinates) | |
9 | + delimiter: ' ', | |
10 | + | |
11 | + isArray: function (obj) { | |
12 | + return !!(obj && obj.constructor == Array); | |
13 | + }, | |
14 | + | |
15 | + /** | |
16 | + * An object for reading WKT strings and writing geographic features | |
17 | + * @param {String} An optional WKT string for immediate read | |
18 | + * @param {Wkt.Wkt} A WKT object | |
19 | + */ | |
20 | + Wkt: function (initializer) { | |
21 | + var beginsWith, endsWith, trim; | |
22 | + | |
23 | + /** | |
24 | + * @private | |
25 | + */ | |
26 | + beginsWith = function (str, sub) { | |
27 | + return str.substring(0, sub.length) === sub; | |
28 | + }; | |
29 | + | |
30 | + /** | |
31 | + * @private | |
32 | + */ | |
33 | + endsWith = function (str, sub) { | |
34 | + return str.substring(str.length - sub.length) === sub; | |
35 | + }; | |
36 | + | |
37 | + /** | |
38 | + * @private | |
39 | + */ | |
40 | + trim = function (str, sub) { | |
41 | + sub = sub || ' '; // Defaults to trimming spaces | |
42 | + // Trim beginning spaces | |
43 | + while (beginsWith(str, sub)) { | |
44 | + str = str.substring(1); | |
45 | + } | |
46 | + // Trim ending spaces | |
47 | + while (endsWith(str, sub)) { | |
48 | + str = str.substring(0, str.length - 1); | |
49 | + } | |
50 | + return str; | |
51 | + }; | |
52 | + | |
53 | + /** | |
54 | + * The default delimiter between X and Y coordinates. | |
55 | + */ | |
56 | + this.delimiter = Wkt.delimiter; | |
57 | + | |
58 | + /** | |
59 | + * Some regular expressions copied from OpenLayers.Format.WKT.js | |
60 | + */ | |
61 | + this.regExes = { | |
62 | + 'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/, | |
63 | + 'spaces': /\s+|\+/, // Matches the '+' or the empty space | |
64 | + 'numeric': /-*\d+\.*\d+/, | |
65 | + 'comma': /\s*,\s*/, | |
66 | + 'parenComma': /\)\s*,\s*\(/, | |
67 | + 'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, | |
68 | + 'trimParens': /^\s*\(?(.*?)\)?\s*$/ | |
69 | + }; | |
70 | + | |
71 | + /** | |
72 | + * Returns true if the internal geometry is a collection of geometries. | |
73 | + * @return {Boolean} Returns true when it is a collection | |
74 | + */ | |
75 | + this.isCollection = function () { | |
76 | + switch (this.type.slice(0, 5)) { | |
77 | + case 'multi': | |
78 | + // Trivial; any multi-geometry is a collection | |
79 | + return true; | |
80 | + case 'polyg': | |
81 | + // Polygons with holes are "collections" of rings | |
82 | + return true; | |
83 | + default: | |
84 | + // Any other geometry is not a collection | |
85 | + return false; | |
86 | + } | |
87 | + }; | |
88 | + | |
89 | + /** | |
90 | + * The internal representation of geometry--the "components" of geometry. | |
91 | + */ | |
92 | + this.components = undefined; | |
93 | + | |
94 | + /** | |
95 | + * Sets internal geometry (components) from framework geometry (e.g. | |
96 | + * Google Polygon objects or google.maps.Polygon). | |
97 | + * @param obj {Object} The framework-dependent geometry representation | |
98 | + * @return {Wkt.Wkt} The object itself | |
99 | + */ | |
100 | + this.fromObject = function (obj) { | |
101 | + var result = this.deconstruct.call(this, obj); | |
102 | + this.components = result.components; | |
103 | + this.isRectangle = result.isRectangle || false; | |
104 | + this.type = result.type; | |
105 | + return this; | |
106 | + }; | |
107 | + | |
108 | + /** | |
109 | + * Creates external geometry objects based on a plug-in framework's | |
110 | + * construction methods and available geometry classes. | |
111 | + * @param config {Object} An optional framework-dependent properties specification | |
112 | + * @return {Object} The framework-dependent geometry representation | |
113 | + */ | |
114 | + this.toObject = function (config) { | |
115 | + return this.construct[this.type].call(this, config); | |
116 | + }; | |
117 | + | |
118 | + /** | |
119 | + * Reads a WKT string, validating and incorporating it. | |
120 | + * @param wkt {String} A WKT string | |
121 | + * @return {Array} An Array of internal geometry objects | |
122 | + */ | |
123 | + this.read = function (wkt) { | |
124 | + var matches; | |
125 | + matches = this.regExes.typeStr.exec(wkt); | |
126 | + if (matches) { | |
127 | + this.type = matches[1].toLowerCase(); | |
128 | + this.base = matches[2]; | |
129 | + if (this.ingest[this.type]) { | |
130 | + this.components = this.ingest[this.type].apply(this, [this.base]); | |
131 | + } | |
132 | + } else { | |
133 | + console.log("Invalid WKT string provided to read()"); | |
134 | + throw { | |
135 | + name: "WKTError", | |
136 | + message: "Invalid WKT string provided to read()" | |
137 | + } | |
138 | + } | |
139 | + return this.components; | |
140 | + }; // eo readWkt | |
141 | + | |
142 | + /** | |
143 | + * Writes a WKT string. | |
144 | + * @param components {Array} An Array of internal geometry objects | |
145 | + * @return {String} The corresponding WKT representation | |
146 | + */ | |
147 | + this.write = function (components) { | |
148 | + var i, pieces, data; | |
149 | + | |
150 | + components = components || this.components; | |
151 | + | |
152 | + pieces = []; | |
153 | + | |
154 | + pieces.push(this.type.toUpperCase() + '('); | |
155 | + | |
156 | + for (i = 0; i < components.length; i += 1) { | |
157 | + if (this.isCollection() && i > 0) { | |
158 | + pieces.push(','); | |
159 | + } | |
160 | + | |
161 | + // There should be an extract function for the named type | |
162 | + if (!this.extract[this.type]) { | |
163 | + return null; | |
164 | + } | |
165 | + | |
166 | + data = this.extract[this.type].apply(this, [components[i]]); | |
167 | + if (this.isCollection()) { | |
168 | + pieces.push('(' + data + ')'); | |
169 | + } else { | |
170 | + pieces.push(data); | |
171 | + // If not at the end of the components, add a comma | |
172 | + if (i !== components.length - 1) { | |
173 | + pieces.push(','); | |
174 | + } | |
175 | + } | |
176 | + } | |
177 | + | |
178 | + pieces.push(')'); | |
179 | + | |
180 | + return pieces.join(''); | |
181 | + }; | |
182 | + | |
183 | + /** | |
184 | + * This object contains functions as property names that extract WKT | |
185 | + * strings from the internal representation. | |
186 | + */ | |
187 | + this.extract = { | |
188 | + /** | |
189 | + * Return a WKT string representing atomic (point) geometry | |
190 | + * @param point {Object} An object with x and y properties | |
191 | + * @return {String} The WKT representation | |
192 | + */ | |
193 | + 'point': function (point) { | |
194 | + return point.x + this.delimiter + point.y; | |
195 | + }, | |
196 | + /** | |
197 | + * Return a WKT string representing multiple atoms (points) | |
198 | + * @param point {Array} Multiple x-and-y objects | |
199 | + * @return {String} The WKT representation | |
200 | + */ | |
201 | + 'multipoint': function (multipoint) { | |
202 | + var i, parts = []; | |
203 | + for (i = 0; i < multipoint.length; i += 1) { | |
204 | + parts.push(this.extract.point.apply(this, [multipoint[i]])); | |
205 | + } | |
206 | + return parts.join(','); | |
207 | + }, | |
208 | + /** | |
209 | + * Return a WKT string representing a chain (linestring) of atoms | |
210 | + * @param point {Array} Multiple x-and-y objects | |
211 | + * @return {String} The WKT representation | |
212 | + */ | |
213 | + 'linestring': function (linestring) { | |
214 | + // Extraction of linestrings is the same as for points | |
215 | + return this.extract.point.apply(this, [linestring]); | |
216 | + }, | |
217 | + /** | |
218 | + * Return a WKT string representing multiple chains (multilinestring) of atoms | |
219 | + * @param point {Array} Multiple of multiple x-and-y objects | |
220 | + * @return {String} The WKT representation | |
221 | + */ | |
222 | + 'multilinestring': function (multilinestring) { | |
223 | + var i, parts = []; | |
224 | + for (i = 0; i < multilinestring.length; i += 1) { | |
225 | + parts.push('(' + this.extract.linestring.apply(this, [multilinestring[i]]) + ')'); | |
226 | + } | |
227 | + return parts.join(','); | |
228 | + }, | |
229 | + /** | |
230 | + * Return a WKT string representing multiple atoms in closed series (polygon) | |
231 | + * @param point {Array} Collection of ordered x-and-y objects | |
232 | + * @return {String} The WKT representation | |
233 | + */ | |
234 | + 'polygon': function (polygon) { | |
235 | + // Extraction of polygons is the same as for multipoints | |
236 | + return this.extract.multipoint.apply(this, [polygon]); | |
237 | + }, | |
238 | + /** | |
239 | + * Return a WKT string representing multiple closed series (multipolygons) of multiple atoms | |
240 | + * @param point {Array} Collection of ordered x-and-y objects | |
241 | + * @return {String} The WKT representation | |
242 | + */ | |
243 | + 'multipolygon': function (multipolygon) { | |
244 | + var i, parts = []; | |
245 | + for (i = 0; i < multipolygon.length; i += 1) { | |
246 | + parts.push('(' + this.extract.polygon.apply(this, [multipolygon[i]]) + ')'); | |
247 | + } | |
248 | + return parts.join(','); | |
249 | + } | |
250 | + }; | |
251 | + | |
252 | + /** | |
253 | + * This object contains functions as property names that ingest WKT | |
254 | + * strings into the internal representation. | |
255 | + */ | |
256 | + this.ingest = { | |
257 | + | |
258 | + /** | |
259 | + * Return point feature given a point WKT fragment. | |
260 | + * @param str {String} A WKT fragment representing the point | |
261 | + */ | |
262 | + 'point': function (str) { | |
263 | + var coords = trim(str).split(this.regExes.spaces); | |
264 | + // In case a parenthetical group of coordinates is passed... | |
265 | + return [{ // ...Search for numeric substrings | |
266 | + x: parseFloat(this.regExes.numeric.exec(coords[0])[0]), | |
267 | + y: parseFloat(this.regExes.numeric.exec(coords[1])[0]) | |
268 | + }]; | |
269 | + }, | |
270 | + | |
271 | + /** | |
272 | + * Return a multipoint feature given a multipoint WKT fragment. | |
273 | + * @param str {String} A WKT fragment representing the multipoint | |
274 | + */ | |
275 | + 'multipoint': function (str) { | |
276 | + var i, components, points; | |
277 | + components = []; | |
278 | + points = trim(str).split(this.regExes.comma); | |
279 | + for (i = 0; i < points.length; i += 1) { | |
280 | + components.push(this.ingest.point.apply(this, [points[i]])); | |
281 | + } | |
282 | + return components; | |
283 | + }, | |
284 | + | |
285 | + /** | |
286 | + * Return a linestring feature given a linestring WKT fragment. | |
287 | + * @param str {String} A WKT fragment representing the linestring | |
288 | + */ | |
289 | + 'linestring': function (str) { | |
290 | + var i, multipoints, components; | |
291 | + | |
292 | + // In our x-and-y representation of components, parsing | |
293 | + // multipoints is the same as parsing linestrings | |
294 | + multipoints = this.ingest.multipoint.apply(this, [str]); | |
295 | + | |
296 | + // However, the points need to be joined | |
297 | + components = []; | |
298 | + for (i = 0; i < multipoints.length; i += 1) { | |
299 | + components = components.concat(multipoints[i]); | |
300 | + } | |
301 | + return components; | |
302 | + }, | |
303 | + | |
304 | + /** | |
305 | + * Return a multilinestring feature given a multilinestring WKT fragment. | |
306 | + * @param str {String} A WKT fragment representing the multilinestring | |
307 | + */ | |
308 | + 'multilinestring': function (str) { | |
309 | + var i, components, line, lines; | |
310 | + components = []; | |
311 | + lines = trim(str).split(this.regExes.parenComma); | |
312 | + for (i = 0; i < lines.length; i += 1) { | |
313 | + line = lines[i].replace(this.regExes.trimParens, '$1'); | |
314 | + components.push(this.ingest.linestring.apply(this, [line])); | |
315 | + } | |
316 | + return components; | |
317 | + }, | |
318 | + | |
319 | + /** | |
320 | + * Return a polygon feature given a polygon WKT fragment. | |
321 | + * @param str {String} A WKT fragment representing the polygon | |
322 | + */ | |
323 | + 'polygon': function (str) { | |
324 | + var i, j, components, subcomponents, ring, rings; | |
325 | + rings = trim(str).split(this.regExes.parenComma); | |
326 | + components = []; // Holds one or more rings | |
327 | + for (i = 0; i < rings.length; i += 1) { | |
328 | + ring = rings[i].replace(this.regExes.trimParens, '$1').split(this.regExes.comma); | |
329 | + subcomponents = []; // Holds the outer ring and any inner rings (holes) | |
330 | + for (j = 0; j < ring.length; j += 1) { | |
331 | + // Split on the empty space or '+' character (between coordinates) | |
332 | + subcomponents.push({ | |
333 | + x: parseFloat(ring[j].split(this.regExes.spaces)[0]), | |
334 | + y: parseFloat(ring[j].split(this.regExes.spaces)[1]) | |
335 | + }); | |
336 | + } | |
337 | + components.push(subcomponents); | |
338 | + } | |
339 | + return components; | |
340 | + }, | |
341 | + | |
342 | + /** | |
343 | + * Return a multipolygon feature given a multipolygon WKT fragment. | |
344 | + * @param str {String} A WKT fragment representing the multipolygon | |
345 | + */ | |
346 | + 'multipolygon': function (str) { | |
347 | + var i, components, polygon, polygons; | |
348 | + components = []; | |
349 | + polygons = trim(str).split(this.regExes.doubleParenComma); | |
350 | + for (i = 0; i < polygons.length; i += 1) { | |
351 | + polygon = polygons[i].replace(this.regExes.trimParens, '$1'); | |
352 | + components.push(this.ingest.polygon.apply(this, [polygon])); | |
353 | + } | |
354 | + return components; | |
355 | + }, | |
356 | + | |
357 | + /** | |
358 | + * Return an array of features given a geometrycollection WKT fragment. | |
359 | + * @param str {String} A WKT fragment representing the geometry collection | |
360 | + */ | |
361 | + 'geometrycollection': function (str) { | |
362 | + console.log('The geometrycollection WKT type is not yet supported.'); | |
363 | + } | |
364 | + | |
365 | + }; // eo ingest | |
366 | + | |
367 | + // An initial WKT string may be provided | |
368 | + if (initializer && typeof initializer === 'string') { | |
369 | + this.read(initializer); | |
370 | + } else if (this.fromGeometry) { // Or, an initial geometry object to be read | |
371 | + this.fromGeometry(initializer); | |
372 | + } | |
373 | + | |
374 | + } // eo WKt.Wkt | |
375 | + | |
376 | + }; // eo return | |
377 | + | |
378 | +}()); // eo Wkt | ... | ... |