/src/com/google/maps/extras/arcgislink/ArcGISUtil.as
ActionScript | 430 lines | 275 code | 34 blank | 121 comment | 91 complexity | be83d3ba2faffd5fcad91e30e872bed5 MD5 | raw file
1/* 2 * ArcGIS for Google Maps Flash API 3 * 4 * License http://www.apache.org/licenses/LICENSE-2.0 5 */ /** 6 * @author nianwei at gmail dot com 7 */ 8package com.google.maps.extras.arcgislink { 9 10 import com.google.maps.*; 11 import com.google.maps.extras.arcgislink.json.*; 12 import com.google.maps.interfaces.IOverlay; 13 import com.google.maps.overlays.*; 14 15 import flash.events.*; 16 import flash.geom.Point; 17 import flash.net.*; 18 import flash.utils.*; 19 20 // import mx.rpc.events.*; 21 // import mx.rpc.http.*; 22 23 /** 24 * Utility tools 25 */ 26 public class ArcGISUtil { 27 public function ArcGISUtil() { 28 29 } 30 31 /** 32 * Helper method to convert an {@link ArcGISEnvelope} object to <code>GLatLngBounds</code> 33 * @param {Envelope} extent 34 * @return {GLatLngBounds} gLatLngBounds 35 */ 36 internal static function fromEnvelopeToLatLngBounds( /*Envelope*/ extent:*):LatLngBounds { 37 var sr:SpatialReference=SpatialReferences.getSpatialReference(extent.spatialReference.wkid); 38 sr=sr || SpatialReferences.WGS84; 39 var sw:Array=sr.reverse([extent.xmin, extent.ymin]); 40 var ne:Array=sr.reverse([extent.xmax, extent.ymax]); 41 return new LatLngBounds(new LatLng(sw[1], sw[0]), new LatLng(ne[1], ne[0])); 42 } 43 44 internal static function fromLatLngBoundsToEnvelope(gLatLngBounds:LatLngBounds, spatialReference:SpatialReference=null):* { 45 spatialReference=spatialReference || SpatialReferences.WGS84; 46 var sw:Array=spatialReference.forward([gLatLngBounds.getSouthWest().lng(), gLatLngBounds.getSouthWest().lat()]); 47 var ne:Array=spatialReference.forward([gLatLngBounds.getNorthEast().lng(), gLatLngBounds.getNorthEast().lat()]); 48 return {xmin: sw[0], ymin: sw[1], xmax: ne[0], ymax: ne[1], spatialReference: {wkid: spatialReference.wkid}}; 49 } 50 51 internal static function fromLatLngToPoint(gLatLng:LatLng, sr:SpatialReference=null):* { 52 sr=sr || SpatialReferences.WGS84; 53 var p:Array=sr.forward([gLatLng.lng(), gLatLng.lat()]); 54 return {x: p[0], y: p[1], spatialReference: {wkid: sr.wkid}}; 55 } 56 57 internal static function fromPointToLatLng(point:*, opt_sr:*=null):LatLng { 58 var srid:*=point.spatialReference || opt_sr; 59 var sr:SpatialReference=srid ? SpatialReferences.getSpatialReference(srid.wkid) : SpatialReferences.WGS84; 60 sr=sr || SpatialReferences.WGS84; 61 if (isNaN(point.x) || isNaN(point.y)) { 62 return null; 63 } 64 65 var p:Array=sr.reverse([point.x, point.y]); 66 return new LatLng(p[1], p[0]); 67 } 68 69 /** 70 * Add a ArcGIS Server resource to map. if it is cached, it will be added as a map type, if dynamic, it will be added as overlay. 71 * @param map 72 * @param url 73 * @param opt_callback 74 * 75 */ 76 public static function addArcGISMap(map:Map, url:String, opt_callback:Function=null):void { 77 var service:MapService=new MapService(url); 78 service.addEventListener(ServiceEvent.LOAD, function(evt:Event):void { 79 if (service.singleFusedMapCache) { 80 var tile:ArcGISTileLayer=new ArcGISTileLayer(service); 81 var type:ArcGISMapType=new ArcGISMapType([tile], new ArcGISMapTypeOptions({name: tile.getName().replace(/ /g, '\n')})); 82 map.addMapType(type); 83 if (opt_callback != null) { 84 opt_callback.call(null, type); 85 } 86 } else { 87 var ov:ArcGISMapOverlay=new ArcGISMapOverlay(service); 88 map.addOverlay(ov); 89 if (opt_callback != null) { 90 opt_callback.call(null, ov); 91 } 92 } 93 }); 94 } 95 96 /** 97 * Enable wheel zoom as alternative to Map.enableScrollWheelZoom because the later passed zoom=0 to Projection. 98 */ 99 internal static function enableScrollWheelZoom(map:com.google.maps.Map):void { 100 map.getDisplayObject().addEventListener(MouseEvent.MOUSE_WHEEL, function(evt:MouseEvent):void { 101 var p:flash.geom.Point=new flash.geom.Point(evt.localX, evt.localY); 102 var latlng:LatLng; 103 var c:flash.geom.Point=new flash.geom.Point(map.width / 2, map.height / 2); 104 var z:Number=map.getZoom(); 105 if (evt.delta > 0) { 106 z=Math.min(z + 1, map.getMaxZoomLevel()); 107 latlng=map.fromViewportToLatLng(new flash.geom.Point((p.x + c.x) / 2, (p.y + c.y) / 2)); 108 } else { 109 z=Math.max(z - 1, map.getMinZoomLevel()); 110 latlng=map.fromViewportToLatLng(new flash.geom.Point(c.x * 2 - p.x, c.y * 2 - p.y)); 111 } 112 map.setCenter(latlng, z); 113 }); 114 } 115 116 internal static var ArcGISConfig:*={maxPolyPoints: 3000, style: {icon: null, strokeStyle: {thickness: 3, color: 0xffff00, alpha: 0.5, pixelHinting: true}, fillStyle: {color: 0xFFFF00, alpha: 0.5} 117 118 }}; 119 120 /** 121 * Convert a {@link ArcGISFeature} or {@link ArcGISIdentifyResult} or {@link ArcGISFindResult} to core Google Maps API 122 * overlays such as {@link ArcGISGMarker}, 123 * {@link ArcGISGPolyline}, or {@link ArcGISGPolygon}s. 124 * Note ArcGIS Geometry may have multiple parts, but the coresponding GOverlay 125 * does not support multi-parts, so the result is an array. 126 * <ul><li><code>feature</code>: an object returned by ArcGIS Server with at least <code>geometry</code> property of type {@link ArcGISGeometry}. 127 * if it contains a name-value pair "attributes" property, it will be attached to the result overlays. 128 * <li><code>opt_sr</code>: optional {@link ArcGISSpatialReference}. Can be object literal. 129 * <li><code>opt_agsStyle</code> {@link ArcGISStyleOptions}. default is {@link ArcGISConfig}.style. 130 * <li><code>opt_displayName</code> optional field name used for title of feature. 131 * @param {Feature} feature 132 * @param {ArcGISSpatialReference} opt_sr 133 * @param {overlayOptions} opt_agsStyle 134 * @param {String} opt_displayName 135 * @return {GOverlay[]} 136 */ 137 138 internal static function fromGeometryToOverlays(geom:*, opt_sr:SpatialReference=null, overlayOptions:OverlayOptions=null, title:String=''):Array { 139 var ovs:Array=[]; 140 var sr:SpatialReference=null; 141 var ov:IOverlay=null; 142 overlayOptions=overlayOptions || new OverlayOptions(); 143 if (opt_sr) { 144 sr=opt_sr; 145 } else { 146 sr=SpatialReferences.getSpatialReference(geom.spatialReference.wkid); 147 } 148 if (sr === null) { 149 return ovs; 150 } 151 152 var x:String, i:int, ic:int, j:int, jc:int, parts:Array, part:Array, lnglat:Array, glatlngs:Array; 153 154 if (geom.x) { 155 //point 156 lnglat=sr.reverse([geom.x, geom.y]); 157 if (!overlayOptions.marker) { 158 overlayOptions.marker=new MarkerOptions({tooltip: title}); 159 } else if (!overlayOptions.marker.tooltip) { 160 overlayOptions.marker.tooltip=title; 161 } 162 ov=new Marker(new LatLng(lnglat[1], lnglat[0]), overlayOptions.marker); 163 ovs.push(ov); 164 } else { 165 //mulpt, line and poly 166 parts=geom.points || geom.paths || geom.rings; 167 if (!parts) { 168 return ovs; 169 } 170 for (i=0, ic=parts.length; i < ic; i++) { 171 part=parts[i]; 172 if (geom.points) { 173 // multipoint 174 lnglat=sr.reverse(part); 175 ov=new Marker(new LatLng(lnglat[1], lnglat[0]), overlayOptions.marker); 176 ovs.push(ov); 177 } else { 178 if (part.length > ArcGISConfig.maxPolyPoints) { 179 // TODO: do a simple point reduction 180 continue; 181 } 182 glatlngs=[]; 183 for (j=0, jc=part.length; j < jc; j++) { 184 lnglat=sr.reverse(part[j]); 185 glatlngs.push(new LatLng(lnglat[1], lnglat[0])); 186 } 187 if (geom.paths) { 188 ov=new Polyline(glatlngs, overlayOptions.polyline); //new PolylineOptions({strokeStyle: style.strokeStyle})); 189 ovs.push(ov); 190 } else if (geom.rings) { 191 overlayOptions.polygon=overlayOptions.polygon || new PolygonOptions(); 192 overlayOptions.polygon.tooltip=title; 193 if (ov == null) { 194 ov=new Polygon(glatlngs, overlayOptions.polygon); //new PolygonOptions({strokeStyle: style.strokeStyle, fillStyle: style.fillStyle, tooltip: title})); //, style.outlineColor, style.outlineWeight, style.outlineOpacity, style.fillColor, style.fillOpacity); 195 ovs.push(ov); 196 } else { 197 (ov as Polygon).setPolyline(i, glatlngs); 198 } 199 } 200 } 201 202 } 203 } 204 205 return ovs; 206 } 207 208 internal static function fromOverlaysToGeometry(ovs:Array, opt_sr:SpatialReference=null):* { 209 var sr:SpatialReference=null; 210 if (opt_sr) { 211 sr=opt_sr; 212 } else { 213 sr=SpatialReferences.WGS84; 214 } 215 var ov:*; 216 if (!ovs || ovs.length == 0) 217 return null; 218 ov=ovs[0]; 219 var x:String, i:int, ic:int, j:int, jc:int, parts:Array, part:Array, ll:LatLng, lnglat:Array; 220 if (ovs.length == 1 && (ov is LatLng || ov is Marker)) { 221 ll=(ov is LatLng ? ov as LatLng : (ov as Marker).getLatLng()); 222 lnglat=sr.forward([ll.lng(), ll.lat()]); 223 return {x: lnglat[0], y: lnglat[1], spatialReference: {wkid: sr.wkid}}; 224 } 225 parts=[]; 226 if (ov is LatLng) { 227 for (i=0; i < ovs.length; i++) { 228 ll=ovs[i] as LatLng; 229 parts.push(sr.forward([ll.lng(), ll.lat()])); 230 } 231 return {points: parts, spatialReference: {wkid: sr.wkid}}; 232 } else if (ov is Marker) { 233 for (i=0; i < ovs.length; i++) { 234 ll=(ovs[i] as Marker).getLatLng(); 235 parts.push(sr.forward([ll.lng(), ll.lat()])); 236 } 237 return {points: parts, spatialReference: {wkid: sr.wkid}}; 238 } else if (ov is Polyline || ov is Polygon) { 239 for (i=0; i < ovs.length; i++) { 240 part=[]; 241 //var line:Polyline = ovs[i] as Polyline; 242 ov=ovs[i]; 243 for (j=0; j < ov.getVertexCount(); j++) { 244 ll=ov.getVertex(j); 245 part.push(sr.forward([ll.lng(), ll.lat()])); 246 } 247 parts.push(part); 248 } 249 if (ov is Polyline) { 250 return {paths: parts, spatialReference: {wkid: sr.wkid}}; 251 } else { 252 return {rings: parts, spatialReference: {wkid: sr.wkid}}; 253 } 254 } 255 } 256 257 internal static function restRequest(url:String, params:Object, thisObj:IEventDispatcher, sucessFn:Function, failFn:Function=null):void { 258 var full:String=url + (url.indexOf('?') === -1 ? '?' : '&'); 259 /* 260 var service:HTTPService=new HTTPService(); 261 service.url=url; 262 service.addEventListener(FaultEvent.FAULT, function(evt:FaultEvent):void { 263 var err:ServiceError=new ServiceError({code: -1, message: evt.toString(), details: []}); 264 if (thisObj != null) { 265 thisObj.dispatchEvent(new ServiceEvent(ServiceEvent.ERROR, err)); 266 } 267 if (failFn != null) { 268 failFn(thisObj, err); 269 } 270 }); 271 service.addEventListener(ResultEvent.RESULT, function(evt:ResultEvent):void { 272 var jsons:String=evt.result as String; 273 var d:JSONDecoder=new JSONDecoder(jsons, false); // false strict to allow NAN in geocode results 274 var json:Object=d.getValue(); 275 if (json.error) { 276 var err:ServiceError=new ServiceError(json.error); 277 thisObj.dispatchEvent(new ServiceEvent(ServiceEvent.ERROR, err)); 278 if (failFn != null) { 279 failFn.call(thisObj, err); 280 } 281 } else if (sucessFn != null) { 282 sucessFn.call(thisObj, json); 283 } 284 }); 285 service.send(params); 286 */ 287 var variables:URLVariables=new URLVariables(); 288 if (params) { 289 for (var x:String in params) { 290 if (params.hasOwnProperty(x)) { 291 //full+=(x + '=' + escape(params[x]) + '&'); 292 variables[x]=params[x]; 293 } 294 } 295 } 296 297 var loader:URLLoader=new URLLoader(); 298 var request:URLRequest=new URLRequest(full); 299 request.method=URLRequestMethod.GET; //.POST; 300 request.data=variables; 301 302 loader.addEventListener(Event.COMPLETE, function(evt:Event):void { 303 var jsons:String=evt.target.data as String; 304 var d:JSONDecoder=new JSONDecoder(jsons, false); // false strict to allow NAN in geocode results 305 var json:Object=d.getValue(); 306 if (json.error) { 307 var err:ServiceError=new ServiceError(json.error); 308 thisObj.dispatchEvent(new ServiceEvent(ServiceEvent.ERROR, err)); 309 if (failFn != null) { 310 failFn.call(thisObj, err); 311 } 312 } else if (sucessFn != null) { 313 sucessFn.call(thisObj, json); 314 } 315 316 317 }); 318 loader.addEventListener(IOErrorEvent.IO_ERROR, function(evt:IOErrorEvent):void { 319 var err:ServiceError=new ServiceError({code: -1, message: evt.toString(), details: []}); 320 if (thisObj != null) { 321 thisObj.dispatchEvent(new ServiceEvent(ServiceEvent.ERROR, err)); 322 } 323 if (failFn != null) { 324 failFn(thisObj, err); 325 } 326 327 }); 328 loader.load(request); 329 330 } 331 332 /** 333 * Extract the substring from full string, between start string and end string 334 * @param {Object} full 335 * @param {Object} start 336 * @param {Object} end 337 */ 338 internal static function extractString(full:String, start:String, end:String):String { 339 var i:int=(start === '') ? 0 : full.indexOf(start); 340 var e:int=end === '' ? full.length : full.indexOf(end, i + start.length); 341 return full.substring(i + start.length, e); 342 } 343 344 internal static function augmentObject(src:*, dest:*, force:Boolean=false):Object { 345 if (src && dest) { 346 var p:String; 347 // used to count/check if it is a dyna class 348 // with properties that can be looped with for.. in 349 // In AS3, for..in can not be used to loop properties of a class instance except those 350 // dynamic added. That makes converting parameter classes into REST request harder. 351 var i:int=0; 352 for (p in src) { 353 i++; 354 if (src.hasOwnProperty(p)) { 355 if (force || !(p in dest) || (p in dest && !dest[p])) { 356 dest[p]=src[p]; 357 } 358 } 359 } 360 if (i == 0) { 361 var varList:XMLList=flash.utils.describeType(src)..variable; 362 for (i=0; i < varList.length(); i++) { 363 p=varList[i].@name; 364 if (src[p]) { 365 dest[p]=src[p]; 366 } 367 } 368 } 369 } 370 return dest; 371 } 372 373 internal static function isString(o:*):Boolean { 374 return o && o is String; 375 } 376 377 internal static function isArray(o:*):Boolean { 378 return o && o is Array; 379 } 380 381 public static function getAttributeValue(attrs:*, name:String):* { 382 if (attrs[name]) 383 return attrs[name]; 384 for (var x:String in attrs) { 385 if (x.toUpperCase() == name.toUpperCase()) { 386 return attrs[x]; 387 } 388 } 389 return null; 390 } 391 392 internal static function fromGeometryToJSON(geom:Object, opt_includeSR:Boolean=false):String { 393 /*function fromPointsToJSON(pts) { 394 var arr = []; 395 for (var i = 0, c = pts.length; i < c; i++) { 396 arr.push('[' + pts[i][0] + ',' + pts[i][1] + ']'); 397 } 398 return '[' + arr.join(',') + ']'; 399 } 400 function fromLinesToJSON(lines) { 401 var arr = []; 402 for (var i = 0, c = lines.length; i < c; i++) { 403 arr.push(fromPointsToJSON(lines[i])); 404 } 405 return '[' + arr.join(',') + ']'; 406 } 407 408 var json = '{'; 409 if (geom.x) { 410 json += 'x:' + geom.x + ',y:' + geom.y; 411 } else if (geom.xmin) { 412 json += 'xmin:' + geom.xmin + ',ymin:' + geom.ymin + ',xmax:' + geom.xmax + ',ymax:' + geom.ymax; 413 } else if (geom.points) { 414 json += 'points:' + fromPointsToJSON(geom.points); 415 } else if (geom.paths) { 416 json += 'paths:' + fromLinesToJSON(geom.paths); 417 } else if (geom.rings) { 418 json += 'rings:' + fromLinesToJSON(geom.rings); 419 } 420 if (opt_includeSR && geom.spatialReference) { 421 json += ',spatialReference:{wkid:' + geom.spatialReference.wkid + '}'; 422 } 423 json += '}'; 424 return json; 425 */ 426 var e:JSONEncoder=new JSONEncoder(geom); 427 return e.getString(); 428 } 429 } 430}