1

I have a very simple OpenLayers 3 map which draws nothing but a base layer from a third party tile source using the WebGL renderer:

ol3.html:

<!doctype html>
<html lang="en">
  <head>
    <script src="http://openlayers.org/en/v3.13.1/build/ol.js" type="text/javascript"></script>
    <link rel="stylesheet" href="http://openlayers.org/en/v3.13.1/css/ol.css" type="text/css">
    <script src="http://code.jquery.com/jquery-2.2.0.min.js"></script>

    <script src="ol3_map.js"></script>

    <style>
      .map {
        height: 400px;
        width: 100%;
      }
    </style>

  </head>
  <body>

    <div id="map" class="map"><div id="popup"></div></div>

  </body>
</html>

ol3_map.js:

$(document).ready(function(){ 

    // base layer
    var baselayer = new ol.layer.Tile({
        source: new ol.source.XYZ({
            url: 'http://a.tile.stamen.com/toner/{z}/{x}/{y}.png',
        })
    })

    // map
    var map = new ol.Map({
        target: 'map',
        renderer: 'webgl', // use WebGL renderer
        layers: [
          baselayer
          ],
        view: new ol.View({
          center: ol.proj.fromLonLat([37.41, 8.82]),
          zoom: 4
        })
    });

});

However, when hosting the webpage on a webserver (e.g. at localhost), loading the map tiles fails with this error being shown in the browser's console:

Uncaught SecurityError: Failed to execute 'texImage2D' on 'WebGLRenderingContext': The cross-origin image at http://a.tile.stamen.com/toner/4/9/7.png may not be loaded.

Apparently, this is a known issue and there are reasons for this behaviour which I do not want to question here because it is not likely to change anytime soon.

Instead, I am looking for a work around how I can still use map tiles in my OpenLayers 3 WebGL map which are coming from another server but the one the webpage is hosted at.

After a bit of searching, I found some discussions pointing out that one might use MapProxy to proxy the third party map tiles on the server where the webpage is hosted (e.g. localhost) and serve them from there so that as a matter of fact no cross-origin request is made anymore.

Hence, I installed MapProxy as described in its documentation, created an example configuration file and started MapProxy on the same server that my webpage is hosted at (again, localhost for simplicity).

Then, I changed the definition of my base layer to point to one of the MapProxy demo layers which are activated by the example configuration file:

// base layer
var baselayer = new ol.layer.Tile({
    source: new ol.source.XYZ({
        url: 'http://localhost:8080/wmts/osm/webmercator/{z}/{x}/{y}.png',
    })
})

but the cross-origin error remains the same. I am pretty sure that this is due to the fact that the webserver with the webpage and MapProxy are now running on the same host (localhost) but on different ports which in fact is still a cross-origin request and consequently gets blocked. Running both applications on the same port, however, is not possible without further ado because one TCP port is not supposed to point to multiple applications at the same time.

Looking through the MapProxy docs, I found this little passage about adding the Access-control-allow-origin:* header to all HTTP responses from MapProxy which actually should rule out my problem as far as I understand - the map tiles would then still be served from a different port than my webpage but adding this header should explicitly allow this so no cross-origin error should be thrown anymore. Hence, I added

globals:
  http:
    access_control_allow_origin:

to the bottom of my MapProxy configuration file. Now, when loading my webpage with the XYZ source pointing to the MapProxy layer as described above, my requests for map tiles have the following HTTP request and response headers:

request:

Accept:image/webp,image/*,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch
Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Connection:keep-alive
Host:localhost:8080
Referer:http://localhost:8000/ol3.html
User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36

response:

Access-control-allow-origin:*
Cache-control:max-age=259200 public
Content-length:1751
Content-type:image/png
Date:Mon, 29 Feb 2016 10:22:29 GMT
ETag:c7485dcc8d256a6f197ed7802687f252
Server:MapProxy/1.8.2 (Werkzeug based) Python/3.4.3

Still, the cross-origin issue does not disappear and no map tiles are loaded.

What am I missing here or what would be another working solution to overcome this problem?

  • 1
    FIY, don't reimplement existing class. Instead of using ol.source.XYZ, use a ol.source.Stamen({layer: 'toner'}) – ThomasG77 Feb 29 '16 at 12:45
  • That's true, I was a little imprecise here. The real server that I actually want to use is internal within my company so that there is no ol3 class existing for it and I have to use XYZ source instead. – Dirk Feb 29 '16 at 14:30
2

You don't need Mapproxy in particular.

Any web server can manage CORS as long as you configure it correctly (see http://enable-cors.org for more info).

You should also play with option crossOrigin, directly available in ol.source.XYZ to deal with your CORS issue.

You can also use the following code to manage Stamen layers instead of using ol.source.XYZ. FIY, ol.source.Stamen already manages cross origin using internally the option crossOrigin: 'anonymous',.

<!DOCTYPE html>
<html>
  <head>
    <title>Simple Map</title>
    <link rel="stylesheet" href="http://openlayers.org/en/v3.13.1/css/ol.css" type="text/css">
    <script src="http://openlayers.org/en/v3.13.1/build/ol.js"></script>
  </head>
  <body>
    <div id="map" class="map"></div>
    <script>
      var map = new ol.Map({
        layers: [
          new ol.layer.Tile({
            source: new ol.source.Stamen({
              layer: 'toner'
            })
          })
        ],
        target: 'map',
        renderer: 'webgl', // use WebGL renderer
        view: new ol.View({
          center: [0, 0],
          zoom: 2
        })
      });
    </script>
  </body>
</html>
  • Looks like my Stamen/Toner example was a little imprecise. The XYZ source that I really want to use is a company-internal tile server that I am not in control of, so there is no chance of setting the CORS header on that server myself. Hence, I have to use XYZ source instead of Stamen directly. However, what made the difference is setting crossOrigin: 'anonymous' within the ol.source.XYZ({... options! Looks like without this, the requests' headers are not correctly set for CORS while the responses' headers were already set correctly by MapProxy. That way, it works as expected. – Dirk Feb 29 '16 at 14:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.