OpenLayers Google Maps Projection Problem w/ KML - google-maps

This is my first time on stackoverflow and working with Openlayers & Google Maps.
I've been browsing different forums & sites, including OpenLayers.org, to solve my issue. I've done searches on a combination of the following: openlayers, google map projections, and spherical mercator... but I have not found a solution.
Problem: The KML data from a web service call (func setDataSource) is shifting as I zoom in and out of the map. My guess is that the projections in my code are wrong or perhaps wrongly placed. I don't have any background on map projections so it is difficult to digest mapping terminology online :-(. Can someone help?
//start here
var options = {
projection: new OpenLayers.Projection("EPSG:900913"),
displayProjection: new OpenLayers.Projection("EPSG:4326"),
units: "m",
numZoomLevels: 18,
maxResolution: 156543.0339,
maxExtent: new OpenLayers.Bounds(-20037508, -20037508,
20037508, 20037508)};
//*map = new OpenLayers.Map('map');
map = new OpenLayers.Map('map', options);
var gphy = new OpenLayers.Layer.Google(
"Google Street",
{'sphericalMercator':true});
// Add the background images via WMS
var bglayer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
"http://labs.metacarta.com/wms/vmap0", {layers: 'basic'}, {'reproject': true});
//map.addLayer(bglayer);
map.addLayers([gphy, bglayer]);
map.addControl(new OpenLayers.Control.LayerSwitcher());
map.zoomToMaxExtent(); //* Zoom all the way out, this command also initalizes the map
OpenLayers.Console.log("initialized");
}
function setDataSource() {
OpenLayers.Console.log("Setting data source to " + OpenLayers.Util.getElement('loc').value);
if (layer != undefined) {map.removeLayer(layer)};
if (selectControl != undefined) {map.removeControl(selectControl)};
// Encode the destination url as a parameter string.
var params = OpenLayers.Util.getParameterString({url:OpenLayers.Util.getElement('loc').value})
// Make the http request to the transformer, with the destination url as a parameter.
layer = new OpenLayers.Layer.GML("KML", transformerURL + params,
{
format: OpenLayers.Format.KML,
formatOptions: {
extractStyles: true,
extractAttributes: true,
maxDepth: 2,
//projection: new OpenLayers.Projection("EPSG:4326"),
}
});
map.addLayer(layer);
Thank you!!!

I figured out the problem. Instead of GML, I tried using Vector instead like this:
layer = new OpenLayers.Layer.Vector("KML", {
projection: map.displayProjection,
strategies: [new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.HTTP({
url: transformerURL + params,
format: new OpenLayers.Format.KML({
extractStyles: true,
extractAttributes: true
})
})
});
I found the solution in this sundials example: http://openlayers.org/dev/examples/sundials-spherical-mercator.html :-) Hope this helps anyone w/ the same problem.

Related

Combining WMTS and WMS in OpenLayers for custom projection

I want to combine two layers for my map, ortophoto map and on top of that, polygons showing parcel boundaries. For ortophoto I have available WMTS and for polygons WMS.
Projection I need to use is EPSG:3765. The problem is, when I combine these two layers, they don't match at all, polygons are on a completely wrong places.
Initially I tried to implement similar thing in a Leaflet but with using older WMS source for ortophoto map and it worked fine, but since now I wanted to use WMTS, I switched to OpenLayers and cannot get it to work properly. I guess the main problem is projection.
I am completely new to this area so I apologize in advanced if there is some obvious error I did.
Here is my code from main.js, I set projection extent based on info from epsg site, resolution and origin were set based on data found in getCapabilities response:
const projName = 'EPSG:3765';
proj4.defs(projName,"+proj=tmerc +lat_0=0 +lon_0=16.5 +k=0.9999 +x_0=500000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs");
register(proj4);
const projection = getProjection(projName);
projection.setExtent([208311.05, 4608969.52,744179.92, 5161549.72]);
const scaleDenominators = [5000000.0,2500000.0,1000000.0,500000.0,250000.0,100000.0,50000.0,25000.0,10000.0,5000.0,2500.0,1000.0,500.0]; //from getCapabilities
const resolutions = new Array(13);
const matrixIds = new Array(13);
for (let z = 0; z < resolutions.length; ++z) {
resolutions[z] = 0.00028 * scaleDenominators[z];
matrixIds[z] = z;
}
let tileGrid = new WMTSTileGrid({
origin: [-203224.0, 5429184.0], //from getCapabilities topLeftCorner
sizes: [[4,3],[8,6],[20,15],[39,30],[78,60],[194,149],[387,298],[774,596],[1934,1489],[3868,2977],[7735,5953],[19338,14881],[38675,29762]], //from getCapabilities
resolutions: resolutions,
matrixIds: matrixIds,
tileSize: [256, 256],
});
const map = new Map({
layers: [
// main raster
new TileLayer({
opacity: 0.7,
source: new WMTS({
url: 'https://geoportal.dgu.hr/services/auth/orthophoto_2019_2020/wmts?authKey=<key>',
layer: 'DOF5_2019_2020',
matrixSet: 'EPSG3765:256x256',
format: 'image/jpeg',
projection: projection,
tileGrid: tileGrid,
extent: projection.getExtent(),
style: 'default',
wrapX: true,
}),
}),
// layer with parcel boundaries
new TileLayer({
extent: projection.getExtent(),
source: new TileWMS({
url: 'https://www.geohrvatska.hr/viewer/oss/wms',
params: {
'LAYERS': 'oss:DKP_CESTICE',
'format': 'image/png',
'version': '1.1.1',
'crs' : projName,
},
serverType: 'geoserver',
transition: 0,
projection: projection,
}),
}),
],
target: 'map',
view: new View({
projection: projName,
center: [477174.25,4882262.63],
zoom: 1
}),
});
Here is a snipet of getCapabilities response displaying TileMatrixSet used:
<TileMatrixSet>
<ows:Identifier>EPSG3765:256x256</ows:Identifier>
<ows:SupportedCRS>urn:ogc:def:crs:EPSG::3765</ows:SupportedCRS>
<TileMatrix>
<ows:Identifier>0</ows:Identifier>
<ScaleDenominator>5000000.0</ScaleDenominator>
<TopLeftCorner>-203224.0 5429184.0</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>4</MatrixWidth>
<MatrixHeight>3</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>1</ows:Identifier>
<ScaleDenominator>2500000.0</ScaleDenominator>
<TopLeftCorner>-203224.0 5429184.0</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>8</MatrixWidth>
<MatrixHeight>6</MatrixHeight>
</TileMatrix>
result:
result
EDIT
After a comment from Mike, I tried displaying my WMTS on top of OSM. For tileMatrix=4, it is aligned while from tileMatrix=5 it is not. I am uploading pictures for demonstration:
tileMatrix=4
tileMatrix=5

Leaflet MarkerCluster with GeoJson

I am currently working on a Leaflet Project where I use external geojson files as data input. Since the json contains a lot of objects I would like to use the MarkerCluster plugin which I got from Mappbox:
<script src='https://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-markercluster/v0.4.0/leaflet.markercluster.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-markercluster/v0.4.0/MarkerCluster.css' rel='stylesheet' />
<link href='https://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-markercluster/v0.4.0/MarkerCluster.Default.css' rel='stylesheet' />
Displaying the json-layer without the clustering works just fine, but if i try to assign it to the cluster nothing is displayed.
var markersBar = L.markerClusterGroup();
var barLayer = new L.GeoJSON.AJAX("json/eat_drink/bar.geojson", {
pointToLayer: function(feature, latlng) {
var icon = L.icon({
iconSize: [27, 27],
iconAnchor: [13, 27],
popupAnchor: [1, -24],
iconUrl: 'icon/' + feature.properties.amenity + '.png'
});
return L.marker(latlng, {icon: icon})
},
onEachFeature: function(feature, layer) {
layer.bindPopup(feature.properties.name + ': ' + feature.properties.opening_hours);
}
});
markersBar.addLayer(barLayer);
console.log(markersBar);
map.addLayer(markersBar);
The console.log output lets me assume that there are no objects, but I don't get it why.
Object { options: Object, _featureGroup: Object, _leaflet_id: 24, _nonPointGroup: Object, _inZoomAnimation: 0, _needsClustering: Array[0], _needsRemoving: Array[0], _currentShownBounds: null, _queue: Array[0], _initHooksCalled: true }
What am I doing wrong?
Well it looks like you are using Leaflet-Ajax...so an async request is made to grab your geojson..and your immediate next line is markersBar.addLayer(barLayer);..which would contain nothing since the request is almost certainly not complete yet...
Instead, I believe you can use the loaded event provided in the documentation like
barLayer.on('data:loaded', function () {
markersBar.addLayer(barLayer);
console.log(markersBar);
map.addLayer(markersBar);
});
For anyone looking for a straight forward example for adding a marker cluster with geojson ajax to a map, binding pop-ups and adding to layer control:
// pop-up function
function popUp(f, l) {
var out = [];
if (f.properties) {
for (key in f.properties) {
out.push(key + ": " + f.properties[key]);
}
l.bindPopup(out.join("<br />"));
}
}
// add layer to map and layer control
function add_layer(layr, layr_name) {
map.addLayer(layr);
layerControl.addOverlay(layr, layr_name);
}
// fire ajax request
var points = new L.GeoJSON.AJAX("../data/points.geojson", { onEachFeature: popUp });
// create empty marker cluster group
var markers = L.markerClusterGroup()
// when geojson is loaded, add points to marker cluster group and add to map & layer control
points.on('data:loaded', function () {
markers.addLayer(points);
add_layer(markers, "Point Markers")
});

Google Map Animate ImageMapType Overlay

I have been reading and researching how to animate overlays. I Haven't found anything related to what I am looking for. Mostly it was related to markers. I have an overlay I apply like this and it works great.
tileNEX = new google.maps.ImageMapType({
getTileUrl: function(tile, zoom) {
return "http://mesonet.agron.iastate.edu/cache/tile.py/1.0.0/nexrad-n0q-900913/" + zoom + "/" + tile.x + "/" + tile.y +".png?"+ (new Date()).getTime();
},
tileSize: new google.maps.Size(256, 256),
opacity: 0.60,
name: 'NEXRAD',
isPng: true
});
The source for the data also offers 10 other past images. So I would like to create an animated loop with those feeds. Is this option available in V3 as I have read some conflicts with doing this. I mean it must be possible because I have seen others with it. How would I go about loading the multiple layers then animating them?
-Thanks!
I know this is old but I hope this helps someone else looking for the same thing. This probably isn't the most elegant solution but it got the task done. I simply map over the predefined image urls creating my ImageMapTypes and then pass that into an animation loop which checks to see if there is a layer on the map, clears if it there is and then sets the new layer based on a loop count. Hope this helps.
var map;
// Weather tile url from Iowa Environmental Mesonet (IEM): http://mesonet.agron.iastate.edu/ogc/
var urlTemplate = 'http://mesonet.agron.iastate.edu/cache/tile.py/1.0.0/nexrad-n0q-{timestamp}/{zoom}/{x}/{y}.png';
// The time stamps values for the IEM service for the last 50 minutes broken up into 5 minute increments.
var timestamps = ['900913-m50m', '900913-m45m', '900913-m40m', '900913-m35m', '900913-m30m', '900913-m25m', '900913-m20m', '900913-m15m', '900913-m10m', '900913-m05m', '900913'];
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 38.0781, lng: -97.7030},
zoom: 5
});
let tileSources = timestamps.map((timestamp) => {
return new google.maps.ImageMapType({
getTileUrl: function(tile, zoom) {
const { x, y} = tile;
return `https://mesonet.agron.iastate.edu/cache/tile.py/1.0.0/nexrad-n0q-${timestamp}/${zoom}/${x}/${y}.png?`+ (new Date()).getTime();
},
tileSize: new google.maps.Size(256, 256),
opacity:0.60,
name : 'NEXRAD',
isPng: true
});
});
startAnimation(map, tileSources);
}
function startAnimation(map, layers) {
// create empty overlay entry
map.overlayMapTypes.push(null);
var count = 0;
window.setInterval(() => {
if(map.overlayMapTypes.getLength() > 0)
map.overlayMapTypes.clear();
map.overlayMapTypes.setAt("0",layers[count]);
count = (count + 1) % layers.length;
},800);
}

show/hide kml on defined zoom levels

I´m trying to hide/show my own kml files (polygons) depending on zoom levels in OpenLayers - when reached certain zoom level one layer should hide and another show. So far I found this solution (How to load layers depending on zoom level?), but it doesn´t seem to be working in my case. I´m relatively new to javascript and I don´t know if I´m using this right, I also made some changes to the example:
map.events.register("zoomend", map, zoomChanged); //inserted in function init()
function zoomChanged()
{
if (map.getZoom() == 18)
{
kml1.setVisibility (true);
kml2.setVisibility (false);
}
else if (map.getZoom() == 19)
{
kml1.setVisibility (false);
kml2.setVisibility (true);
}
}
I also tried another solution to hide kml1, but in this case my layer isn´t drawn. The LayerSwitcher works - the layer is unselectable in defined zoom levels, but nothing is visible when zoomed out (when layer is already selectable):
var kml1 = new OpenLayers.Layer.Vector("prehled",
{minScale: 1000,}, //1:1000
{
projection: map.displayProjection,
strategies: [new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.HTTP({
url: "kml/zahrada.kml",
format: new OpenLayers.Format.KML({
extractStyles: true,
extractAttributes: true,
})
})
});
map.addLayer(kml1);
Thanks for any response and advice on this.
Try:
var kml1 = new OpenLayers.Layer.Vector("prehled", {
minResolution: map.getResolutionForZoom(18), // or the desired maximum zoom
projection: map.displayProjection,
strategies: [new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.HTTP({
url: "kml/zahrada.kml",
format: new OpenLayers.Format.KML({
extractStyles: true,
extractAttributes: true
})
})
});
map.addLayer(kml1);
```

WFS Layer in OpenLayers queries server for GML data but doesn't display the returned data

I have added a WFS layer to a map and can see (using Fiddler) a request being made to the server for the layer data. The server uses GML as the data format and the data being returned is valid. However, OpenLayers does not display the data. Here is the code that I use.
$(document).ready(
function () {
// allow testing of specific renderers via "?renderer=Canvas", etc
var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
var geographic = new OpenLayers.Projection("EPSG:4326");
var mercator = new OpenLayers.Projection("EPSG:900913");
map = new OpenLayers.Map({
div: "map",
layers: [
new OpenLayers.Layer.WMS("OpenLayers WMS",
"http://vmap0.tiles.osgeo.org/wms/vmap0",
{ layers: "basic" }
),
new OpenLayers.Layer.Vector("GML", {
strategies: [new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.WFS({
url: "http://localhost/MapServer/Default.aspx",
featureType: "Layer_ACTIVE",
featureNS: "http://www.tstgis.org/gml",
version: "1.1.0",
geometryName: "line"
}),
renderers: renderer
})
],
zoom: 15
});
var bb = new OpenLayers.Bounds(-179.821327209473, 12.1057098342161, -56.5289154052734, 78.1442901657839);
map.zoomToExtent(bb);
});
This test script is part of an HTML shell that is running under localhost/mapserver, so it rules out the familiar cross-domain issue.
What is going on? Note: the returned data set is pretty big (1.5 MB) and I wonder if that has anything to do with the missing display.
I had no doubt that this was a configuration issue with OpenLayers. From what I knew about OpenLayers, I had it configured properly. Turns out, it is essential, in my case, to set the featurePrefix option. The reason for this is that the namespace and prefix in the XML response (GML) for each FeatureMember Node is used to identify the appropriate reader for the response. By default, the featurePrefix is set to ‘feature’. If the response's namespace + prefix does not match the configuration in OpenLayers, the features are not added to the layer and hence not displayed. In my case, the prefix is set to an empty string as the server does not add a prefix to the response.
Also, setting the geometryName is critical. The default value for geometryName is ‘the_geom’ for WFS version 1.0, and null for higher versions. The geometryName is used by the server to actually locate the features. In my case, geometryName used on the server was "msGeometry".
Here is the working code.
$(document).ready(
function () {
// allow testing of specific renderers via "?renderer=Canvas", etc
var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
var geographic = new OpenLayers.Projection("EPSG:4326");
var mercator = new OpenLayers.Projection("EPSG:900913");
map = new OpenLayers.Map({
div: "map",
layers: [
new OpenLayers.Layer.WMS("OpenLayers WMS",
"http://vmap0.tiles.osgeo.org/wms/vmap0",
{ layers: "basic" }
),
new OpenLayers.Layer.Vector("GML", {
strategies: [new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.WFS({
url: "http://localhost/MapServer/Default.aspx",
featureType: "Data_ACTIVE",
version: "1.1.0",
geometryName: "msGeometry",
featurePrefix: ""
}),
renderers: renderer
})
],
zoom: 15
});
var bb = new OpenLayers.Bounds(-179.821327209473, 12.1057098342161, -56.5289154052734, 78.1442901657839);
map.zoomToExtent(bb);
});