How to set wms parameter (bbox, width, height, x, y) - javascript

I access the geoserver after creating the wms url. Then I use getFeatureInfo to get the information.
How do I set the parameters to get multiple layers info?
what is means width, height, x,y, bbox?
var bboxControl = 0.0001;
var bbox = (coordinate[0]-bboxControl) + ',' +
(coordinate[1]-bboxControl) + ',' +
(coordinate[0]+bboxControl) + ',' +
(coordinate[1]+bboxControl);
var projection = map.getView().getProjection().getCode();
const parameter = "?SERVICE=WMS
&VERSION=1.1.1
&REQUEST=GetFeatureInfo
&FORMAT=image/png
&TRANSPARENT=true"
+ "&QUERY_LAYERS=" + layers
+ "&LAYERS=" + layers
+ "&exceptions=application/vnd.ogc.se_inimage
&INFO_FORMAT=application/json
&FEATURE_COUNT=50
&X=50&Y=50"
+ "&SRS=" + projection
+ "&STYLE=&WIDTH=101&HEIGHT=101"
+ "&BBOX=" + bbox;
$(document).ready(function(){
$.ajax({
url: getFeatureInfoUrl + parameter,
dataType : 'json',
success: function(result){
success(result);
}
});
});
This works but not depending on the zoom state.

If you want more layers, somewhere in your code you have a list/array named layers. Add every layer you want into that.
For your second question check here for GeoServer requests.
But in nutshell:
BBox means the bounding box. it define the area GeoServer sends the data.
Width and height define map size returned from GeoServer.

Related

Update/Reload specific tile in leaflet dynamicly

If my server tells the clients when and what tile to reload/update, how can reload/update the tile sent from the server? I'm using the L.CRS.Simple CRS. And I have no zoom levels on a custom game map.
Here is my code:
var map = L.map('map', {
crs: L.CRS.Simple,
attributionControl: false
}).setView([0, 0], 2)
L.tileLayer('/chunks/{x}.{y}.png', {
maxNativeZoom: 1,
minNativeZoom: 1,
}).addTo(map)
function ReloadTile(x,y){
// UPDATE TILE HERE Request for example /chunks/{1}.{1}.png depending on input
}
First, listen to the tileload and tileunload events of the L.TileLayer to grab references to tiles as they load (and free those references as they unload), e.g.
let myTileLayer = L.tileLayer(...);
let tileRefs = {};
myTileLayer.on('tileload', function(ev) {
tileRefs[ ev.coords.x + ',' + ev.coords.y ] = ev.tile;
});
myTileLayer.on('tileunload', function(ev) {
delete tileRefs[ ev.coords.x + ',' + ev.coords.y ];
});
...so whenever you want to update a tile, you just have to search for it in the data structure.
Remember that in order to reload a HTMLImageElement, you've got to change its src property, e.g.:
function ReloadTile(x,y){
const tile = tileRefs[x + ',' + y];
if (!tile) {
// Tile is not currently loaded on the screen
return;
}
tile.src = "/chunks/{1}.{1}.png";
}
Beware of requesting a URL that your browser has cached. Do your homework regarding cache busting and relevant HTTP headers.
Note that I'm using a javascript Object as key-value data structure, and string concatenation to build up a unique key per tile. You're free to use other data structures (such a Map) and any other method to index the tiles (such as a double-depth data structure for x-y, or triple-depth for x-y-z, or indexing by tile URL). Also note that the sample code is usign only the X and Y coordinates of the tile since your TileLayer seems to have only one zoom level.
lThanks for the help!
My final code:
var map = L.map('map', {
crs: L.CRS.Simple,
attributionControl: false
}).setView([0, 0], 2)
const path = '/chunks/{x}.{y}.png?cash={time}'
const layer = L.tileLayer(path, {
maxNativeZoom: 1,
minNativeZoom: 1,
time: 0
}).addTo(map)
function VectorToString(vector) {
return vector.x + "." + vector.y
}
class TileHandler {
constructor(layer){
this.layer = layer
this.layers = {}
this.layer.on('tileload', (tile) => {
this.layers[VectorToString(tile.coords)] = tile
})
this.layer.on('tileunload', (tile) => {
delete this.layers[VectorToString(tile.coords)]
})
}
update(position){
const tile = this.layers[VectorToString(position)]
const url = L.Util.template(tile.target._url, {
...position,
time: new Date().getTime()
})
if (!tile) return
tile.tile.src = url
}
}

Get latitude and longitude stops in a trimble map

Using trimble maps, i'm creating a route with origin and destination points given by latitude and longitude. The user can put new stops at the interactive map. The problem is that when the library returns me the stops locations, it is giving me those like X and Y and not like longitude and latitude.
For example, if the origin is
Latitude: 37.66427
Longitude: -97.72388
The application is returning me the point like
x: -10878572.558428662
y: 4532106.384744367
I'm doing this to get the stops:
var routeElements = routingLayer.getRouteElements("MyRoute");
var numberOfStops = routeElements.stops.length;
for (var i = 0; i < numberOfStops; i++) {
console.log("Stop " + i);
console.log("Lon: " + routeElements.stops[i].geometry.x);
console.log("Lat: " + routeElements.stops[i].geometry.y);
}
As says at the following documentation:
https://developer.trimblemaps.com/trimble-maps/1.2/docs/routing/
I need to know the way to convert X and Y to Longitude and Latitude or get directly the LonLat with any specific command.
You'd have to use the transform function to change the point back to a geographic projection.
In this case you would need to call
routeElements.stops[i].transform(map.getProjectionObject(),new ALKMaps.Projection("EPSG:4326"))
to get a Point object where the x and y are longitude and latitude values.
It's basically the reverse of what's described here: https://developer.trimblemaps.com/trimble-maps/1.2/docs/getting-started/spherical-mercator/

Openlayers 3 - WFS not updating on map pan

I was using a CQL Filter to return my points based on a certain type. I then added a BBOX to the filter to limit the results only to the current extent of the map. This was all working correctly. The only issue is if I pan the map it does not reload the WFS call. So if I zoom into a point the wfs filters correctly, if I then pan to an area where I know there is a point there will be nothing there unless I change the zoom level. This will update the WFS.
In my ServerVector loader parameter I call a function updateWFSURL(extent). I use this to build the WFS url based on the filter criteria to be passed to the CQL_FILTER. I currently have no loading strategy, I tried using createTile and bbox but neither gave me the proper result.
Here is the code I am using to add the BBOX to my CQL_FILTER. More filters are added after and this filter is appended to the WFS url.
var coord1 = ol.proj.transform([extent[0], extent[1]], 'EPSG:3857', 'EPSG:4326');
var coord2 = ol.proj.transform([extent[2], extent[3]], 'EPSG:3857', 'EPSG:4326');
var filter = "&CQL_FILTER=BBOX(GEOM, " + coord1 + ", " + coord2 + ", 'EPSG:4326')";
The workaround I found was to capture the map moveend event and call the updateWFSURL(extent) from there. This will update properly on a pan but causes the updateWFSURL to be called twice on zoom since it gets fired from the WFS loader and moveend event.
The solution I am looking for is to either have my WFS update properly on the pan or how to determine the difference between a pan/zoom so I can not call the function in moveend if it was a zoom.
Added more code below. Removed the geoserver url and layer name I am using and stripped out some of CQL Filter that was just filtering on an ID. This wfs is not updating the map on pan without my work around.
//this function gets called on page load
function loadPointAssets() {
// Source retrieving WFS data in GeoJSON format using JSONP technique
pointAssetVector = new ol.source.ServerVector({
format: new ol.format.GeoJSON(),
loader: function (extent, resolution, projection) {
//this gets called whenever the map zooms
updateWFSURL(extent);
},
projection: 'EPSG:3857'
});
//cluster markers adds the markers to the map from the pointAssetVector
clusterMarkers();
}
function updateWFSURL(extent) {
//transform the extent to coordinates can be passed in as lat/long
var coord1 = ol.proj.transform([extent[0], extent[1]], 'EPSG:3857', 'EPSG:4326');
var coord2 = ol.proj.transform([extent[2], extent[3]], 'EPSG:3857', 'EPSG:4326');
var filter = "&CQL_FILTER=BBOX(GEOM, " + coord1 + ", " + coord2 + ", 'EPSG:4326')";
$.ajax({
//create the url with the filter
url: 'http://myGeoserverURL/wfs?' +
'service=WFS&request=GetFeature&' +
'version=1.1.0&typename=LayerName&outputFormat=text/javascript&' +
'format_options=callback:loadFeatures&' +
'srsname=EPSG:4326' + filter,
dataType: 'jsonp'
});
pointAssetVector.clear(true);
}
// Executed when data is loaded by the $.ajax method.
var loadFeatures = function (response) {
//clear the features before adding them to the source again
pointAssetVector.clear();
pointAssetVector.addFeatures(pointAssetVector.readFeatures(response));
};
You cannot use CQL filter and BBOX together in the same WFS request. If you have a CQL property filter then you need to add the BBOX filter as a CQL filter.
For example, this BBOX filter:
bbox: extent.join(',') + ',EPSG:3857'
Is equivalent to this CQL filter:
cql_filter: "BBOX(geometry," + extent.join(',') + ")"

Store the longtitude and the latitude of a placemark into array

So I'm working on a webpage using the Google Earth API where a shuttle moves around the town and picks up passengers. PASSENGERS is a predefined array of objects and is a separate javascript file due to the length. Here is my code for the function populate which populates the 3D map with placemarks:
function populate()
{
// mark houses
for (var house in HOUSES)
{
// plant house on map
new google.maps.Marker({
icon: "https://google-maps-icons.googlecode.com/files/home.png",
map: map,
position: new google.maps.LatLng(HOUSES[house].lat, HOUSES[house].lng),
title: house
});
}
// get current URL, sans any filename
var url = window.location.href.substring(0, (window.location.href.lastIndexOf("/")) + 1);
// scatter passengers
for (var i = 0; i < PASSENGERS.length; i++)
{
// pick a random building
var building = BUILDINGS[Math.floor(Math.random() * BUILDINGS.length)];
// prepare placemark
var placemark = earth.createPlacemark("");
placemark.setName(PASSENGERS[i].name + " to " + PASSENGERS[i].house);
// prepare icon
var icon = earth.createIcon("");
icon.setHref(url + "/img/" + PASSENGERS[i].username + ".jpg");
// prepare style
var style = earth.createStyle("");
style.getIconStyle().setIcon(icon);
style.getIconStyle().setScale(4.0);
// prepare stylemap
var styleMap = earth.createStyleMap("");
styleMap.setNormalStyle(style);
styleMap.setHighlightStyle(style);
// associate stylemap with placemark
placemark.setStyleSelector(styleMap);
// prepare point
var point = earth.createPoint("");
point.setAltitudeMode(earth.ALTITUDE_RELATIVE_TO_GROUND);
point.setLatitude(building.lat);
point.setLongitude(building.lng);
point.setAltitude(0.0);
// associate placemark with point
placemark.setGeometry(point);
// add placemark to Earth
earth.getFeatures().appendChild(placemark);
// add marker to map
var marker = new google.maps.Marker({
icon: "https://maps.gstatic.com/intl/en_us/mapfiles/ms/micons/man.png",
map: map,
position: new google.maps.LatLng(building.lat, building.lng),
title: PASSENGERS[i].name + " at " + building.name
});
//remember passenger's placemark and marker for pick-up's sake
PASSENGERS[i].lat = placemark.getGeometry.getLatitude();
PASSENGERS[i].lng = placemark.getGeometry.getLongtitude();
}
}
However, when I load my page I get the following error from the console: Uncaught TypeError: undefined is not a function regarding the two last lines of code:
PASSENGERS[i].lat = placemark.getGeometry.getLatitude();
PASSENGERS[i].lng = placemark.getGeometry.getLongtitude()
Any ideas how I can correctly get the lat and lng? Thank you in advance.
As others have said getGeometry is a method and needs brackets () to be executed.
Also looking at your code you can just use building.lat and building.lng - as these are the properties used to set the placemark's geometry in the first place.
Really it is pointless calling more methods, getGeometry().getLatitude(), to get a value you already have.
The person is at the building, no?
e.g.
PASSENGERS[i].lat = building.lat;
PASSENGERS[i].lng = building.lng;
Try
PASSENGERS[i].lat = placemark.getGeometry().getLatitude();
PASSENGERS[i].lng = placemark.getGeometry().getLongtitude();

How to display markers (huge numbers) on a map which has been logically divided into segments?

What i have done so far:
i'm developing an application where i have to display more than(50K) points/Markers on the Navteq map divided into different segments.
for example: if i have 50K points i will divide all points into different segments.
if i divide 50K points into 50 segments each segment would have 1000 points (may not be 50 segments , it may depend).
right now it is working but it takes long time and hangs to render all the points on the MAP.so that i would like to perform segmentation displaying to display only few points with clustering.
so that i can get an idea of how the segment will look like.
but the problem here is i should only perform the clustering based on the segments.otherwise points from different segments willbe mixed together and displayed
as single unit and that conveys the wrong information to the user.
so here my question is: is it possible to perform the clustering based on the segment. so that only points from same segment will be clustered.
Note: if this is not possible, i would like to use Latest version of here-maps 2.5.3 (Asynchronous) may reduce some time while loading, so that i would like to use indexing functionality also while rendering the points
to improve the rendering time using nokia.maps.clustering.Index class.
i studied that indexing would reduce the time while rendering the points/markers on map. does it help in my case? could anybody please suggest how to perform indexing ?
This is the code with which i'm displaying points on map:
function displayAllLightPoints(arrLightPointCoordinats, totalLightPoints,
selectedSegmentId, totalSegmentsCount,segmentColorcode)
{
var MyTheme1 = function () {
};
segmentColorcode = segmentColorcode.substring(2,segmentColorcode.length-1);
MyTheme1.prototype.getNoisePresentation = function (dataPoint) {
var markerLightPoint = new nokia.maps.map.Marker(dataPoint, {
icon: new nokia.maps.gfx.BitmapImage("..//Images//Lightpoint//" +
segmentColorcode + ".png"),
anchor: {
x: 12,
y: 12
}
});
return markerLightPoint;
};
MyTheme1.prototype.getClusterPresentation = function (data) {
var markerLightPoint = new
nokia.maps.map.StandardMarker(data.getBounds().getCenter(), {
icon: new nokia.maps.gfx.BitmapImage("..//Images//
Segment/" + segmentColorcode + ".png", null, 66, 65),
text: data.getSize(),
zIndex: 2,
anchor: {
x: 12,
y: 12
}
});
return markerLightPoint;
};
var ClusterProvider = nokia.maps.clustering.ClusterProvider,
theme = new MyTheme1(),
clusterProvider = new ClusterProvider(map, {
eps: 0.00000000001,
minPts: 1000000,
strategy: nokia.maps.clustering.ClusterProvider.
STRATEGY_DENSITY_BASED,
theme: theme,
dataPoints: []
});
var lightpointsDataSet1 = new Array();
for (var i = 0; i < totalLightPoints; i++) {
lightpointsDataSet1[i] = { latitude: arrLightPointCoordinats[i][0],
longitude: arrLightPointCoordinats[i][1], title:
'LightPoint ' + (i + 1) };
}
clusterProvider.addAll(lightpointsDataSet1);
clusterProvider.cluster();
}
To deal with a very large (50K+) data set , I would do all the heavy number crunching server side and send over a new JSON response whenever the map is updated. Something like the HTML page described here
The key section of the code is the ZoomObserver:
var zoomObserver = function (obj, key, newValue, oldValue) {
zoom = newValue;
if (zoom < 7)
{ zoom = 7;}
if (zoom > 16)
{ zoom = 16;}
// Define the XML filename to read that contains the marker data
placeMarkersOnMaps('http://api.maps.nokia.com/downloads/java-me/cluster/'+ zoom + '.xml'
+ '?lat1=' + map.getViewBounds().topLeft.latitude
+ '&lng1='+ map.getViewBounds().topLeft.longitude
+ '&lat2='+ map.getViewBounds().bottomRight.latitude
+ '&lng2='+ map.getViewBounds().bottomRight.longitude);
};
map.addObserver("zoomLevel", zoomObserver );
Where the REST service returns a "well-known" data format which can be used to add markers and clusters to the map.
Now assuming you have two massive data sets you could make two requests to different endpoints, or somehow distinguish which cluster of data belongs to which so that you would just be returning information of the form:
{latitude':51.761,'longitude':14.33128,'value':102091},
i.e. using the DataPoint standard (which means you could use a heat map as well.
Of course, what I'm not showing here is the back-end functionality to cluster in the first place - but this leaves the client (and the API) to do what it does best displaying data, not number crunching.

Categories