Combining WMTS and WMS in OpenLayers for custom projection - gis

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

Related

Revit shared coordinates to Forge viewer

What is the correct process for getting a transform between Forge coordinates and Revit's shared coordinates? I know there is globalOffset, but does it reference the Revit project internal coordinate system or shared coordinates?
Update Jun 11th, 2021
Now my MultipleModelUtil.js supports the alignments I shared below. Also, we can easily tell Forge Viewer to use By shared coordinates to aggregate models. Here is the code snippet, and you can check out here to know supported alignments
const util = new MultipleModelUtil( viewer );
util.options = {
alignment: MultipleModelAlignmentType.ShareCoordinates
};
const models = [
{ name: '1.rvt', urn: 'urn:dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLlNpaHgxOTVuUVJDMHIyWXZUSVRuZFE_dmVyc2lvbj0x' },
{ name: '2.rvt', urn: 'urn:dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLldVRHJ4ajZ6UTBPLTRrbWZrZ3ZoLUE_dmVyc2lvbj0x' },
{ name: '3.rvt', urn: 'urn:dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLjRyZW5HRTNUU25xNHhYaW5xdWtyaWc_dmVyc2lvbj0x' }
];
util.processModels( models );
==================
First, Forge Viewer supports 3 kinds of Revit link methods as the below, and you can take a look at the 3rd one (By shared coordinates).
Origin to origin: Apply the globalOffset of the 1st model to others. Check MultipleModelUtil/MultipleModelUtil.js for the demo
Center to center: the default way of the viewer.
By shared coordinates: set up applyRefpoint: true and make the globalOffset to the refPoint. This method is the one you are looking for.
The refPoint is the Revit survey point location inside Revit internal coordinate system. It's accessible with the AecModelData. Meanwhile, you can take advantage of the AggregatedView to use this aligning option. Here is an example of telling how to use AggregatedView:
https://gist.github.com/yiskang/c404af571ba4d631b5929c777503891e
If you want to use this logic with the Viewer class directly, here is a code snippet for you:
let globalOffset = null;
const aecModelData = bubbleNode.getAecModelData();
const tf = aecModelData && aecModelData.refPointTransformation; // Matrix4x3 as array[12]
const refPoint = tf ? { x: tf[9], y: tf[10], z: 0.0 } : { x: 0, y: 0, z: 0 };
// Check if the current globalOffset is sufficiently close to the refPoint to avoid inaccuracies.
const MaxDistSqr = 4.0e6;
const distSqr = globalOffset && THREE.Vector3.prototype.distanceToSquared.call(refPoint, globalOffset);
if (!globalOffset || distSqr > MaxDistSqr) {
globalOffset = new THREE.Vector3().copy(refPoint);
}
viewer.loadDocumentNode(doc, bubbleNode, { applyRefpoint: true, globalOffset: globalOffset, keepCurrentModels: true });
The bubbleNode can be either of the following:
bubbleNode = doc.getRoot().getDefaultGeometry()
//Or
const viewables = viewerDocument.getRoot().search({'type':'geometry'});
bubbleNode = viewables[0];
To get AecModelData, please refer to my gist: https://gist.github.com/yiskang/c404af571ba4d631b5929c777503891e#file-index-html-L67
// Call this line before using AecModelData
await doc.downloadAecModelData();
// doc.downloadAecModelData(() => resolve(doc));
See here for details of the AecModelData: https://forge.autodesk.com/blog/consume-aec-data-which-are-model-derivative-api
I've also found success feeding the refPointTransformation into a matrix4.
This way, the orientation of the model is also taken into account. (This is based off Eason's Answer).
const bubbleNode = doc.getRoot().getDefaultGeometry();
await doc.downloadAecModelData();
const aecModelData = bubbleNode.getAecModelData();
const tf = aecModelData && aecModelData.refPointTransformation;
const matrix4 = new THREE.Matrix4()
.makeBasis(
new THREE.Vector3(tf[0], tf[1], tf[2]),
new THREE.Vector3(tf[3], tf[4], tf[5]),
new THREE.Vector3(tf[6], tf[7], tf[8])
)
.setPosition(new THREE.Vector3(tf[9], tf[10], tf[11]))
viewer.loadDocumentNode(doc, viewables, {
placementTransform: matrix4,
keepCurrentModels: true,
globalOffset: {
"x": 0,
"y": 0,
"z": 0
},
applyRefpoint: true,
applyScaling: 'ft',
})

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);
});

OpenLayers Google Maps Projection Problem w/ KML

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.