1

I'm trying to add placemarks to my map using a KmlLayer, but the coordinates aren't mapping properly. I already know the coordinates are in long,lat order, the issue is that the coordinates seem like they're zoomed out further than the map, but relative to each other, they seem to be correctly placed, the end result being that all of my placemarks are in a cluster in the middle of my map instead of being spread across the whole map like they should be. I had previously tried manually creating google.maps.Marker() objects, and the coordinates were correct when I did that, so I know for certain that the coordinates are correct.

I tried setting up a JSFiddle to demonstrate, but I can't seem to get it to work, so I'll just link you to the live page. On that page, the map, when zoomed out fully, is bounded by the coordinates (+/-64,+/-64), because using those bounds made marker placement easier, and obviously, I don't care about correct mapping to any real-world coordinates. For reference, the southwest-most heart marker is specified as being at coordinate (-59.75,-56.75), so it should be nearly at the corner of the map (on the deck of the southwest-most ship in the bay, to be precise). When I create a google.maps.Marker() object with those exact coordinates, it goes exactly where I want it to. So, can anybody help me understand why the KmlLayer coordinates are not mapping out correctly?

Here is the full KML file

qwertymodo
  • 435
  • 7
  • 16

1 Answers1

1

KML uses WGS84 longitude and latitude coordinates. You are using a custom map projection that doesn't use these coordinates.

When you create the markers with the Maps API, it goes through the custom projection, but I don't think KML layers do that.

Presumably this KML file is something you're generating yourself on your server or with some script? Then you could project the coordinates as needed when you generate the KML, to match the projection used in your Maps API code.

If that's not convenient or possible, then is it necessary to use the KML layer? Since it works to use the Maps API Marker object, perhaps it would work to just run with that.

Here is a related question.

Following up on our conversation in the comments, I think the best approach will be to use JSON to store the marker data. JSON doesn't impose much required structure; it has objects and arrays just like JavaScript and you can put them together in any way that is useful for you. Here's one version of what your XML file might look as JSON:

{
    "groups": [
        {
            "type": "heartPiece",
            "icon": "heartpiece.png",
            "markers": [
                {
                        "id": "p1",
                        "name": "Heart Piece #1",
                        "lat": -11.5,
                        "lng": 3.5
                },
                {
                        "id": "p2",
                        "name": "Heart Piece #2",
                        "lat": 5.75,
                        "lng": 58
                },
                {
                        "id": "p3",
                        "name": "Heart Piece #3",
                        "lat": 28.25,
                        "lng": 60.75
                },
                {
                        "id": "p4",
                        "name": "Heart Piece #4",
                        "lat": -58.75,
                        "lng": -16.25
                },
                {
                        "id": "p5",
                        "name": "Heart Piece #5",
                        "lat": -35,
                        "lng": -14
                },
                {
                        "id": "p6",
                        "name": "Heart Piece #6",
                        "lat": -11.25,
                        "lng": -30.25
                },
                {
                        "id": "p7",
                        "name": "Heart Piece #7",
                        "lat": -40,
                        "lng": -9.5
                },
                {
                        "id": "p8",
                        "name": "Heart Piece #8",
                        "lat": -5.75,
                        "lng": 9.75
                },
                {
                        "id": "p9",
                        "name": "Heart Piece #9",
                        "lat": -5,
                        "lng": 0.5
                },
                {
                        "id": "p10",
                        "name": "Heart Piece #10",
                        "lat": 2.75,
                        "lng": -10.25
                },
                {
                        "id": "p11",
                        "name": "Heart Piece #11",
                        "lat": 28.75,
                        "lng": 19.75
                },
                {
                        "id": "p12",
                        "name": "Heart Piece #12",
                        "lat": -54.75,
                        "lng": 45.75
                },
                {
                        "id": "p13",
                        "name": "Heart Piece #13",
                        "lat": -39.75,
                        "lng": 5.25
                },
                {
                        "id": "p14",
                        "name": "Heart Piece #14",
                        "lat": -56.75,
                        "lng": -59.75
                },
                {
                        "id": "p15",
                        "name": "Heart Piece #15",
                        "lat": -60.25,
                        "lng": 23
                },
                {
                        "id": "p16",
                        "name": "Heart Piece #16",
                        "lat": 64,
                        "lng": 0
                },
                {
                        "id": "p17",
                        "name": "Heart Piece #17",
                        "lat": -52.25,
                        "lng": 60.75
                },
                {
                        "id": "p18",
                        "name": "Heart Piece #18",
                        "lat": 48.25,
                        "lng": 31.25
                },
                {
                        "id": "p19",
                        "name": "Heart Piece #19",
                        "lat": 60.5,
                        "lng": 20.5
                },
                {
                        "id": "p20",
                        "name": "Heart Piece #20",
                        "lat": 22.75,
                        "lng": 24.5
                },
                {
                        "id": "p21",
                        "name": "Heart Piece #21",
                        "lat": 61.25,
                        "lng": 10.75
                },
                {
                        "id": "p22",
                        "name": "Heart Piece #22",
                        "lat": 40,
                        "lng": 56.5
                },
                {
                        "id": "p23",
                        "name": "Heart Piece #23",
                        "lat": -14.25,
                        "lng": 32.25
                },
                {
                        "id": "p24",
                        "name": "Heart Piece #24",
                        "lat": 29.75,
                        "lng": -61.25
                },
                {
                        "id": "p25",
                        "name": "Heart Piece #25",
                        "lat": 60.25,
                        "lng": -40.25
                },
                {
                        "id": "p26",
                        "name": "Heart Piece #26",
                        "lat": 63,
                        "lng": -44
                },
                {
                        "id": "p27",
                        "name": "Heart Piece #27",
                        "lat": 28.25,
                        "lng": -27.25
                }
            ]
        },
        {
            "type": "heartContainer",
            "icon": "heartcontainer.png",
            "markers": [
                {
                        "id": "c1",
                        "name": "Heart Container #1",
                        "lat": -2.75,
                        "lng": 56
                },
                {
                        "id": "test",
                        "name": "Test",
                        "lat": -90,
                        "lng": -90
                }
            ]
        }
    ]
}

And here is some untested sample code to load that JSON data and create your markers:

$.getJSON( 'markers.json', function( json ) {
    json.groups.forEach( function( group ) {
        group.markers.forEach( function( mark ) {
            mark.group = group;
            addMarker( mark );
        });
    });
});

function addMarker( mark ) {
    var marker = new google.maps.Marker({
        map: map,
        icon: '/images/icons/' + mark.group.icon,
        position: new google.maps.LatLng( mark.lat, mark.lng ),
        title: mark.name
    });
}

This code would go where you're creating the KML layer now. The code uses jQuery's $.getJSON() to download the JSON file. If you don't want to use jQuery for this, there are any number of equivalent functions you could use, such as the downloadUrl() function in the Maps API samples. You could use it like this:

downloadUrl( 'markers.json', function( data ) {
    var json = JSON.parse( data );
    json.groups.forEach(...
        ...
    });
});

Another option for JSON data is to simply turn it into a script. If you take that JSON file and put loadMarkers( at the beginning, before the first {, and put ) at the end, after the last }, now the JSON file is an executable script that calls a global loadMarkers() function and passes it the JSON data. Call the file markers.js instead of markers.json, and then you can use the very simple downloadScript() function in the Maps API sample linked above, something like this:

loadMarkers = function( json ) {
    json.groups.forEach(...
        ...
    });
};
downloadScript( 'markers.js' );

That should help get you started; give a shout with any questions.

Community
  • 1
  • 1
Michael Geary
  • 28,450
  • 9
  • 65
  • 75
  • Michael, for completeness might the OP also consider some sort of client-side coordinate transform, ie WGS84-to-custom? Just a thought, I don't have a reference. – Beetroot-Beetroot Jul 05 '13 at 03:29
  • @Beetroot-Beetroot - Yes, that's a very good point. One could read a KML file in JavaScript and apply any kind of coordinate projection to the data in it. The [D3 library](http://d3js.org/) has a bunch of projections implemented in JavaScript. One limitation would be that you couldn't use the result as a KML *layer* in the Maps API. You'd have to create markers using the API in JavaScript instead, simply using the KML as a data source. To use a KML layer, Google's servers have to be able to read the KML file's URL. – Michael Geary Jul 05 '13 at 05:40
  • Honestly, I'm not locked into KML as a data source, I just want to put my markers in a separate file from my map API Javascript code, and the KmlLayer object seemed like a really convenient way of doing that. I had previously tried [using XML](http://www.birdtheme.org/useful/markersfromXMLv3.html) but I couldn't get the markers to display. I may just go back to that method and try to make it work, since it seems that method gives me a bit more control over customizing the markers, such as custom shadows and such. I'd rather keep the coordinates in my custom projection. – qwertymodo Jul 05 '13 at 07:47
  • Yeah, you get a lot more control using the Maps API directly instead of a KML layer. But instead of XML as a data format, I strongly recommend JSON. It is much easier to work with in your JavaScript code than XML, and just as easy to generate from any server language. The only reason there are all these Maps API examples using XML is that they are really old examples from before anyone knew how much better JSON is for this. I have to call it a night for now, but if I get a few minutes in the morning I may convert that XML example to JSON just for fun. – Michael Geary Jul 05 '13 at 08:23