Saturday, December 13, 2008

GoogleBar hack

How to make a Local search and have an info window opened during page load.

If you load the full blown Google AJAX Search API, that's not a problem. If you are using Local Search GControl, you have a searchFormHint option but not a method to trigger the search. With GGoogleBar you don't have either of them.

Both GoogleBar and Local Search Control generate a form element with a text input field and a submit button. The form element of GoogleBar is not generated until the GoogleBar is opened. So we can assume that the form is the last one on your page and we can access it like:

  var lastForm = document.forms.length - 1;
  document.forms[lastForm].elements[0].value = "Music";
  document.forms[lastForm].elements[1].click();

You have to define the GoogleBar to open initially by googleBarOptions:{showOnLoad:true}.

How to trigger the form access? There is no event that would tell us when the form is built. I ended up using onIdleCallback function plus a small timeOut. The onIdleCallback  is triggered when GoogleBar is formed. It is triggered also after each search so I set a variable to make sure the seach is made only once.

It seems to work but with no quarantee.

Tuesday, December 02, 2008

softstateurlhook

Just noticed an intresting GMap2 event 'softstateurlhook'. I did not find it mentioned on web. 

It is triggered when map state changes.  After panning , zooming or map type change and also on page load. Try it. It returns an object as its single parameter. The properties of the object:
  • ll: "60.170293,24.939554"
  • spn: "0.043719,0.087891"
  • z: 13
  • t: "k"
They are lat/lng, span, zoom and map type. They are maps.google.com url parameters. If you click 'Powered by Google' logo link, those parameters are used to form the url.

You can use those for similar linking purpose, or you could store those in a cookie. A back button could also be constructed using the event.

The event is most convenient for fetching marker data from a server. Center lat/lng and span are just the parameters needed and the event is triggered just when needed.


Saturday, November 29, 2008

Map Kitchen [part 5] ZMarker

GMarker is quite a complicated object. That is why a few hundread markers are too heavy a load for poor browsers. Loading even more features on GMarker doesn't sound sensible. Well, there is some sense if the new features enhance the UI so that user doesn't have too zoom in/out that often.

Z-index switching is a way to make each marker reachable on a crowded map. It is one of the simpliest solutions to the eternal markers-too-close-together problem. The marker that is clicked and the info window was read, is thrown behind the others.

The technique was first published in Dec 2006. The code of the original test page was once again so messy that not very many people have applied the idea. Now it is written as a simple to use GMap2 method createZMarker(). We need to know the status of infowindow, that is why it is a GMap2 method.

I haven't been promoting the technique very actively because it was based on an undocumented option of GMarker. The ZIndexProcess option has been documented for a few months now but still the documentation doesn't  tell the full truth. The ZIndexProcess function is documented to be called when marker is addOverlayed and when marker's infowindow is opened. Yes, that happens but the function is called also when the infowindow is closed. That is the most valuable feature but still undocumented and maybe a bug.

Whether the infowindow is opening or closing is determined by status of GMap2.getInfoWindow().isHidden(). If the timing of that flag changes, the z-index swap starts behaving wrong way. Live page using this method should be locked to a certain api version and tested with new version always before updating. That is actually a good practice anyway.

There have been some discussion about new documented way to control z-index. If we get it, we could control z-index also by mouseover. Go and star the request if you find it useful.

Marker icon color change is independent of z-index process but it is built in createZMarker(). I find it an important feature for the journey towards better UI. It makes co-operation between sidebar and markers more efficient.

The marker icons are created by Google Charts API using MapIconMaker script. The markers are slightly transparent which looks good but harms color contrast in very crowded places.

Sunday, November 23, 2008

Map Kitchen [part 4]

Sidebar of maps.google.com can be collapsed to get full map view. The way it works is impressive. The map stays static when it is widened and its actual centerpoint changes. User doesn't see the tiles loading, they are loaded already.

A few people have been asking how to make something similar. The secret of the implementation seems to be solved. The map is fullscreen all the time. Only the sidebar is swithed on and off over the map.

Copying the idea is not very simple. There are many tricky details to be solved. Most of them are solved now. See the page

The base and basic components for better map pages are about constructed. A page that imports and parses its data from text files, puts it on map and in collapsible interactive sidebar with category switching.

Now we can concentrate more at filtering the data and fine tuning the actual map UI.


Thursday, November 13, 2008

Drag & drop sortable list

Today we have something more fascinating than a regular part of Map Kitchen series. We have a sortable list with GDraggableObject(). v0.1.

Monday, November 10, 2008

Map Kitchen [part 3]

Marker categories is the basic trick of filtering markers. Groups of markers can be switched on and off and the groups have different icons. The classic switching by sidebar checkboxes was chosen.

Shall we load each category from a file of its own or shall we have a single file with category data. We shall provide both ways. The concepts can even be mixed.

First we create a SideBar() object, then we create the needed BarCategory() objects. A category will be created if it is specified in file loading options. Also one is created if a new category name is found from files category cell.

The category checkbox switches display of category entries and its markers. Also visibility of marker image on sidebar is switched making sidebar cleaner.

See the page, play with it and see its source.

Thursday, November 06, 2008

Map Kitchen [part 2]

A map page reading marker and infowindow data from a text file. It comes equipped with a 2-way interactive sidebar. That that fulfills many basic needs.

If you have a single file with not very many markers and they do not overlap, you don't need the upcoming parts of this series. Just copy the code and display your file. You can make the infowindow and sidebar look good by css.

Sidebar label and marker tooltips title are the same as infowindow header. The data cell of them is still hardcoded [2] but it is easy to find in the source code and can be changed independently to any cell in your csv line.

I suddenly noticed that [part 1] did not parse the text file with IE7. It is fixed now and it was just introducing the parseCsv() too late in the code for IE. Risk of that kind of troubles is avoided when I package the methods as a library. There are methods coming before that.

Too many markers is the most common UI issue on map pages. The following parts will show methods for filtering the markers.

Sunday, November 02, 2008

Map Kitchen [part 1]

What is Map Kitchen? It is a  series of experimental pages. Also it is a JavaScript library that will contain the functions and methods used on those pages.

Where does the name come from? We have a great FM-station in Helsinki playing experimental music. One of their best DJ's took a name 'Soul Kitchen' for his program (he is a cook). So this is thanks to DJ Soppamies for inspiring me by music.

Where does the idea come from? Many people been asking me help for applying code ideas presented in 
  • marker data from a text file
  • sorting by nearest location
  • switching z-index of a marker
  • two-way interactive sidebar
I was exploring source codes of those pages and I did not understand much. No wonder that people been asking for my help. So I decided to start from scratch.

The series will use CSV text files as the data source because it been asked a lot. Still you can use any source:
  • inline hardcoded
  • XML/KML
  • JSON
  • feeds
  • Spreadsheets API
  • TSV
I been trying to write all the functions and methods to be programming modules for any kind of data source processing.

I already blogged a few methods how to generate CSV-formatted text files. Many of you have those files already from various sources.

When does it start? Just now, we start putting the CSV-data on maps. The first part is very simple: polyline from CSV.

Friday, October 31, 2008

Geocoding by Google Spreadsheets

Tony Hirst published a highly inspiring article about doing a Google Maps mashup without JavaScript.

I would like to show that Google Spreadsheets can do geocoding as well as Pipes.

There is http-version of Google Maps geocoder. Send a request:
http://maps.google.com/maps/geo?q=1600+Amphitheatre+Parkway,+Mountain+View,+CA&output=xml
and you get a full-blown XML response. With parameter output=csv you get a simplified response
200,6,42.730070,-73.690570 Status, accuracy, lat, lng, as a four cell csv text response,  yahoo!

Google Spreadsheets can import csv-data by  =ImportData()formula. So why don't we make it fetch coordinates of the address in the next cell.

The order of the parameters is not convenient for us, so we change it. The query-parameter 'q' shall be the last one. We type in the first cell of Spreadsheets:
http://maps.google.com/maps/geo?output=csv&q=
In the next cell we type the requested address like Helsinki.

The third cell will add together the two previous cells and ask for data by ImportData() formula:
=ImportData(CONCATENATE(A2,B2))

When you finished typing that, you will see a miracle in the next cells: '200' (http status), '4' (Google accuracy parameter), 60.169879 (latitude of Helsinki) and 24.938408(longitude of Helsinki). Did we ask anything more?

And they say that there is no geocoding in Google Spreadsheets. Plus think how it can import and export. Hold my hat!

There is also automatic googling to get input data.
=GoogleLookup(entity,attribute)
can fetch your POI (entity) address(attribute), and you geocode that as above.

You can use the output via a variety of feeds or read it by ajax-api as JSON. You can for instance preload your pages GClientGeocoder cache to know the POIs. Your visitors don't find the POI by geocoding unless you took care of it.


What happens when all the pages read each other in a loop.

Thursday, October 30, 2008

Batch geocoder with CSV output

Geocoding a list of addresses as a batch can be done faster than some times ago. Still you cannot send a bunch of geocode requests by GClientGeocoder at one moment. They must be done one by one. Just wait that the former request is answered before you send a new one.

This is a batch geocoder page that takes a line feed separated list of addresses. Many address lists don't contain country or state names if they are constant. There are even lists without town mentioned. Those missing constant names can be added in the [extension] field of the page.

Paste your address list and hit [geocode]. The geocoded coordinates will be listed on output field and corresponding markers are placed on map. Non-found addresses will be listed on error report field.

Thursday, October 23, 2008

Reverse GClientGeocoder

Pamela announced a few hours ago that the much requested feature is here. Great. I made a random click on the example page and saved the server response.



_xdc_._1fmnhtk4s && _xdc_._1fmnhtk4s({
"name":"-122.124367,37.436975",
"Status":{
"code":200,
"request":"geocode"
},
"Placemark":[{
"id":"p1",
"address":"870 Bruce Dr, Palo Alto, CA 94303, USA",
"AddressDetails":{
"Country":{
"CountryNameCode":"US",
"CountryName":"USA",
"AdministrativeArea":{
"AdministrativeAreaName":"CA",
"Locality":{
"LocalityName":"Palo Alto",
"Thoroughfare":{
"ThoroughfareName":"870 Bruce Dr"
},
"PostalCode":{
"PostalCodeNumber":"94303"
}
}
}
},
"Accuracy": 8
},
"Point":{
"coordinates":[-122.124557,37.437032,0]
}
}]
})

I don't know who's address that is but you can see the structure.

The query is given conveniently as a GLatLng() to the GClientGeocoder.getLocations() as you can see in the updated documentation.

So the client side script makes the decision wheter we send geocode or reverse-geocode request. That is clever.

The new feature is a perfect building brick for my next csv generator page. I was just intending to publish the page!

See a temporary test page.

Wednesday, October 22, 2008

GDraggableObject

I had to update the contex menu of the editor of previous post. The idea of using infowindow as a context menu was bad. It covers the map and the area of interest is just beneath the infowindow.

I wrote a function that creates a popup block with hide() and show() methods. The block is made a GDraggableObject so that it will no more be on your way.

There has been a trouble with text input fields in a GDraggableObject.  No matter how hard you try to click it, the cursor doesn't stay in the field and you cannot enter anything. Suddenly the solution was found.

TIP! Add to the properties of the text input:
onmouseover="this.focus()";

Driving directions were also added to the editor.

A few hours later:

Drop down selector is another problematic element on draggable block.  The block gets dragged unintentionally when the scrollbar is dragged. Actually all the elements with scrollbars do the same.

Fortunately GDraggableObject has some handy methods. disable() freezes the block and enable() makes it draggable again. The scrollbar problem was solved by disabling draggability on selector mouseover.

TIP! Add to the properties of a draggable selector:
onmouseover = draggableObject.disable();
onmouseout = draggableObject.enable();

After a few hours of sleep:

I noticed that Chrome doesn't build the selector options dynamically in the way I been using for a long time.
selector.options[index] = new Option(value);

Chromes dom inspector shows the generated options but they are not rendered! So the trouble is in Chrome. Lets go on.

Tuesday, October 21, 2008

Polyline editor, csv output

A polyline editor is the first page that uses the CSV parser of the last post. At least I have not heard of any other.

The online editor uses the GPolyline.enableDrawing() method that came with api v2.111. The behaviour is similar to MyMaps. The line data is printed on a textarea in CSV format.

The csv parser is used for redrawing the polyline from generated code. You can come back with your csv file and go on editing.  That is a client side application aka web page. So you have to copy/paste the cvs text to your own text editor and save it.

Why csv? The reasons are still the same as they were in 1967.  It is still the interchange format with widest support. Also people are asking for it. It is the easiest to understand and edit. You can use it with any character set. A syntax error will not corrupt the whole file. The list goes on..

There is no csv standard. The concept is so simple that there was never a need for a written standard. Instead there is an informational RCF document 4108 from 2005!

The data validation I am using at the moment is super simple. If either of the excpected coordinate cells includes NaN data, the whole line is skipped. You don't even need a separate empty line skipping. I have not managed to write a bad line that would confuse the other lines.

The current parser is even slightly simplier than the one in previous post. I will not update the previous post. See the source of the editor page.

A series of csv based pages follows.

Saturday, October 18, 2008

CSV parser

Comma separated text file CSV  is a lightweight alternative to XML/KML and JSON. It is extremely simple to create and maintain by a text editor. All the spreadheets and databases can export CSV.

GPS POI files are widely available around the web in CSV format. No wonder that many people are trying to display those files on Google Maps. The code of my old page is so messy that not many people have managed to use it as a template. I am just finishing a cleaner page but actually the CSV parsing was one the main troubles.

Basic CSV is extremely simple to parse. Split the file into lines by split("\n") and split the lines by split(","). That's it, BUT. In real CSV the strings (that are separated by commas) may also contain commas if the string is quoted by double quotes. How not to split those quoted strings?

Four hours of googling and 100+ articles, discussions and snippets didn't bring a script but an idea how to do it.

First split the line by quote. Then find the odd-indexed splits by if(i%2) and replace its commas temporarily with something like "::::". Do the usual splitting by comma and after that replace the original commas back.

Four hours googling plus one hour of code writing and testing gave results. A CSV parser that seems to work perfect.

/**
 * parseCsv()
 * @return an array of GLatLng() objects
 * @author Esa 2008
 */
String.prototype.parseCsv = function(opt_options){
  var results = [];
  var opts = opt_options||{};
  var iLat = opts.lat||1;
  var iLng = opts.lng||0;
  var lines = this.split("\n");
  for (var i=0; i
    var blocks = lines[i].split('"');
    //finding commas inside quotes. Replace them with '::::'
for(var j=0;j
      if(j%2){
        blocks[j]=blocks[j].replace(/,/g,'::::');
      }
    }  //@author Esa 2008, keep this note.
    lines[i] = blocks.join("");
    var lineArray = lines[i].split(",");
    //skip empty lines
    if(lineArray.length>1){
      var lat = lineArray[iLat]*1;
      var lng = lineArray[iLng]*1;
      var point = new GLatLng(lat,lng);
      //after splitting with commas, we put hidden ones back
 for(var cell in lineArray){
        lineArray[cell] = lineArray[cell].replace(/::::/g,',');
      }
 point.textArray = lineArray;
 results.push(point);
    }
  }
  return results;
}

Note that the array of a line cells is attached as .textArray property to the GLatLng() object.

Monday, October 06, 2008

Sprites

GIcon() property .sprite was discovered in the api code two months ago but no-one has presented any use for that. It is an object with two properties .image and .top.

sprite.image is uri of an image and sprite.top sets its style.top relative to the marker container.

You can have a set of markers in a single image file and bring one icon at a time visible by .top.

Hosting a large set of markers is not fun. A single file will do now. Also you don't have to preload separate image files anymore.

See a demo.


Update:
There are also 'width' and 'height' properties. Those can be used if you did not specify GIcon.size. You can have different sized icon images in a single sprite. Thanks Mike.

Friday, April 04, 2008

zmarker.js library

It is simple to write a test page using a new functionality that was announced or was discovered by Mike Williams. I been doing that many times within a few hours from the publishing of a new feature.

Sometimes you just got an idea and write a page for study and publish it. People mail you positive feedback. It is rewarding, useful and fun.

Writing an api extension is totally different. You have to think about all the developers that are going to use your code. You have to write functions that have useful and useable I/O. You have to write clear code with descriptive variable and function names. You have to comment your code clearly. You have to follow general code writing conventions. You have to follow general comment tagging conventions. You have to write a reference. You have to write useful example pages. You have to make a page that describes the extension. You have to monitor and react feedback on group. Still you will get a lot of personal email.

Writing a library of api extensions sounds like something that is not intended for any intelligent creature.

I took the challenge. zmarker.js library will be announced in a few days.

Writing an API is like something from Moon or Mars compared to my 'library'. Now I do respect the Google maps api people even more than ever before. They are doing some unbelievable work.

P.S. From the beginning of the api group: Many of the first posters were disturbed that the marker is ugly. How did they find the right group without a vision.