[GIS] How to work with GeoJSON, Leaflet and pixel coordinates

coordinate systemgeojsonleaflet

I'm a noob trying to piece together a – as I thought – simple thing:
A Leaflet map based on a raster picture with markers coming from a GeoJSON file. As this is a fictional map, I use the CRS.Simple in Leaflet and thus just use my picture pixels as coordinate system.
From some examples I put together the following:

    var map = L.map('map', {
        crs: L.CRS.Simple,
        minZoom: -1
    });


//- set the image width and height to w and h
    var w = 1700;
    var h = 2200;

    //the stuff to be able to work with xy instead of latlon
    var yx = L.latLng;
    var xy = function(x, y) {
        if (L.Util.isArray(x)) {    // When doing xy([x, y]);
            return yx(x[1], x[0]);
        }
        return yx(y, x);  // When doing xy(x, y);
    };

    var bounds = [xy(0, 0), xy(w, h)];

    //this makes the map fit the bounds we just defined
    map.fitBounds(bounds).setMaxBounds(bounds);


// insert image name here
    var image = L.imageOverlay('img/background.png', bounds).addTo(map);


    //create map icon class
    var TaladasIcon = L.Icon.extend({
    options: {
        iconSize:     [50, 50],
        iconAnchor:   [25, 25],
        popupAnchor:  [0, -25]
    }
});

    //define icons
    var metropolisIcon = new TaladasIcon({iconUrl: 'img/metropolis.png'}),
        cityIcon = new TaladasIcon({iconUrl: 'img/city.png'}),
        townIcon = new TaladasIcon({iconUrl: 'img/town.png'}),
        villageIcon = new TaladasIcon({iconUrl: 'img/village.png'}),
        innIcon = new TaladasIcon({iconUrl: 'img/tankard.png'});

//- load geoJson file
    L.geoJson(json-test, {
        onEachFeature: layer.setIcon(metropolisIcon)
    }).addTo(map);

My GeoJSON file looks like this so far:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "name": "Cityname",
        "description": "This is a very important city",
      },
      "geometry": {
        "type": "Point",
        "coordinates": [
          719,
          1210
        ]
      },
    },
  ]
}

There is obviously something wrong, as I only see the background image but no markers from the GeoJSON. I think it might have to do with part that exchanges latlng for xy, but honestly I'm not sure what exactly happens there.
It does work if I define a marker directly in the script, but not from the GeojJSON. Which is a bummer because I plan on having quite a few markers and being able to write them down in a table on geojson.io would save me a lot of time.

Can anyone point out what I'm doing wrong here?

EDIT: Second try with AJAX, still not working:

var map = L.map('map', {
  crs: L.CRS.Simple,
  minZoom: -1
});

var w = 1700;
var h = 2200;

var yx = L.latLng;
var xy = function(x, y) {
  if (L.Util.isArray(x)) {    // When doing xy([x, y]);
    return yx(x[1], x[0]);
  }
  return yx(y, x);  // When doing xy(x, y);
};

var bounds = [xy(0, 0), xy(w, h)];
map.fitBounds(bounds).setMaxBounds(bounds);
var image = L.imageOverlay('img/kisandra.png', bounds).addTo(map);

var TaladasIcon = L.Icon.extend({
  options: {
      iconSize:     [50, 50],
      iconAnchor:   [25, 25],
      popupAnchor:  [0, -25]
  }
});

var metropolisIcon = new TaladasIcon({iconUrl: 'img/metropolis.png'}),
    cityIcon = new TaladasIcon({iconUrl: 'img/city.png'}),
    townIcon = new TaladasIcon({iconUrl: 'img/town.png'}),
    villageIcon = new TaladasIcon({iconUrl: 'img/village.png'}),
    innIcon = new TaladasIcon({iconUrl: 'img/tankard.png'});

var jsonMetropolis = new L.geoJSON();

$.ajax({
dataType: "json",
url: "json-test.geojson",
success: function(data) {
  $(data.features).each(function(key, data) {
      jsonMetropolis.addData(data);
  });
}
});

jsonMetropolis.addTo(map);

Best Answer

Answer will be based on second try with AJAX (I added the code, gotten via Google drive).

There are a few things wrong in the code:

  • L.geoJSON() is not object, but function creating an instance of object, so the correct syntax for creating GeoJSON layer is var jsonMetropolis = L.geoJSON(...
  • When reading GeoJSON file json-test.geojson with AJAX, it must contain pure GeoJSON, not JS assignment in the form of var json-test = { ...
  • Since GeoJSON data is read async, GeoJSON layer has to be added to the map when reading data is finished, inside AJAX success callback.

You can also use pointToLayer option to assign custom icons to GeoJSON points.

So to wrap it up, the relevant part of the code could then look something like this (tested):

var jsonMetropolis = L.geoJSON([], {
  pointToLayer: function (feature, yx) {
    return L.marker(yx, {
      icon: metropolisIcon
    });
  }
});

$.ajax({
  dataType: "json",
  url: "json-test.geojson",
  success: function(data) {
    $(data.features).each(function(key, data) {
      jsonMetropolis.addData(data);
    });
    jsonMetropolis.addTo(map);
  }
});