Archive for the 'mapping' Category
General update – lots of stuff
It’s been a busy month…
[Fl]expert
I’ve just been made an Adobe Flex Community Expert ( cheers guys I’m honoured ) so I get to put a cool badge on my blog like this:
:)
360|Flex
Unfortunately I was quite ill during most of 360|Flex San Jose and spent the majority of time in bed. I did manage to drag myself out for my session though. Luckily the presentations are recorded so I will have to watch them now that I’m better. The one full session I did catch was Michael Labriola‘s deepdive talk on DataBinding. Goodness knows, I don’t want to massage his ego any more than necessary but Michael is quite easily one of the best speakers I’ve ever seen, so if you get a chance to see one of his talks at a future conference be sure to check him out. Thanks to all the sponsors and to John & Tom for putting on what I’m sure was another great conference – if only I could have seen it! No thanks to whoever made me ill ;)
In Print
I’ve had a print article published this month in Flash & Flex Developer’s Magazine. The article is a guide to Flex and the Google Map AS3 API. The magazine is not currently available in the UK but if you live in the US check it out. I’ve just received my copy and look forward to thumbing through the rest of the content as it looks great.
The Bay Area
The girlfriend and I turned our visit to 360|Flex in to a two week holiday. We both fell in love with The Bay Area ( to the point where we’d both consider moving there once she’s finished her law courses ). We went to San Jose & San Francisco then hired a car and drove along Route One, stopping off in places such as Santa Cruz [ where we rode the rollercoaster featured in The Lost Boys - got to be done! ] and Santa Barbara. We met some fantastic people and had a lot of fun.
GoogleMaps, Custom Cursors & Events
I've just updated the example mapping app with custom cursors ... well, as much as I can for the time-being.
Here are a couple of points relating to issues I encountered (in version 1.3 of the swc):
Possible event bugs
MapMouseEvent.MOUSE_MOVE does not fire in the current version of the swc. This has been reported by others so it's fairly safe to assume that it's a bug (currently this is preventing me from adding the "grab" cursor when the user moves the map).
MapMouseEvent.ROLL_OUT isn't working properly for me either. I have an HBox on the left of my map. When my mouse leaves the map Flex throws null object error (apparently before it gets around to dispatching the event - I pasted the stack trace here.
I got around this by subscribing to the UIComponent's MouseEvent.ROLL_OUT instead:
-
_map.addEventListener(MapMouseEvent.ROLL_OVER, mapRollOverHandler);
-
this.addEventListener(MouseEvent.ROLL_OUT, mapRollOutHandler);//map instance is instantiated inside a UIComponent subclass
Also note that in the above code ROLL_OUT is a constant on MouseEvent, not MapMouseEvent.
Disabling Custom Cursor for map controls
I needed to disable the custom cursor when the mouse rolls over the controls. This was straight forward:
-
//"removeCustomCuror" and "addMoveCursor" are custom methods
-
//which use the CursorManager to add or remove my custom cursor.
-
_zoomControl = new ZoomControl();
-
_zoomControl.addEventListener(MouseEvent.MOUSE_OVER, removeCustomCuror);
-
_zoomControl.addEventListener(MouseEvent.MOUSE_OUT, addMoveCursor);
-
_map.addControl(_zoomControl);
Disabling Custom Cursor for markers
When it came to markers I expected to be able to do this:
-
marker.addEventListener(MouseEvent.ROLL_OVER, removeCustomCuror); // doesn't work
...but found that I had to listen for MapMouseEvent instead:
-
marker.addEventListener(MapMouseEvent.ROLL_OVER, removeCustomCuror); //works
I guess this is because markers are map overlays, and so just another part/layer of the map.
Determine Cursor Type At Startup
To do this I listen out to the MOUSE_OVER event from the UIComponent, change the cursor if necessary and remove the listener. It just seemed like less work than determining the position of my mouse or performing a hit-test.
-
this.addEventListener(MouseEvent.MOUSE_OVER, determineCursorAtStartUp);
-
-
/**
-
* If mouse is over map when application starts, change the cursor
-
*/
-
private function determineCursorAtStartUp(event:Event):void
-
{
-
addMoveCursor(event); //CursorManager.setCursor(_moveCursor);
-
this.removeEventListener(MouseEvent.MOUSE_OVER, determineCursorAtStartUp);
-
}
Buggy Behaviour In Firefox
Sometimes, when I move the mouse and I'm over the map, the default cursor appears alongside my custom cursor momentarily, but this only happens in Firefox.
4 comments
GoogleMaps 101
GoogleMaps 101
I've already covered some of the basics for getting up and running with the new GoogleMaps AS3 API in Flex, with a Hello World example and an explanation of how to get rid of "Debug Mode" text / geocode when testing locally.
My experiences so far have been really positive. The documentation is well written, and although the AS3 API isn't as fully featured as it's counterpart, the team are bug fixing and adding features on a daily basis (plus taking requests, so be sure to check out the group). You shouldn't really have any major problems getting to grips with the code and finding out what you need. The purpose of this post is simply to distil what I thought may be the most sought after bits of code, in to a handy post.
GoogleMap tips
The code (from point 3 onwards) is taken from this application
1. Adding the GoogleMaps .swc to your project in FlexBuilder 3 is really easy!
2. After you have obtained your key, you can then create your new map. You will need to add it to the stage as the child of a UIComponent:
-
//as3 code
-
_map = new Map();
-
mapContainer.addChild(_map);
-
-
//mxml code
-
<mx:UIComponent id="mapContainer" width="100%" height="100%"/>;
-
-
/************************************************************
-
Note: Alternatively you can create a map component by subclassing UIComponent
-
as shown here: http://www.igorcosta.org/?p=140
-
*************************************************************/
All code from here-on-in assumes we have created our own map component by subclassing UIComponent - see here for an example.
3. You cannot interact with your map until it is ready! To know when this happens you need to listen out for the MapEvent.MAP_READY event:
-
private function init(event:Event):void
-
{
-
_map = new Map();
-
_map.key = "paste your key here";
-
_map.addEventListener(MapEvent.MAP_READY, onMapReady);
-
_map.setSize( new Point (this.width, this.height))
-
this.addChild(_map); //add as child of UIComponent
-
}
4. Once we know our map is ready we can set it up by instantiating controls, adding markers to it and so on. Below is the code needed to centre the map on a particular location as well as add the familiar GoogleMap controls such as the zoom slider and map-type buttons (we'll talk more about locations and LatLng in a moment):
-
private function onMapReady(event:Event):void
-
{
-
_map.setCenter( new LatLng(54.4700,-3.4277), 14, MapType.PHYSICAL_MAP_TYPE);
-
_map.addControl(new ZoomControl());
-
_map.addControl(new PositionControl());
-
_map.addControl(new MapTypeControl());
-
_map.setZoom(5);
-
-
//dispatch event from our map component so that we know basic setup is complete
-
dispatchEvent(new Event(Event.COMPLETE));
-
}
5. Whether you are centring your map as in the above example, or placing a marker on the map, you will at some point need to deal with latitude and longitude. Essentially they are number representing your location on the map. Latitude is used to express how far north or south you are and longitude shows your location in an east-west direction, relative to the Greenwich meridian.
I found the following resources useful for calculating latlng:
http://www.capelinks.com/cape-cod/maps/geocode/ - type the address in to the top input box, and the latitude/longitude appear in xml tags at the bottom of the page
http://www.satsig.net/maps/lat-long-finder.htm - this is useful for when you don't know the address. Drag the map around and it gives you both lat and lng. If you put it in to your map and it doesn't look the same, that's probably due to your zoom level, which you can set.
6. Geocoding is the process of taking an address in the form of a String, and having it converted to lat and lng. You may be thinking "cool, I'll just geocode everything", but a word of warning: Google's geocoder has a max number of queries per second, so it's not wise to loop over addresses and goecode them all. Plus geocoding is an aysnchronous operation, so bear your user in mind and don't make them wait that long. Instead, convert addresses to lat and lng in advance where possible. The tools mentioned above will help - or you can build your own of course :]
Read this thread for information of a geocoding bug ... it will hopefully be fixed by the time you read this post
7. Creating markers is something you will very likely want to do when you've got your map up and running. Here is a quick overview of how I went about it.
First off is a snippet of the xml. Note that I set isBranch="false" on the point nodes - this is so they show as leaf-nodes and not branch nodes in the Flex Tree component...
-
//EXAMPLE OF XML STRUCTURE...
-
<group label="offices" color="0x00FF00">
-
<point label="Winchfield Lodge" isBranch="false">
-
<address lat="51.284920" lng="-0.909787">Some address</address>
-
<phone>12345678</phone>
-
<fax>12345678</fax>
-
<email>email@address.com</email>
-
</point>
-
<point label="Hartham Park" isBranch="false">
-
<address lat="51.448433" lng="-2.199214">Another address</address>
-
<phone>12345678</phone>
-
<fax>12345678</fax>
-
<email>email@address.com</email>
-
</point>
-
</group>
Next we have the code in the main application file. I send in an XMLList of the point nodes, loop through each one, generate a marker object, extract the latitide and longitude, and then call a method on my map component:
-
//CODE WITHIN MY MAIN APPLICATION FILE
-
/**
-
* Creates and places markers on the map.
-
*/
-
private function plotMarkers(points:XMLList):void
-
{
-
for each(var item:XML in points)
-
{
-
var markerOptionObj:MarkerOptions = getMarkerObject(item);
-
var lat:Number = Number(item.address.@lat);
-
var lng:Number = Number(item.address.@lng);
-
mapComp.setMarkerFromLatAndLng(lat,lng, markerOptionObj);
-
}
-
}
-
-
/**
-
* @return An object which specifies the visual attributes of the marker
-
* (e.g. colour & roll-over text)
-
*/
-
private function getMarkerObject(item:XML):MarkerOptions
-
{
-
var markerOptionObj:MarkerOptions = new MarkerOptions
-
({
-
fillStyle:{color:Number(item.parent().@color)},
-
label:item.@label.substr(0,1).toUpperCase().toString(),
-
labelFormat:{color:0x000000, bold:true},
-
strokeStyle:{thickness: 2, color: 0x333333},
-
tooltip: String(item.@label)
-
});
-
return markerOptionObj;
-
}
Finally here is the code within my map component. It takes the lat, lng and marker options object and generates a marker. I also add each marker to an ArrayCollection, to make it easy to loop over the markers later.
-
//CODE WITHIN MY MAP COMPONENT
-
/**
-
* This takes lat and lng and places a marker at that location.
-
* @param lat The latitude of the marker.
-
* @param lng The longitude of the marker.
-
* @param markerOptionObj An object of type MarkerOptions, defining how the marker should look.
-
* @return A reference to the Marker instance created.
-
*/
-
public function setMarkerFromLatAndLng(lat:Number, lng:Number, markerOptionObj:MarkerOptions = null):void
-
{
-
var latlng:LatLng = new LatLng(lat, lng);
-
var marker:Marker = new Marker(latlng);
-
marker.addEventListener(MapMouseEvent.CLICK, onMarkerClick);
-
if(markerOptionObj != null) marker.setOptions(markerOptionObj);
-
_map.addOverlay(marker);
-
_markers.addItem(marker); // add to ArrayCollection
-
}
Hopefully this will give you enough info to get on with building your own map using the API. Any questions related to this post please feel free to leave a comment :]
8 comments