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

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

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

Use Data Visualization Extension multiple times

I have two separate extensions visualizing different "datapoints" from different domains (measurements like temperature, humidity, ... and image locations). These extensions should remain separated (and should not know each other).
There is no problem when using the extensions independent of each other. But problems occur, if both extensions are used at the same time (for the same model) in the Forge Viewer.
Both extensions try to get an existing instance of the DataVis-extension and load the extension if it is not available (call to viewer.loadExtension in each custom extension no difference).
The extensions create ViewableData and add viewables to it (sharing a single instance of ViewableData makes no difference).
After adding viewables await viewableData.finish() is called and the addViewables-method of the DataVis-extension is called.
One of the main problems is, that method changeOcclusion changes the occlusion only of the viewables which were added last. The other viewables remain visible at any time.
Probably because the pointMaterial in the DataVis-extension is overwritten any time the addViewables extension is called.
Is there a way to instantiate the extension multiple times, so that it is guaranteed that there are no side-effects when using it from within different custom extensions? Or maybe other mechanisms?
After consulting our engineering team, we submitted an issue report, LMV-6574, for tracking this issue. Please take note of the LMV prefixed id for tracking. We're welcome to ask for updates in the future by sending the issue id to forge (DOT) help (AT) autodesk (DOT) com.
However, we don't want to stop your development, so here is a workaround.
As I mentioned in the above comments area, SpriteViewable's constructor accepts an argument, ViewableStyles, that is used to set up the sprite icon. So, you don't need to call DataVisualization.addViewables(data) twice. Before finishing the ViewableData, you can add viewables with different ViewableStyles without a doubt.
Back to your use case, you want to reuse the DataVisualization extension for different data sources. To do so, I would advise you to store your device (sensor) data separately (e.g. device container). When you need to add/remove devices, just modify the device container, clear viewables, and then add new viewalbe data among your device container.
Here are some code snippets demonstrating this idea:
Example code for initializing:
let sensorStyleDefinitions = {
co2: {
url: "http://localhost:3000/assets-1/images/co2.svg",
color: 0xffffff,
},
temperature: {
url: "http://localhost:3000/assets-1/images/thermometer.svg",
color: 0xffffff,
},
default: {
url: "http://localhost:3000/assets-1/images/circle.svg",
color: 0xffffff,
},
};
// Create model-to-style map from style definitions.
let styleMap = {};
Object.entries(sensorStyleDefinitions).forEach(([type, styleDef]) => {
styleMap[type] = new Autodesk.DataVisualization.Core.ViewableStyle(
Autodesk.DataVisualization.Core.ViewableType.SPRITE,
new THREE.Color(styleDef.color),
styleDef.url
);
});
let devices = [
{
id: "Hall I",
position: {
x: -14.297511041164398,
y: -77.6432056427002,
z: 11.31889820098877,
},
type: "temperature",
sensorTypes: ["temperature"],
},
{
id: "Hall IV",
position: {
x: 60.53697395324707,
y: -74.6432056427002,
z: 11.31889820098877,
},
type: "co2",
sensorTypes: ["co2"],
},
];
const viewableData = new Autodesk.DataVisualization.Core.ViewableData();
viewableData.spriteSize = 16;
// Add viewables
devices.forEach((device, index) => {
const style = styleMap[device.type] || styleMap["default"];
const viewable = new Autodesk.DataVisualization.Core.SpriteViewable(
device.position,
style,
index + 1
);
viewableData.addViewable(viewable);
});
await viewableData.finish();
dataVizExt.addViewables(viewableData);
Example code for adding a device(sensor)
devices.push({
id: "Hall XII",
position: {
x: -15,
y: -70,
z: 50,
},
type: "temperature",
sensorTypes: ["temperature"],
});
// Remove existing sprites
dataVizExt.removeAllViewables();
const viewableData = new Autodesk.DataVisualization.Core.ViewableData();
viewableData.spriteSize = 16;
// re-add viewables
devices.forEach((device, index) => {
const style = styleMap[device.type] || styleMap["default"];
const viewable = new Autodesk.DataVisualization.Core.SpriteViewable(
device.position,
style,
index + 1
);
viewableData.addViewable(viewable);
});
await viewableData.finish();
dataVizExt.addViewables(viewableData);
Example code for removing a device(sensor)
devices = devices.splice(1, 1);
// Remove existing sprites
dataVizExt.removeAllViewables();
const viewableData = new Autodesk.DataVisualization.Core.ViewableData();
viewableData.spriteSize = 16;
// re-add viewables
devices.forEach((device, index) => {
const style = styleMap[device.type] || styleMap["default"];
const viewable = new Autodesk.DataVisualization.Core.SpriteViewable(
device.position,
style,
index + 1
);
viewableData.addViewable(viewable);
});
await viewableData.finish();
dataVizExt.addViewables(viewableData);

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',
})

$http.get() returns blank data in AngularJS from external source

I tried to fetch data from external sources in Angular JS via $http but it returns blank data. The same service when used to fetch data from a local file works perfect.
The code for fetching data from local source:
controllers.controller('listController', ['$scope', '$http',
function($scope,$http) {
var data = null;
$http.get('js/test.json').success(function(data) {
$scope.markers = data.hooks;
});
$scope.map = {
center: {
latitude: 45,
longitude: -73
},
zoom: 2
};
}]);
The code for fetching data from an external source:
controllers.controller('listController', ['$scope', '$http',
function($scope,$http) {
var data = null;
$http.get('http://115.113.151.200:8081/user/maphook/mobile/generate_list.jsp').success(function(data) {
$scope.markers = data.hooks;
});
$scope.map = {
center: {
latitude: 45,
longitude: -73
},
zoom: 2
};
}]);
As you can see just the url has changed, in the latter case firebug happens to show the following in the console:
Image
I'm not sure what is wrong. I use the json data to plot points on a google map. The test.json works perfectly. I'm assuming it could either be that external sources of data (domain different than the current) are not allowed, or the Angular goes on to render the google map even before the data is returned from the external source.
The problem is that the external server doesn't allows you to request data.
There is a whole standard shaping this.
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
http://enable-cors.org/
You should read on JSONP if you intend to use ajax cross-domain to send and receive json data. It is supported in angular.js, you just need to find an example (Google has loads).
Jsonp is what you need.

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.