[转帖]Tutorial : Thematic mapping with the Google Maps Flash API

Tutorial : Thematic mapping with the Google Maps Flash API

      Google has recently released a Flash version of the Google Maps API. The goal is to give developers and designers more freedom in the presentation and graphical effects of their maps. Google is certainly not the first provider of a Flash mapping API (see the Yahoo! Maps AS3 API for example) but as one of the original proponents of Ajax web mapping tools, it marks an interesting change of direction (although the original Google Maps API is probably not going away anytime soon). One interesting aspect of using Flash is also a boost in vector graphics rendering speed, which I am going to demonstrate here, with a tutorial showing how to display a chloropleth map with this new API. Here is a link to the final application. It has to be noted that I am definitely not a Flex/ActionScript guru (just a few evenings of experience) so there are probably better ways of doing things than what I am showing here.

Getting the data

      The data used is the simplified world borders shapefile found on Thematic Mapping (ie the TM_WORLD_BORDERS_SIMPL-0.2.zip file, at the bottom of the page). I have tried with the non simplified world borders as well, it is usable and quite fast to display but still feels somewhat sluggish, so the simplified dataset it is.

The application

      The first step is to create the the MXML, with the component holding the map on it :

 

ContractedBlock.gif ExpandedBlockStart.gif Code
1<?xml version="1.0" encoding="utf-8"?>  
2<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"  
3    xmlns:gvlt="com.vellut.examples.gmapsflash.*">  
4    <gvlt:ThematicMapContainer width="100%" height="100%" />  
5</mx:Application> 

 

      Probably not earth-shattering… The ThematicMapContainer class in the com.vellut.examples.gmapsflash package is a subclass of UIComponent. Here is the beginning of this class, that is going to be enriched as the tutorial progresses :

ContractedBlock.gif ExpandedBlockStart.gif Code
 1public class ThematicMapContainer extends UIComponent   
 2ExpandedBlockStart.gifContractedBlock.gif{   
 3    public var map: Map;   
 4  
 5    public function ThematicMapContainer()   
 6ExpandedSubBlockStart.gifContractedSubBlock.gif    {   
 7        super();   
 8        addEventListener(FlexEvent.INITIALIZE,startMap);   
 9    }
   
10  
11    private function startMap(event:Event):void  
12ExpandedSubBlockStart.gifContractedSubBlock.gif    {   
13        map = new Map();   
14        map.addEventListener(MapEvent.MAP_READY,onMapReady);   
15        addChild(map);   
16        addEventListener(Event.RESIZE,resizeMap);   
17    }
   
18  
19    private function resizeMap(event:Event):void  
20ExpandedSubBlockStart.gifContractedSubBlock.gif    {   
21         map.setSize(new Point(width, height));   
22    }
          
23  
24    private function onMapReady(event:Event):void  
25ExpandedSubBlockStart.gifContractedSubBlock.gif    {   
26        map.disableContinuousZoom();   
27        map.enableScrollWheelZoom();   
28  
29        map.addControl(new PositionControl());   
30        map.addControl(new ZoomControl());   
31        map.addControl(new MapTypeControl());   
32  
33       createAndAddGeometries();   
34    }
   
35}
  

      In the constructor, we subscribe to the “initialize” event of the container to start creating the map. Then when the initialiization is complete, the map is created and added to the container. Note that the API key is passed through the HTML page embedding the application (using flashVars), so this is why it does not appear anywhere here. The interesting thing, as far as the Google Maps Flash API is concerned, happens in the “onMapReady” method, called when the map has finished loading. Controls and various behaviours are added. The “createAndAddGeometries” method is also called: It is going to create the polygons for all the countries in the world, compute a color for each of them (according to an attribute of the country) and place them on the map. Note that contrary to what could be done with the Google Maps AJAX API, there is currently no way to place geometries through a KML or GeoRSS link. This means the polygons must be manually created though more atomic API calls.

Creating the polygons

      There are 2 ways to create a polygon : point by point (with LatLng objects) or using an array of EncodedPolylineData objects, which is more compact. The second method also lets us have levels of detail for the polygons, which means some points will not be displayed when the map is zoomed out, which is quite useful when we have a heavy layer, so for this tutorial I chose the second method. One problem with the current version of the API is that the second method does not seem to work (the issue has been reported on the gmaps-api-issues tracker: there is a complaint of a missing attribute on the EncodedPolylineData object ; encoded polylines work fine). Still there is a very easy workaround. Create this class :

ContractedBlock.gif ExpandedBlockStart.gif Code
1public class EncodedPolylineData extends com.google.maps.overlays.EncodedPolylineData   
2ExpandedBlockStart.gifContractedBlock.gif{   
3    public var options:*;   
4    public function EncodedPolylineData(arg0:String, arg1:Number, arg2:String, arg3:Number)   
5ExpandedSubBlockStart.gifContractedSubBlock.gif    {   
6        super(arg0, arg1, arg2, arg3);   
7    }
   
8}
  
9

      It can be used subsequently everytime the original EncodedPolylineData class is expected. Now that this is over with, on to more interesting things.

      In the following body of the “createAndAddGeometries” method, the polygons are created, added to the map and the call to the method attributing a color class to each geometry is made :

ContractedBlock.gif ExpandedBlockStart.gif Code
 1private function createAndAddGeometries() : void  
 2ExpandedBlockStart.gifContractedBlock.gif{   
 3    var polygons : Array = [];   
 4ExpandedSubBlockStart.gifContractedSubBlock.gif    polygons.push(new Feature(Polygon.fromEncoded([new    EncodedPolylineData('wa|fBrdowJexNndf@{iJ{bQ`cZs`T',32,'BAAB',4),new EncodedPolylineData('wdnjBhnwwJt~CfeWerV`|BnrQib[',32,'BAAB',4)]),{pop2005: 83039, area: 44 }));   
 5    //the rest of the polygons are omitted for brievity   
 6  
 7    for(var i : int ; i < polygons.length ; i++)   
 8ExpandedSubBlockStart.gifContractedSubBlock.gif    {   
 9         map.addOverlay(polygons[i].shape);   
10    }
   
11    chloropleth(polygons,"pop2005",5,new Color(Color.YELLOW),new Color(Color.RED));   
12}
  
13

      The complete set of polygons is generated offline through a Python script, which uses the Shapely and Gpolyenc libraries. The former takes care of reading the world borders Shapefile (through its OGR binding), while the latter encodes each polyline into the Google polyline encoding format and performs some cartographic generalization through the Douglas-Peucker algorithm (which will let us drop some points while zoomed out). The output of the script is a text file containing valid ActionScript code that can be copy-pasted into the “createAndAddGeometries” method body and compiled with the rest of the code. The script is packaged along with the Flex/ActionScript code for this tutorial (the 2 libraries mentioned above and the dataset will have to be downloaded separately though).

      Each country feature gets a geometry, along with 2 attributes: Area and Population in 2005. The thematic analysis will be performed on the population (”pop2005″, in the call to the chloropleth method). We want 5 classes (each class will have the same number of countries). Finally, the countries with the lowest population will be in yellow, while the ones with the largest will be in red.

Computing the classes

      The “chloropleth” method is as follows:

ContractedBlock.gif ExpandedBlockStart.gif Code
 1public function chloropleth(objects:Array, field:String, numClasses:int, colorBegin:Color, colorEnd: Color): void  
 2ExpandedBlockStart.gifContractedBlock.gif{   
 3    if(numClasses < 2)   
 4        return;   
 5  
 6    var i : int ;   
 7  
 8ExpandedSubBlockStart.gifContractedSubBlock.gif    var sobjects:Array = objects.sort(function(a:Feature,b:Feature):int {   
 9        if(a.attributes[field] < b.attributes[field])   
10            return -1;   
11        else if (a.attributes[field] > b.attributes[field])   
12            return 1;   
13        else  
14            return 0;   
15    }
);   
16  
17    var colors:Array = [];   
18    var incr: Number = (colorEnd.r - colorBegin.r) / (numClasses - 1);   
19    var incg: Number = (colorEnd.g - colorBegin.g) / (numClasses - 1);   
20    var incb: Number = (colorEnd.b - colorBegin.b) / (numClasses - 1);   
21    colors[0= colorBegin;   
22    colors[numClasses - 1= colorEnd;   
23    for(i = 1 ; i < numClasses - 1; i++)   
24ExpandedSubBlockStart.gifContractedSubBlock.gif    {   
25        colors[i] = new Color(0);   
26        colors[i].setRGB(Math.round(colors[i-1].r + incr),   
27            Math.round(colors[i-1].g + incg),   
28            Math.round(colors[i-1].b + incb));   
29    }
   
30  
31    var curClass : int = 0;   
32    for(i  = 0; i < objects.length ; i++)   
33ExpandedSubBlockStart.gifContractedSubBlock.gif    {   
34        if(i > Math.round((curClass + 1/ numClasses * objects.length) - 1)   
35            curClass++;   
36  
37ExpandedSubBlockStart.gifContractedSubBlock.gif        sobjects[i].shape.setOptions(new PolygonOptions({fillStyle:   
38ExpandedSubBlockStart.gifContractedSubBlock.gif            {   
39                color : Color(colors[curClass]).rgb,   
40                alpha : 0.7   
41            }
   
42        }
));   
43    }
   
44}
  
45

      There are 3 steps in the process :

  • Sorting the countries in order of their population value.
  • Obtaining the intermediate colors, between yellow and red. An interpolation on the values of the RGB components is performed. We get a color value for each class.
  • Finally, associating each country with a class, according to their population value. The breaks are performed at each quantile according to the “numClasses” parameter (in our case, quintile). The color is associated to the polygon by setting the “fillStyle” option of the PolygonOptions object. We keep the polygons somewhat transparent (70% opaque) so we can still see the ground.

      And this is it! After a very short time, the polygons are all displayed.

      Now about the speed: It has to be said it is pretty fast. Check for yourself. As a comparison, here is the same dataset, displayed on the AJAX API. Note that however, the method to add the polygons is not the same: The latter one uses the GGeoXml class to add a KML file. As noted above, this is not currently available on the Flash API.

The source code

      Here is a link to an archive containing the code, with the Flex Builder 3 project, as well as the “createAs.py” python script.

转载于:https://www.cnblogs.com/maapaa/articles/google_maps_3.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值