Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Openlayers projection with OSM style maps and a GeoJSON vector layer

Note: I know there's another question similar to this but it hasn't been answered and I need to know how mixed projections can be dealt with with GeoJSON and OSM.

I'm so confused. I was using the OSMDroid API on Android for mapping and wanted to replicate it using OpenLayers and GeoExt, but I've got a projection problem with including GeoJSON nodes and action events.

My tile set is OSM based, and is hosted on the same Web server as this HTML/JS. See it all below. I realize my boundaries aren't working, and my projections might be completely wrong. I've been testing different combinations.

The problem is my map displays correctly and is centered fine. However:

  1. My GeoJSON feature nodes are way off the map. They're in a different projection long/lat, but I don't know how to set or convert GeoJSON long/lat to the current map projection.

  2. My mapCtrl doesn't work. When I click it the lonlat is another projection (the OSM projection coords) and I can't seem to convert them)

  3. Any tips on how extent/bounds actually work would be greatly appreciated

Can someone please help with a bit of projection advice? Sigh... I'm not patient enough for this.

enter image description here

Here's my full JS, as is:

var mapPanel, store, gridPanel, mainPanel, nodePop, mapPop;

Ext.onReady(function() {

    var map, mapLayer, vecLayer;
    var lon = -70.885610;
    var lat = 38.345822;
    var zoom = 17;
    var maxZoom = 18;

var toProjection = new OpenLayers.Projection("EPSG:4326");
var fromProjection = new OpenLayers.Projection("EPSG:900913");
    var extent = new OpenLayers.Bounds(-1.32,51.71,-1.18,51.80).transform(fromProjection, toProjection);

    // Setup the node layer feature store and push it all into a vector layer
    vecLayer = new OpenLayers.Layer.Vector("vector");
    store = new GeoExt.data.FeatureStore({
        layer: vecLayer,
        fields: [
            {name: 'name', type: 'string'},
            {name: 'status', type: 'string'}
        ],
        proxy: new GeoExt.data.ProtocolProxy({
            protocol: new OpenLayers.Protocol.HTTP({
                url: "data/sa.json",
                format: new OpenLayers.Format.GeoJSON()
            })
        }),
        autoLoad: true
    });

    // Setup the basic map layer using OSM style tile retreival to pull tiles
    // from the same server hosting this service
    map = new OpenLayers.Map(
        'map', {
            controls:[
                new OpenLayers.Control.Navigation(),
                new OpenLayers.Control.PanZoomBar(),
                new OpenLayers.Control.Attribution(),
                new OpenLayers.Control.ScaleLine()],
            projection: toProjection,
            displayProjection: fromProjection,
            numZoomLevels: 20,
            fractionalZoom: true
        });

    mapLayer = new OpenLayers.Layer.OSM(
        "Local Tiles",
        "tiles/${z}/${x}/${y}.png",
        {
            zoomOffset: 17,
            resolutions: [1.194328566741945,0.5971642833709725,0.2985821416854863] // Zoom level 17 - 19
        });

    map.addLayers([mapLayer, vecLayer]);

    // Create a map panel
    mapPanel = new GeoExt.MapPanel({
            title: "Map",
            region: "center",
            map: map,
            xtype: "gx_mappanel",
            center: new OpenLayers.LonLat(lon, lat),
            zoom: zoom
    });

    // Create a grid panel for listing nodes
    gridPanel = new Ext.grid.GridPanel({
            title: "Nodes",
            region: "east",
            store: store,
            width: 275,
            columns: [{
                header: "Name",
                width: 200,
                dataIndex: "name"
            }, {
                header: "Status",
                width: 75,
                dataIndex: "status"
            }],
            sm: new GeoExt.grid.FeatureSelectionModel({
                autoPanMapOnSelection: true
                })
    });

    // Create the main view port
    new Ext.Viewport({
        layout: "border",
        items: [{
            region: "north",
            contentEl: "title",
            height: 150
        }, mapPanel, gridPanel]
    });
    var lonLat = new OpenLayers.LonLat(lon, lat).transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject());
    map.setCenter(lonLat, zoom);


    // Attach all the event driven stuff here...
    // Create a node selection pop up control
    function nodeAction(feature) {
        nodePop = new GeoExt.Popup({
            title: 'Node selected',
            location: feature,
            width: 200,
            html: "",
            maximizable: true,
            collapsible: true
        });
        nodePop.on({
            close: function() {
                if(OpenLayers.Util.indexOf(vectorLayer.selectedFeatures, this.feature) > -1) {
                    selectCtrl.unselect(this.feature);
                }
            }
        });
        nodePop.show();
    };

    // Attach the pop to node/feature selection events
    var selectCtrl = new OpenLayers.Control.SelectFeature(vecLayer);
    vecLayer.events.on({
        featureselected: function(e) {
            nodeAction(e.feature);
        }
    });

    // Create map selection pop up control
    function mapAction(lonlat) {
        mapPop = new GeoExt.Popup({
            title: 'Map selected',
            location: lonlat,
            width: 200,
            html: "You clicked on (" + lonlat.lon.toFixed(2) + ", " + lonlat.lat.toFixed(2) + ")",
            maximizable: true,
            collapsible: true,
            map: mapPanel.map,
            anchored: true
        });
        mapPop.doLayout();
        mapPop.show();
    };

    var mapCtrl = new OpenLayers.Control.Click({
        trigger: function(evt) {
            var lonlat = mapPanel.map.getLonLatFromViewPortPx(evt.xy);
            lonlat.transform(new OpenLayers.Projection("EPSG:4326"), mapPanel.map.getProjectionObject());

            //.transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject());

            mapAction(lonlat);
        }
    });
    mapPanel.map.addControl(mapCtrl);
    mapCtrl.activate();
});

// A control to handle user clicks on the map
OpenLayers.Control.Click = OpenLayers.Class(
    OpenLayers.Control, {
        defaultHandlerOptions: {
            single: true,
            double: false,
            pixelTolerance: 0,
            stopSingle: true
        },
        initialize: function(options) {
            this.handlerOptions = OpenLayers.Util.extend(
                options && options.handlerOptions || {},
                this.defaultHandlerOptions
            );
            OpenLayers.Control.prototype.initialize.apply(
                this, arguments
            );
            this.handler = new OpenLayers.Handler.Click(
                this,
                { click: this.trigger },
                this.handlerOptions
            );
        },
        CLASS_NAME: "OpenLayers.Control.Click"
    }
);

Here's the GeoJSON I'm using:

{
  "type": "FeatureCollection",
  "features": [
    {
      "geometry": {
        "type": "Point",
        "coordinates": [
          -70.3856,
      38.3458
        ]
      },
      "type": "Feature",
      "properties": {
        "name": "Node0",
        "status": "Active",
        "externalGraphic": "img/node2.png",
        "graphicHeight": 75, "graphicWidth": 75
      },
      "id": 100
    },
    {
      "geometry": {
        "type": "Point",
        "coordinates": [
          -70.885810,
      38.344722
        ]
      },
      "type": "Feature",
      "properties": {
        "name": "Node1",
        "status": "Active",
        "externalGraphic": "img/node2.png",
        "graphicHeight": 75, "graphicWidth": 75
      },
      "id": 101
    }
  ]
}
like image 206
dubmojo Avatar asked Nov 11 '22 13:11

dubmojo


1 Answers

Ok, here's how I dealt with the issue:

  1. I'm using the embedded the Jetty Web server in my back-end, but regardless, I created a servlet to respond with GeoJSON format data. Each Feature location lon/lat is converted between projections. (e.g. EPSG:4326 to EPSG:900913)

  2. The lon/lat projection conversation leveraged the GeoTools Java API. This blog post was particularly helpful (http://ariasprado.name/2012/08/13/quick-and-dirty-coordinate-transforming-using-geotools.html) Note that you'll need to go through a fair bit of trial and error if you only want to include the jars required for converting yout projections. GeoTools is large, does a lot, and has a number of jars.

Now when the GeoExt.data.ProtocolProxy loads my GeoJSON content it's already in OSM compatible EPSG:900913. I would have liked to deal with this entirely in GeoExt/OpenLayer, but there doesn't appear to be an easy way. I will acknowledge that GeoExt and OpenLayers don't have super great reference documentation to follow.

I'd include my GeoTools code but "Arias Prado GIS Ramblings" blog post above does a better job than I could. Again though, note that you'll have to trial and error the jars. Projection encoders are loaded dynamically, and they in turn have class dependencies from other jars.

like image 167
dubmojo Avatar answered Nov 14 '22 21:11

dubmojo