OpenLayers GetFeatureInfo requests not being triggered - javascript

I'm creating a little web page containing a map using the Google Maps API and the OpenLayers JS library (v2.13). The base layer of the map is Google Street Maps, and that works fine, I can pan around the map, zoom in etc.
I'm adding a number of layers to the map, which come from a Web Mapping Service hosted on a remote server that I don't own or have access to (the code, I mean). And that works fine too, the layers overlay onto the Google Map just fine.
The problem is when I click on the layer features on the map, the WMSGetFeatureInfo controls are not working. No request is sent to the specified URL, no callback function is triggered, nothing happens at all. Here's some code:
var remoteGeoServer = "https://www.remotegeoserver.com/geoserver/wms";
var options = {
controls: [],
maxExtent: new OpenLayers.Bounds(420000, 485000, 770000, 985000),
projection: "EPSG:2157",
units: 'm',
scales: [5000000, 2500000, 1250000, 600000, 400000, 200000, 100000, 50000, 25000]
};
var map = new OpenLayers.Map('map-container', options);
// ... define styles, rules etc.
var overlayLayer = new OpenLayers.Layer.WMS("Overlayed Layer", remoteGeoServer, {<some_options>}); // other layers are created too...
var infoControls = {
click: new OpenLayers.Control.WMSGetFeatureInfo({
url: "/geoserver/wms",
title: 'Identify features by clicking',
layers: [overlayedLayer],
infoFormat: 'text/plain',
queryVisible: true
})
};
var baseLayer = new OpenLayers.Layer.Google("Google Streets", {numZoomLevels: 20});
map.addLayers( [baseLayer, overlayLayer] );
map.setBaseLayer( baseLayer );
for (var i in infoControls) {
infoControls[i].events.register("getfeatureinfo", this, showInfo);
map.addControl(infoControls[i]);
}
// ... and later
function showInfo(evt) {
console.log("Hurray!");
}
infoControls.click.activate();
I should explain that, even though the url in the infoControls is called "/geoserver/wms", there is no web mapping server running on my server. I just forward on the GetFeatureInfo request to the remote server that runs the actual geoserver. Otherwise the same-origin policy kicks in and the GetFeatureInfo requests are rejected.
But nothing happens, my "/geoserver/wms" endpoint never gets called. Am I doing something wrong? Also, and this isn't my main question, but why am I able to successfully give OpenLayers the remote server URL when creating the layers (should same-origin policy not apply - the map still works fine though, and the GET image requests to the remote server are successful).
Something else worth noting is that when I replace the local url path in infoControls with the remote server url, clicking the map triggers the same-origin policy error in the debug console.

I found a few different potential solutions for this problem that other people were having but none of them worked for me. In the end, the only thing that did work was this:
The URL used in the layer creation constructor(s) (in OpenLayers.Layer.WMS) MUST be the same as the URL used for the GetFeatureInfo constructor (in OpenLayers.Control.WMSGetFeatureInfo)
Otherwise, the map layers will work but the GetFeatureInfo requests will fail. I don't know if just the domain needs to be the same, or the full URL.
I'm not particularly happy about this. It means that I have to create an endpoint on my server that acts as a proxy and routes GetMap requests to the actual remote geoserver. I didn't need to do this before (why not - same-origin policy ??) and now it drives up latency for the layers showing up in the map (not to mention my server gets hammered with GetMap requests).
This seems to be an issue with OpenLayers (but I'm not sure of that). I'd be very interested in hearing about a solution if anyone knows of one.

Related

Defining TileSize when making WMS Request on Leaflet Web page

I am creating a web page with Leaflet to access a WMS server.
The issue I have is that I access a paying WMS server that is "counting" the number of requests I make.
In the first attempt I made, since the default tileSize was 256px each of my requests generated 16 tiles requests and so I was "deducted" 16 requests on my account.
I then tried to modify my code as follow :
var wmsserver = L.tileLayer.wms('http://wmsserver/wms?', {
layers: 'mix:precip_24h:mm' ,
format: 'image/png',
tileSize: window.screen.width,
version: '1.3.0',
transparent: true,
});
Now I still have 2 requests logged, and the result on the screen is a little bit shifted.
There must be a cleaner way to do this.

html5 navigator.geolocation.getcurrentposition() returns 0,0

I recently made a web app and part of it looks for the user's location.
function complete(pos) {
var coordinates = pos.coords;
var positions = {lat: coordinates.latitude, lng:
coordinates.longitude};
console.log(positions);
}
function fail(error) {
console.log('failed')
};
var opt = {
enableHighAccuracy: true
};
navigator.geolocation.getCurrentPosition(complete, fail, opt)
But, in some locations like my school, it always returns (0,0) for some reason. It works perfectly fine in other locations, like on private networks.
Can anyone suggest any reasons why or any workaround? Thanks. (I already white listed the site I was using to run this and allowed location_
According to docs navigator.geolocation is not going to be supported in Chrome with http:// (v50.0+). Use https:// instead if that's possible.
That can also cause your issue because you mentioned that only in some places and I guess in some (chrome) browsers it doesn't work.
Note: As of Chrome 50, the Geolocation API will only work on secure
contexts such as HTTPS. If your site is hosted on an non-secure origin
(such as HTTP) the requests to get the users location will no longer
function.

OpenLayers3 WMTS out-of-memory error

I'm trying to fetch weather radar data for the UK, from a WMST server.
The client can get the Capabilities fine. After that, the browser (Chrome and Firefox) crashes after a while, with an out-of-memory error.
The only thing obviously different in my case, in comparison with simple examples, is that an API key is needed for all interactions with the server.
var parser = new ol.format.WMTSCapabilities();
var map;
//I pass the API key here
fetch('http://datapoint.metoffice.gov.uk/public/data/inspire/view/wmts?REQUEST=GetCapabilities&key=my_key_goes_here').then(function(response) {
return response.text();
}).then(function(text) {
console.log('Capabilities found.'); //ok
var result = parser.read(text);
var options = ol.source.WMTS.optionsFromCapabilities(result, {
layer: 'RADAR_UK_Composite_Highres',
matrixSet: 'EPSG:4326'
});
map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM(),
opacity: 0.7
}),
new ol.layer.Tile({ //fails when this Tile is included
opacity: 1,
source: new ol.source.WMTS(options),
//I set this, figuring that the API key is still needed
url: 'http://datapoint.metoffice.gov.uk/public/data/inspire/view/wmts?my_key_goes_here'
})
],
target: 'mymap',
view: new ol.View({
center: ol.proj.fromLonLat([0, 52.0]), //near London, UK
zoom: 7
})
});
});
The crash happens without the client trying to fetch the tiles (because I can't see any HTTP requests in the debugger; the last one is the get-capabilities request).
Any hints as to what might be causing this?
First of all, note that ol.layer.Tile does not have an url option. Ideally, the GetCapabilities response would advertise a service url that has the key already. Since the server you are trying to access does not have that, you'll have to tweak the result you get from the GetCapabilities response:
result.OperationsMetadata.GetTile.DCP.HTTP.Get[0].href += 'key=my_key_goes_here&'
The second and more severe problem is that the GetCapabilities response advertises incorrect scale denominators and origins. To fix those, you have to tweak the result again:
result.Contents.TileMatrixSet[0].TileMatrix.forEach(function(m) {
m.ScaleDenominator *= 111319.49079327358;
m.TopLeftCorner = m.TopLeftCorner.reverse();
});
After applying these tweaks, you can pass the result to ol.source.WMTS.optionsFromCapabilities, and you will get a correct map.
I'd strongly recommend you contact the service provider (contact information can also be found in the GetCapabilities response) and let them know about the problems with their GetCapabilities response.

Bing maps api works on pc but not mobile web app

I really hope someone can help with my problem. I have built a mobile web app http://ufa-ld-qa.azurewebsites.net/ (the QA site) with asp.net mvc4 using Bing Maps API for various functionality in the app. I am having problems with the directions module. When I view the site on my pc (Chrome and IE) it works fine and I see no errors but on mobile devices it is not working (but it did work fine yesterday when we launched to QA). I have used HTML5 geolocation (this may be the issue) to get user's location to allow them to get directions to a location. I will post my code below and if anyone could please help me it would be greatly appreciated. We have tested it on about 7 different mobile devices with different OS's and it doesn't work on any. Does anyone know if this is a Bing issue or my code below? Thanks so much in advance.
<script type="text/javascript">
var map = null;
var directionsManager = null;
var userLat = null;
var userLong = null;
var userPosition = null;
var latlng = new Microsoft.Maps.Location(#Model.latitude, #Model.longitude);
navigator.geolocation.getCurrentPosition(locationHandler);
function locationHandler(position)
{
userPosition = new Microsoft.Maps.Location(position.coords.latitude, position.coords.longitude);
}
function GetMap() {
// Initialize the map
map = new Microsoft.Maps.Map(document.getElementById("map"), { credentials: "Au_7giL-8dUbFkJ8zLjcQKy4dV2ftPfpMxQ0_sVBksoj4Y-1nBT00Z1oqUIU894_",
mapTypeId: Microsoft.Maps.MapTypeId.road});
Microsoft.Maps.loadModule('Microsoft.Maps.Directions', { callback: directionsModuleLoaded });
}
function directionsModuleLoaded() {
// Initialize the DirectionsManager
directionsManager = new Microsoft.Maps.Directions.DirectionsManager(map);
// Create start and end waypoints
var startWaypoint = new Microsoft.Maps.Directions.Waypoint({ location: userPosition });
var endWaypoint = new Microsoft.Maps.Directions.Waypoint({ location: latlng });
directionsManager.addWaypoint(startWaypoint);
directionsManager.addWaypoint(endWaypoint);
// Set request options
directionsManager.setRequestOptions({ routeMode: Microsoft.Maps.Directions.RouteMode.driving });
// Set the render options
directionsManager.setRenderOptions({
itineraryContainer: document.getElementById('directionPanel'),
displayWalkingWarning: false,
walkingPolylineOptions: { strokeColor: new Microsoft.Maps.Color(200, 0, 255, 0) },
});
// Specify a handler for when an error occurs
Microsoft.Maps.Events.addHandler(directionsManager, 'directionsError', displayError);
// Calculate directions, which displays a route on the map
directionsManager.calculateDirections();
}
function displayError(e) {
// Display the error message
alert(e.message);
}
</script>
A couple of things to try. First ensure that your app is allowed to access the users location. Most mobile platforms require you to mark that the app requires access to the GPS in the manifest. Another thing to look into is the possibility that the userLocation isn't populated before your callback for the directions manager is called. It's possible that the GPS takes a little longer on the mobile device to find the users location and as such the directions loaded function is firing before the users location is set, thus passing in a null starting . You might find it useful to have a flag to indicate that the directions manager has loaded and a simple function that runs after setting the flag and also runs after setting the use location that checks that both the directions manager has loaded and the user location has been set and then calls your directions loaded function.
My Windows Phone 8 App is experiencing similar behavior. (Nokia 920)
http://bing.com/maps/default.aspx?cp=47.677797~-122.122013&lvl=12
When the Website preference is set to 'desktop version' the map renders correctly.
When the Website preference is set to 'mobile version' the map renders incorrectly.
Just started happening about a week ago !

Google Geocode via HTTP callback function?

I want to use the google geocode via HTTP functionality to translate a city name into longitude and latitude in for my AJAX web application.
However, it appears that no callback function exists for the HTTP geocoder functionality
http://code.google.com/apis/maps/documentation/geocoding/index.html
Is that true, no callback function exists?
Because if that is true, it essentially means that the Google geocode via HTTP api is useless when used with AJAX because JavaScript will throw a crossdomain exception error.
Any ideas on how I can use the geocode via HTTP api in my AJAX web application in JavaScript?
Note: I do not want to use the full blown Google Maps API which is approx 200kb download (i.e. GClientGeocoder). I want to use the HTTP api b/c of it's super quick responsiveness and lack of needing my web users from having to download the huge full blown interactive google maps api.
E.g.
http://maps.google.com/maps/geo?output=json&sensor=false&key={API_KEY}&q={CITY,STATE}&CALLBACK=????
Thanks
Here is an example that uses the Google Maps Geocoder. The geocoder function getLocation takes a callback function as the second argument.
function findAddress(street, city, state, zip) {
var address = [
street,
city.toLowerCase(),
state.toLowerCase(),
zip
].join(', ');
if (!geocoder) {
geocoder = new GClientGeocoder();
}
if (geocoder) {
geocoder.getLocations(
address,
function(result) {
var dialog, len, point;
if (result.Status.code != G_GEO_SUCCESS) {
alert("Error: "+result.Status.code)
} else {
len = result.Placemark.length;
if (len > 1) {
alert("Multiple matches were found. I'll leave it as an exercise to handle this condition");
} else {
point = new GLatLng(
result.Placemark[0].Point.coordinates[1],
result.Placemark[0].Point.coordinates[0]
);
}
}
}
);
}
}
hmm....I think you'd have to have your AJAX call back to your own server, and then call Google's Geocode from your server.
Thats how I do AJAX geocoding, it all goes through my ASP.NET code.
EDIT:
In the ASP.NET webforms environment I might implements this as a lightweight ASHX file, but for the purposes of simplicity, here's an ASPX example:
public partial class GoogleHandler : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e) {
Response.Write(GetGoogleXML("http://pseudo_googlegeocode?parameter=" + parametersFromQuerystring);
}
}
In the example above, the .NET page is only passing the request along.
But in a real environment, I'd rather my .NET code do more than just pass the data over. This way I can do error handling, filtering, validation, business logic, all on the server, before sending that data over to the client.
Also, this allows for greater abstraction. i.e, I might change from google to yahoo geocoding. In this way I'd only have to change my serve logic, and leave the client to just receive a generic set of coordinates/location data.
Also, using that abstraction I could actually aggregate multiple data from various geocoding data sources. Again, the server takes care of aggregating, the client just receives and displays the filtered data.
As others noted, you didn't read the full page. You want what that page calls the JavaScript Client Geocode.
Here's a simplified version of a script I wrote a while back. It also uses a Google Map control, but feel free to ignore that. The delay function hack is because it seemed Google was occasionally returning null when I hit their servers too fast. I don't know if this is still an issue, so don't put it in unless you have to.
<script type="text/javascript">
//<![CDATA[
var freezeLocations;
var coder;
var map;
function load() {
if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById("map"));
map.setCenter(new GLatLng(38.479395, -98.349609), 4);
map.addControl(new GLargeMapControl());
}
coder = new GClientGeocoder();
missionLocations = new Array();
missionLocationsDelayed = new Array();
addMissionLocation("Atlanta, Georgia", "http://improveverywhere.ning.com/group/atlanta");
//etc.
}
function addMissionLocation(newLocation, url)
{
var successful = false;
var counter = 0;
while(!successful && counter < 3)
{
coder.getLatLng(
newLocation,
function(point) {
if (!point) {
//alert(newLocation + " not found");
successful = false;
} else {
missionLocations.push(new GMarker(point, { title:newLocation}));
//alert(missionLocations.length);
map.addOverlay(missionLocations[missionLocations.length - 1]);
missionLocations[missionLocations.length - 1].bindInfoWindowHtml("<a href='" + url + "'>" + newLocation + "</a>");
successful = true;
}
}
);
if(!successful)
{
delayGeocode();
}
counter++;
}
}
function delayGeocode()
{
for(var i = 0; i < 2000000; i++)
{
}
}
//]]>
</script>
You could use Yahoo Query language as outlined in my blog post http://jawtek.blogspot.com/2009/03/unwritten-guide-to-yahoo-query-langauge.html
You would be able to use a yql statement like: select * from json where
url="http://maps.google.com/maps/geo?output=json&sensor=false&q=Indianapolis,In"
Then you would add a script tag to your html (can be done with document.createElement('script')) with a src http://query.yahooapis.com/v1/public/yql?q={your yql here}&format=json&callback={your function here} where {your yql here} is replace with a URI Encoded version of you yql statment.
If you have looked at the
documentation and not found it and
both Andrew and Mike have not said
"yes", and told you how to do it, I
suspect you have your answer.
lol
and lets all read the service's documentation:
10.13 hide or mask from Google the identity of your service as it uses
the Service, including by failing to
follow the identification conventions
listed in the Maps APIs Documentation;
or
10.14 violate any policies in the Maps APIs Documentation or violate Google's
Software Principles (...)
Also
This service is designed for geocoding static (known) addresses
using a REST interface, for placement
of application content on a map. For
dynamic geocoding of user-defined
addresses (for example, within a user
interface element), consult the
documentation for the JavaScript
Client Geocoder or the Maps API for
Flash Client Geocoder. Geocoding is a
time and resource intensive task.
Whenever possible, pre-geocode known
addresses (using the Geocoding Service
described here or another geocoding
service), and store your results in a
temporary cache of your own design.
But then again you could try Google Maps API V3 Geocoder
Look at the Google Maps API. It has some functions with callback that uses it's geocoding service.
http://code.google.com/apis/maps/documentation/reference.html#GClientGeocoder.getLatLng
I second the suggestion to create a server-side page to access the geocoder. I am doing something similar and it works great. There's a good article about working with the geocoder in PHP here.
Also note that technically you're not permitted to use Google's geocoder unless you'll be displaying the data on a Google Map - but I don't know if they'll actually check on you.
I too encountered the challenges you described above. As you indicated, Google prevents cross-domain HTTP access to the Geocode API URL:
Same origin policy
JSONP and Google Maps API Geocoder - Not a Bug
This does severely diminish its usefulness when using client-side scripting. The only solution I found to this challenge was to create a server-side proxy service that relays the responses from the Google Maps Geocode API to my client-side script.
I wrote an extremely long-winded blog post describing this process.

Categories

Resources