How to properly overlay WMS tiles on google maps? WMS tiles are shifted when overlayed on Google Maps - google-maps-api-3

I am getting started with GIS.
I am trying to publish a Geography Markup Language (GML) file on geoserver. For this I converted it to a shape file using ogr2ogr tool
ogr2ogr -f "ESRI Shapefile" adur.shp adur.gml
I published the converted Shapefile on geoserver and loaded it in my application as a WMS layer. Here is the jsfiddle link for the below code snippet https://jsfiddle.net/3kmwpsu2/
<!DOCTYPE html>
<html>
<head>
<title>WMS and Google Maps</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<style>
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
const EXTENT = [-Math.PI * 6378137, Math.PI * 6378137];
const xyzToBounds = (x, y, z) => {
let tileSize = (EXTENT[1] * 2) / Math.pow(2, z);
let minx = EXTENT[0] + x * tileSize;
let maxx = EXTENT[0] + (x + 1) * tileSize;
// remember y origin starts at top
let miny = EXTENT[1] - (y + 1) * tileSize;
let maxy = EXTENT[1] - y * tileSize;
return [minx, miny, maxx, maxy];
}
const getWMSTileUrl = (coordinates, zoom) => {
return (
"http://wms.prod.qgiscloud.com/abhilash/cloud" +
"?REQUEST=GetMap&SERVICE=WMS&VERSION=1.1.0" +
"&LAYERS=adur" +
"&FORMAT=image%2Fpng" +
"&SRS=EPSG:3857&WIDTH=256&HEIGHT=256&TRANSPARENT=TRUE" +
"&BBOX=" +
xyzToBounds(coordinates.x, coordinates.y, zoom).join(","))
};
function initMap() {
let map = new google.maps.Map(document.getElementById("map"), {
zoom: 19,
center: {
lat: 50.83238,
lng: -0.330478
},
tilt: 0,
mapTypeId: window.google.maps.MapTypeId.HYBRID
});
let layer = new google.maps.ImageMapType({
getTileUrl: getWMSTileUrl,
minZoom: 0,
maxZoom: 19,
opacity: 1.0
});
layer.addListener("tilesloaded", () => {
console.log("Layers loaded")
});
map.overlayMapTypes.push(layer);
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?&callback=initMap">
</script>
</body>
</html>
I noticed that the boundaries are not aligned properly. I see some shift in the boundaries.
Note : Please note that I used QGIS cloud's WMS link in the demo instead of my local geoserver's WMS URL
I imported the same shape file into QGIS to check if there is anything wrong with the converted Shapefile. But the borders are aligned properly here. I am attaching both the screenshots below for comparison. You can see that the borders are not aligned properly in the first image. There is some shift here.
I am attaching the GML and converted Shape files here for your reference.
This screenshot is from my application where the borders are not aligned properly.
This screenshot is from QGIS where borders are aligned properly
UPDATE :
After several attempts, I added same WMS layer twice in QGIS.
One with EPSG:4326 - WGS 84 CRS
And the other with EPSG:27700 OSGB36 / British National Grid - CRS
This is the result. The WMS layer with EPSG:27700 CRS aligned perfectly.
But I am not able to do the same with GeoServer / in my application. CRS on GeoServer is already in EPSG:27700.

Related

Leaflet: How to handle touch events (e.g. on mobile devices)

Please take a look at the code below. It's drawing a circle onto the map. If the user clicks with the mouse or taps with his finger (on a mobile device) into the circle and drags it, the circle should be moved across the map.
This works for Desktop Firefox, Desktop Chrome, Mobile Firefox. But not with Mobile Chrome. I think that the code in Mobile Firefox maybe just works because the browser emulates touch-inputs to mouse-inputs which of course are working well in Leaflet.
So I need help how to best implement touch events (parallel to mouse events) within Leaflet. Touch events are not mentioned in Leaflet's Doc. But i think the usual Javascript DOM-events should work too? (touchstart, touchmove, touchend). But not within Chrome mobile the way my code is written.
<!DOCTYPE html>
<html>
<head>
<title>Eventtest</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.1.0/dist/leaflet.css"/>
<script src="https://unpkg.com/leaflet#1.1.0/dist/leaflet.js"></script>
</head>
<body>
<div id="mapid" style="width: 600px; height: 400px;"></div>
<script>
var mymap = L.map('mapid').setView([51.505, -0.09], 13);
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: 'mapbox.streets'
}).addTo(mymap);
circle = L.circle([51.508, -0.11], 500, {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.5
}).addTo(mymap);
circle.on('mousedown touchstart', onCircleDown);
function onCircleDown (e1) {
mymap.dragging.disable();
var mouseStartingLat = e1.latlng.lat;
var mouseStartingLng = e1.latlng.lng;
var circleStartingLat = e1.target._latlng.lat;
var circleStartingLng = e1.target._latlng.lng;
circle.on('mousemove', function (e2) {
var mouseNewLat = e2.latlng.lat;
var mouseNewLng = e2.latlng.lng;
var latDifference = mouseNewLat - mouseStartingLat;
var lngDifference = mouseNewLng - mouseStartingLng;
var currentCircleCoords = L.latLng (circleStartingLat + latDifference, circleStartingLng + lngDifference);
e1.target.setLatLng (currentCircleCoords);
});
circle.on ('mouseup', function () {
circle.off('mousemove');
mymap.dragging.enable();
});
}
</script>
</body>
</html>

Display geoserver WMS layer on Google MAP where datasource for Geoserver is PostGreSQL

I am working on a web application of ASP.NET with the following API/tools Google Map, GeoServer, PostGreSQL(With POSTGIS).
I have a requirement to display WMS layer of GeoServer on the google map V3.26.Data source of GeoServer is POSTGIS. I have inserted hundreds of MULTILINESTRING in PostGreSQL(With POSTGIS) using the below given scripts.
INSERT INTO public."LineData"("ID", "LineCoordinates") values('11195625',ST_GEOMFROMTEXT('MULTILINESTRING ((<Latitude Longitude>))',4326));
Then publish a layer in Geoserver that use the data from "LineData" table and keep Native SRC EPSG:4326. To display this layer on Google Map V3.26, I have used the below given code.
The problem is that tiles are not rendering correctly. They are broken and shown at wrong places. Can anyone tell what wrong I am doing?
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Google Map with Geoserver WMS Overlay</title>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.26&language=en-US"></script>
<style type="text/css">
html {
height: 100%;
}
body {
height: 100%;
margin: 0px;
padding: 0px;
}
#map_canvas {
height: 100%;
}
</style>
</head>
<body onload="initialize()">
<h1>Google Map with Geoserver WMS and KML Overlay</h1>
<div id="map_canvas"></div>
</body>
</html>
<script type="text/javascript">
var TILE_SIZE = 256;
var wmsparams = [
"SERVICE=WMS",
"VERSION=1.1.1",
"REQUEST=GetMap",
"FORMAT=image/png",
"TRANSPARENT=TRUE",
"BGCOLOR=0xFFFFFF",
"SRS=EPSG:4326",
"WIDTH=256",
"HEIGHT=256"
];
</script>
<script type="text/javascript">
function initialize() {
// Create a MapOptions object with the required properties for base map
var mapOptions = {
zoom: 9,
center: new google.maps.LatLng(36.04450, -80.34747),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
// Create the base map
map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);
//Define custom WMS layer for census output areas in WGS84
var censusLayer =
new google.maps.ImageMapType(
{
getTileUrl: function (coord, zoom) {
// Compose URL for overlay tile
var s = Math.pow(2, zoom);
var twidth = 256;
var theight = 256;
//latlng bounds of the 4 corners of the google tile
//Note the coord passed in represents the top left hand (NW) corner of the tile.
var gBl = map.getProjection().fromPointToLatLng(new google.maps.Point(coord.x * twidth / s, (coord.y + 1) * theight / s)); // bottom left / SW
var gTr = map.getProjection().fromPointToLatLng(new google.maps.Point((coord.x + 1) * twidth / s, coord.y * theight / s)); // top right / NE
// Bounding box coords for tile in WMS pre-1.3 format (x,y)
var bbox = parseFloat(gBl.lat()) + "," + parseFloat(gBl.lng()) + "," + parseFloat(gTr.lat()) + "," + parseFloat(gTr.lng());
//base WMS URL
var url = "http://localhost:8080/geoserver/TestWorkSpace/wms?";
url += "&service=WMS"; //WMS service
url += "&version=1.1.0"; //WMS version
url += "&request=GetMap"; //WMS operation
url += "&layers=TestLayer"; //WMS layers to draw
url += "&styles="; //use default style
url += "&format=image/png"; //image format
url += "&TRANSPARENT=TRUE"; //only draw areas where we have data
url += "&srs=EPSG:4326"; //projection WGS84
url += "&bbox=" + bbox; //set bounding box for tile
url += "&width=256"; //tile size used by google
url += "&height=256";
//url += "&tiled=true";
return url; //return WMS URL for the tile
},
tileSize: new google.maps.Size(256, 256),
opacity: 0.85,
isPng: true
});
// add WMS layer to map
map.overlayMapTypes.push(censusLayer);
}
</script>
Thanks
You are asking GeoServer to return you the tiles in epsg:4326 while your map is in epsg:3857, hence they look wrong.
So you need to change the line:
url += "&srs=EPSG:4326";
to
url += "&srs=EPSG:3875";
You also need to specify the bounding box in your map coordinates.

Openlayers 3 : Issue in Changing Extents of the Map

I'm facing an issue in changing the extents of the map in my openlayers 3 code.
I'm using EPSG:4326 as the projection format and I intent to change the bounds
to my 'bounds' variable. I'm using
map.getView().fitExtent(bounds, map.getSize());
for changing the extent of the map. The issue is that first I'm providing bounds
values to be [76.9501571655273, 76.9501571655273,78.5841217041016, 78.5841217041016], and after fitting the extent of the map to this value, when I enquire the map extents using
map.getView().calculateExtent(map.getSize());
I'm getting [76.39384841918945, 76.39384841918945, 79.14043045043945, 79.14043045043945] as output. I have a feeling that this might be a projection issue, but I can't figure out where I have done wrong. Can someone please let me know what am I doing wrong?
Here is the Full Code (I've removed unnecessary code to make it more readable):
<!doctype html>
<html lang="en">
<head>
<script src="http://openlayers.org/en/v3.5.0/build/ol-debug.js"></script>
<title>OpenLayers map preview</title>
</head>
<body>
<div id="map" style="width: 500px; height: 500px;"></div>
<script type="text/javascript">
var format = 'image/png';
var bounds = [76.9501571655273, 76.9501571655273,
78.5841217041016, 78.5841217041016];
var untiled = new ol.layer.Image({
source: new ol.source.ImageWMS({
ratio: 1,
url: 'http://localhost:9000/geoserver/TargetWorkspace/wms',
params: {'FORMAT': format,
'VERSION': '1.1.1',
LAYERS: 'TargetWorkspace:hyderbadtargets',
STYLES: '',
}
})
});
var projection1 = new ol.proj.Projection({
code: 'EPSG:4326',
units: 'degrees',
axisOrientation: 'neu'
});
var map = new ol.Map({
controls: ol.control.defaults({
attribution: false
}),
target: 'map',
layers: [
untiled
],
view: new ol.View({
projection: projection1,
center: [77.7671394348, 77.7671394348],
zoom: 0
})
});
var ext = map.getView().calculateExtent(map.getSize());
console.log("Map Bound Before: " + ext[0] + " " + ext[1] + " "+ ext[2] + " "+ ext[3] + " ");
map.getView().fitExtent(bounds, map.getSize());
var ext2 = map.getView().calculateExtent(map.getSize());
console.log("Map Bound After: " + ext2[0] + " " + ext2[1] + " "+ ext2[2] + " "+ ext2[3] + " ");
</script>
</body>
</html>
Thank You,
Sourabh
WMS provides images with predefined scales/resolutions, for example zoom 8 can have resolution 1222.992452 and scale 1:3000000. How is possible to adapt discrete zoom values to the bounds that you provides? OL3 finds for you the best zoom level whose bound coordinates are closed to the bounds that you wants.
Additionally, i have noticed in your code that you redefine projection EPSG:4386 with:new ol.proj.Projection({......
However, OL3 contains already in the library itself this projection. Better to use: new ol.proj.get('EPSG:4326');
If you are calling fitExtent OpenLayers adjusts the view bounds so that the given extent is visible. If the dimensions of the extent do not match the dimensions of the size of the map, the actual view extent might be larger.
As stated in the answer by favero, it only uses diescrete zoom values at default, however this can be avoided by setting the option constrainResolution false. Then the zoom level is allowed to be a floating point number.
Also note that fitExtent is now called fit
map.getView().fit(bounds, { constrainResolution: false })

Google maps custom marker icon configuration from sprite sheet with retina support

My Google map uses a custom Marker pin loaded from a sprite sheet, everything works as expected for 1x, 2x, and 3x displays expect #2x, and #3x the image shows up as twice of three times the size of the original image.
If I try and scale it back to to the 1x dimensions when displaying at #2x and #3x the marker shows up without the image.
Googleing around suggests that I am doing everything right, Icon configuration is based on the documentation.
Is anyone able to spot what might be wrong with my scaledImage configuration?
if(devicePixelRatio > 1) {
pinImage['scaledSize'] = new google.maps.Size(15, 36);
}
Code extract from JavaScript:
var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
// Retina support
var requireImagePrefix = "";
var devicePixelRatio = (window.devicePixelRatio===undefined?1:window.devicePixelRatio);
if(devicePixelRatio == 2) {
requireImagePrefix = "#2x";
}
else if(devicePixelRatio == 3) {
requireImagePrefix = "#3x";
}
// Image for the marker
var pinImage = {
url: "/assets/sprite" + requireImagePrefix + ".png",
size: new google.maps.Size(15*devicePixelRatio, 36*devicePixelRatio),
origin: new google.maps.Point(430*devicePixelRatio, 20*devicePixelRatio),
};
if(devicePixelRatio > 1) {
pinImage['scaledSize'] = new google.maps.Size(15, 36);
}
var officeMarker = new google.maps.Marker({
position: hqLocation,
map: map,
title: "Anomaly HQ",
icon: pinImage,
optimized: false
});
Thanks very much for your time.
My error in the above are:
The scaledSize parameter is meant to be the dimensions of the 1x sprite
We don't need to multiply the size and origin parameters by the aspect ratio
I have posted the updated solution as a Git oh Github

Google Maps api v3 - circle without fill or unclickable fill

.. and sorry for my english.
I'm trying to create circle without fill with google maps api v3. I know that I can set opacity (and fill disapired), but this no-visible fill is still clickable. I need to set fill unclickable, but stroke have to be clickable. Under my circle there are few points and lines, that have to be clickable and circle is upper, than lines, for example. My idea was move circle to different pane, but I didn't find way how to do it. Is there a solution for it?
Thanks!
Ajax
[EDIT]
Here is code sample. My problem is, that I'm not able to click on map or line inside blue circle.
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0; padding: 0 }
#map_canvas { height: 100% }
</style>
<script type="text/javascript"
src="http://maps.googleapis.com/maps/api/js?key=YOUR_GMAP+API_KEY&sensor=true">
</script>
<script type="text/javascript">
function initialize() {
var lat = 49;
var lon = 15;
var myOptions = {
center: new google.maps.LatLng(lat,lon),
zoom: 18,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
google.maps.event.addListener(map, "click", function(clickEvt) {
alert('mapClicked');
});
var line = new google.maps.Polyline({
path: [new google.maps.LatLng(lat + 0.001, lon + 0.001),new google.maps.LatLng(lat - 0.001, lon - 0.001)],
strokeColor: '#00FF00',
map: map
});
google.maps.event.addListener(line, "click", function() {
alert('Line clicked');
});
var circle = new google.maps.Circle({
strokeColor: '#0000FF',
strokeWeight: 2,
fillOpacity: 0,
map: map,
center: new google.maps.LatLng(lat, lon),
radius: 100
});
google.maps.event.addListener(circle, 'click', function(event) {
alert('circle clicked');
});
}
</script>
</head>
<body onload="initialize()">
<div id="map_canvas" style="width:100%; height:100%"></div>
</body>
</html>
set 'clickable: false` in the circle option object to allow the click event be detected in the map layer.
var circle = new google.maps.Circle({
strokeColor: '#0000FF',
strokeWeight: 2,
fillOpacity: 0,
map: map,
center: new google.maps.LatLng(lat, lon),
radius: 100,
clickable: false
});
reference: Unclickable fill in Circle?
Try this: draw 2 circles, one for your outline (clickable) and one for your fill (not clickable). The fill circle should be slightly smaller, and set its z-index to sit on top of the other one, preventing you clicking on anything but the outline circle. The problem will be the other items you want to remain clickable underneath the circle.

Resources