Search code examples
openlayers

How can I show just a single world when zoomed out using OpenLayers?


I only want a single world to show up. Currently, when fully zoomed out, multiple copies of the world will be displayed. I saw in the documentation this should be controlled by multiWorld which says:

if false the view is constrained so only one world is visible, and you cannot pan off the edge. If true the map may show multiple worlds at low zoom levels. Only used if the projection is global. Note that if extent is also provided it is given precedence.

It is my understanding that I need the extent to be defined due to the issue described in How can I pin the top of the map to the top of the containing div?

I am not sure what is meant by Only used if the projection is global, but I assume it would be in my situation.

What do I need to change?

const map = new ol.Map({
  target: "map",
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  view: new ol.View({
    projection: "EPSG:4326",
    center: [120, 15],
    zoom: 0,
    extent: [-Infinity, -85, Infinity, 85],
    showFullExtent: true,
    multiWorld: false
  })
});
#map {
  aspect-ratio: 3 / 1;
  background-color: blue;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/ol.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/dist/ol.min.js"></script>
<div id="map"></div>


Solution

  • To make the projection work like a normal global projection but not show above or below latitude 85 you could simply reduce the extent of the projection from [-180, -90, 180, 90] to [-180, -85, 180, 85] as long as you do not plan to use it anywhere else with full extent (but it would be better to use a matching source and projection).

    ol.proj.get('EPSG:4326').setExtent([-180, -85, 180, 85]);
    
    const osm = new ol.source.OSM();
    osm.setTileGridForProjection(
      'EPSG:4326',
       ol.tilegrid.createXYZ({extent: [-180, -90, 180, 90]})
    );
    
    const map = new ol.Map({
      target: "map",
      layers: [
        new ol.layer.Tile({
          source: osm
        })
      ],
      view: new ol.View({
        projection: "EPSG:4326",
        center: [120, 15],
        zoom: 0,
      })
    });
    #map {
      aspect-ratio: 3 / 1;
      background-color: blue;
    }
    <link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/ol.min.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/dist/ol.min.js"></script>
    <div id="map"></div>

    For a perfect fit the map div aspect ratio should match that of the extent (world minus the polar regions)

    ol.proj.get('EPSG:4326').setExtent([-180, -85, 180, 85]);
    
    const osm = new ol.source.OSM();
    osm.setTileGridForProjection(
      'EPSG:4326',
       ol.tilegrid.createXYZ({extent: [-180, -90, 180, 90]})
    );
    
    const map = new ol.Map({
      target: "map",
      layers: [
        new ol.layer.Tile({
          source: osm
        })
      ],
      view: new ol.View({
        projection: "EPSG:4326",
        center: [120, 15],
        zoom: 0,
      })
    });
    #map {
      aspect-ratio: 360 / 170;
      background-color: blue;
    }
    <link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/ol.min.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/dist/ol.min.js"></script>
    <div id="map"></div>