Openlayers projection with OSM style maps and a GeoJSON vector layer - javascript

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:
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.
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)
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.
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
}
]
}

Ok, here's how I dealt with the issue:
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)
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.

Related

QuerySourceFeature is not working as expected - Mapbox GL

I am looking for a answer that will solve my problem. I am highlighting an address based on geocoder searched coordinates. This same functionality is working perfect on the click on building but I do not want to click on building rather just highlight the building based on searched coordinates. I am providing code below:
map.addLayer(
{
id: "3d-buildings",
source: "composite",
"source-layer": "building",
type: "fill",
minzoom: 10,
paint: {
"fill-color": "#aaa",
},
},
labelLayerId
);
map.addSource("currentBuildings", {
type: "geojson",
data: {
type: "FeatureCollection",
features: [],
},
});
map.addLayer(
{
id: "highlight",
source: "currentBuildings",
type: "fill",
minzoom: 15,
paint: {
"fill-color": "#f00",
},
},
labelLayerId
);
This below click working perfect
map.on("click","3d-buildings", (e) => {
map.getSource("currentBuildings").setData({
type: "FeatureCollection",
features: e.features,
});
});
I want the same features but not on click rather than on geocoder result coordinates:
geocoder.on("result", (e) => {
var coordinates = e.result.geometry.coordinates;
const selectedFeatures = map.querySourceFeatures(coordinates, {
layers: ["3d-buildings"],
});
console.log(selectedFeatures); //giving blank array, how can I get the features same as the onclick function is providing with additional parameter which is 3d-buildings
map.getSource("currentBuildings").setData({
type: "FeatureCollection",
features: e.features,
});
});
Maybe I am wrong at some point in using querySourceFeature. Please direct me in right direction.

Creating point, popup, circle, convert coordinates issues (Arcgis JS 4.6 API)

I want to do some simple things on a basemap project.
Such as creating a circle.
Creating a popup when click on created point.
Converting lat long to x y
How can I do these things?
var pt = new Point({
longitude: 99.909550,
latitude: 99.751906
});
var lineAtt = {
Name: "fill",
Owner: "fill",
Length: "999 km"
};
// Create a graphic and add the geometry and symbol to it
var pointGraphic = new Graphic({
geometry: pt,
symbol: markerSymbol,
attributes: lineAtt,
popupTemplate: { // autocasts as new PopupTemplate()
title: "{Name}",
content: [{
type: "fields",
fieldInfos: [{
fieldName: "Name"
}, {
fieldName: "Owner"
}, {
fieldName: "Length"
}]
}]
}
});
I found the solution that open popup while clicking the created point.

GeoJSON feature coordinates not displaying on OpenLayers map

I'm trying to display a GeoJSON polygon on a map. I've used the example provided by OpenLayers with the following data, but only the second polygon is displayed:
var geojsonObject = {
"type": "FeatureCollection",
"crs": {
"type": "name",
},
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[[103.92240800000013,21.69931],[100.93664,21.66959500000013],[108.031899,18.67076]]]
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[[-5e6, -1e6], [-4e6, 1e6], [-3e6, -1e6]]]
}
}
]
};
The code I'm using to parse and add the GeoJSON to the map is as follows:
var vectorSource = new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(geojsonObject)
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: styleFunction
});
I noticed different kinds of coordinates. In the second set the coordinates are represented like [-5e6, -1e6] with the e which I don't understand and in the first set - that don't work - they look like [103.92240800000013, 21.69931].
Is this a possible reason why my polygon is not displayed?
The problem is your two polygons are specified using different coordinate spaces and you need to determine which map projection you are going to use. By default OpenLayers uses something they call a "spherical mercator" and without delving into the detail the geometry coordinates are represented by pixels on a 2D plane.
Ideally, you would fix your GeoJSON to provide all coordinates in the same projection. If you can't do that, here is a working solution:
The set that you say aren't working look like longitude and latitude (GIS) coordinates and need to be transformed if they are to be displayed on the same layer - in the following example I've tagged the features that require transform using the GeoJSON properties like so:
var geojsonObject = {
type: 'FeatureCollection',
// ...
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [/* ... */],
properties: {
requiresTransform: true // <- custom property
}
}
},
// ...
]
};
Before adding features to the layer source you could then do something like the following:
var features = (new ol.format.GeoJSON()).readFeatures(geojsonObject);
features.forEach(function(feature){
if(!feature.get('requiresTransform'))
return; // ignore
var geometry = feature.getGeometry(),
coords = geometry.getCoordinates();
if(geometry instanceof ol.geom.Polygon)
geometry.setCoordinates(transformPolyCoords(coords));
});
function transformPolyCoords(/* Array */ a){
return a.map(function(aa){
return aa.map(function(coords){
return ol.proj.transform(coords, 'EPSG:4326', 'EPSG:3857');
});
});
}
There may be a cleaner way of managing this and I'd imagine it involves keeping the separate formats in separate GeoJSON objects and I don't know how close it is to what you were expecting, but this is what I came up with using what you provided » working example.

Google maps : pan to a center of a geojson route clicking on it

i´m new in google maps and after reading here and there I can´t make this work.
I´m doing a google map where Im showing bike forest routes using geojson ( linestring type, are given by coordinates points).
"type": "Feature",
"properties": {
"name": "larouco",
"color":"red"
},
"geometry": {
"type": "LineString",
"coordinates": [ [ -7.634432, 41.955357, 981.6 ], [ -7.635379, 41.954641, 896.1 ], [ -7.635824, 41.953955, 900.9 ] ................ ] }
All fine at the moment.
I need to pan the map to the center of the road when you clicks it.
map.data.loadGeoJson('http://myroutes.json');
map.data.setStyle(function(feature) {
var color = feature.getProperty('color');
return {
strokeColor: color,
strokeWeight: 3
};
});
map.data.addListener('mouseover', function(event) {
map.data.revertStyle();
map.data.overrideStyle(event.feature, {strokeWeight: 8});
});
map.data.addListener('mouseout', function(event) {
map.data.revertStyle();
});
here is where the problems starts
map.data.addListener('click', function(event) {
var center_rute = getCenter(new google.maps.LatLngBounds());
map.panTo(center_container);
//map.setZoom(13);
});
Thinks that LatLngBounds is the way but i´m running in circles...
Thanks,
Jul
You must access the geometry of the feature and create the LatLngBounds on your own by iterating over the points of the path(geometry)

Properly Remove LoadMask Once GeoExt MapPanel is Completely Loaded

I would like to have a mask over the whole page which does not get removed until the entire page has completely loaded. More specifically, I have a map created with OpenLayers and GeoExt and I am trying to use an ExtJS loadMask. However, I have not been able to find any other way of doing this other than using a manual setTimeout which I do not want to use. I'd much rather have the mask removed only if the page is completely loaded. I have tried to use the 'loadend' event on the openLayers map as well as windows.onload etc:
My map and loadMask config:
var mask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
mask.show();
Ext.onReady(function() {
var options = {
controls: [new OpenLayers.Control.Navigation()],
maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
units: 'm',
allOverlays: false
}
var map = new OpenLayers.Map(options);
map.events.register("loadend", map , function() {
mask.hide(); alert('howdy');
});
var mapPanel = new GeoExt.MapPanel({
title: "Map",
map: map,
id: 'mapPanel',
layerStore: map.layers,
//Set the map to be centered at specified longitude/latitude, transform our layers (SRID=4326) to display properly on Google
//base layers (SRID=900913)
center: new OpenLayers.LonLat(95.20, 30.34).transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913")),
zoom: 7,
region: "center",
tbar: [measureLength, '-', measureArea, '-'],
bbar: [
{
xtype: "label",
text: "Scale = 1 : "
}
],
items: [{
xtype: "gx_zoomslider",
vertical: true,
height: 300,
aggressive: true,
x: 10,
y: 20,
plugins: new GeoExt.ZoomSliderTip()
}]
});
It seems this event never happens as I never get an alert message. I really really want to get this working, other attempts were:
window.onload = mask.hide();
after the Ext.onReady and at the end of the </body> tag, but then the mask is hidden way before the map is done loading. Could anyone share some insight, I'd really appreciate it!
Add the event 'onMapReady' just after the items like so:
onMapReady: function() {
Ext.getBody().unmask();
}

Categories

Resources