OrdnanceSurvey / OS-Vector-Tile-API-Stylesheets

Cartographic stylesheets for use with OS Vector Tile API
10 stars 12 forks source link

Unable to add text labels due to alleged missing "glyphs" property #4

Closed andokai closed 1 year ago

andokai commented 1 year ago

Hi,

I'm trying to add labels to markers and polygons but I'm running into errors due to the style allegedly missing a "glyphs" property. I can however see that the styles all do appear to have a "glyphs" property so I'm not sure what's going on. I've modified one of the existing examples to demonstrate the issue.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
    <title>OS Vector Tile API | Example Add Overlay | MapLibre GL JS</title>
    <link
      rel="stylesheet"
      href="https://labs.os.uk/public/os-api-branding/v0.3.1/os-api-branding.css"
    />
    <link
      rel="stylesheet"
      href="https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.css"
    />
    <style>
      body {
        margin: 0;
        padding: 0;
      }
      #map {
        position: absolute;
        top: 0;
        bottom: 0;
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div id="map"></div>

    <script src="https://labs.os.uk/public/os-api-branding/v0.3.1/os-api-branding.js"></script>
    <script src="https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.js"></script>
    <script>
      const apiKey = 'API_KEY_HERE';

      // Create a map style object using the ZXY service.
      const style = {
        version: 8,
        sources: {
          'raster-tiles': {
            type: 'raster',
            tiles: [
              'https://api.os.uk/maps/raster/v1/zxy/Outdoor_3857/{z}/{x}/{y}.png?key=' +
                apiKey,
            ],
            tileSize: 256,
          },
        },
        layers: [
          {
            id: 'os-maps-zxy',
            type: 'raster',
            source: 'raster-tiles',
          },
        ],
      };

      // Initialize the map object.
      const map = new maplibregl.Map({
        container: 'map',
        minZoom: 6,
        maxZoom: 15,
        style: style,
        maxBounds: [
          [-10.76418, 49.528423],
          [1.9134116, 61.331151],
        ],
        center: [-1.485, 52.567],
        zoom: 7,
      });

      map.dragRotate.disable(); // Disable map rotation using right click + drag.
      map.touchZoomRotate.disableRotation(); // Disable map rotation using touch rotation gesture.

      // Add navigation control (excluding compass button) to the map.
      map.addControl(
        new maplibregl.NavigationControl({
          showCompass: false,
        })
      );

      map.on('load', function () {
        // Add a source for the boundary polygons.
        map.addSource('boundaries', {
          type: 'vector',
          tiles: [
            'https://api.os.uk/maps/vector/v1/vts/boundaries/tile/{z}/{y}/{x}.pbf?srs=3857&key=' +
              apiKey,
          ],
        });

        // Add a layer showing the boundary polygons.
        // Filter expression is used to display County and Greater London Authority polygons only.
        map.addLayer({
          id: 'boundaries-layer',
          type: 'fill',
          source: 'boundaries',
          'source-layer': 'Boundary_line',
          layout: {},
          paint: {
            'fill-color': 'rgba(175, 88, 186, 0.3)',
            'fill-outline-color': 'rgba(175, 88, 186, 1)',
          },
          filter: ['in', 'AREA_CODE', 'CTY', 'GLA'],
        });

        map.addLayer({
          id: 'boundary-symbols',
          type: 'symbol',
          source: 'boundaries',
          'source-layer': 'Boundary_line',
          layout: {
            'text-field': ['get', 'NAME'],
            'text-size': 12,
          },
        });

        // When a click event occurs on a feature in the boundaries layer, open a popup at the
        // location of the click, with name HTML from its properties.
        map.on('click', 'boundaries-layer', function (e) {
          new maplibregl.Popup()
            .setLngLat(e.lngLat)
            .setHTML(e.features[0].properties.NAME)
            .addTo(map);
        });

        // Change the cursor to a pointer when the mouse is over the boundaries layer.
        map.on('mouseenter', 'boundaries-layer', function () {
          map.getCanvas().style.cursor = 'pointer';
        });

        // Change it back to a pointer when it leaves.
        map.on('mouseleave', 'boundaries-layer', function () {
          map.getCanvas().style.cursor = '';
        });
      });
    </script>
  </body>
</html>
tmnnrs commented 1 year ago

Hi @andokai. In the example provided you aren't ever loading any of the JSON styles (you are just defining a "style" for the ZXY map tiles; then manually adding the source/layers for the boundaries) - so the glyphs URL isn't getting set.

If you add the glyphs to your style definition as follows:

const style = {
  version: 8,
  glyphs: 'https://api.os.uk/maps/vector/v1/vts/resources/fonts/{fontstack}/{range}.pbf?key=' + apiKey,
  sources: {
    'raster-tiles': {
      type: 'raster',
      tiles: [
        'https://api.os.uk/maps/raster/v1/zxy/Outdoor_3857/{z}/{x}/{y}.png?key=' + apiKey,
      ],
      tileSize: 256,
    },
  },
  layers: [
    {
      id: 'os-maps-zxy',
      type: 'raster',
      source: 'raster-tiles',
    },
  ],
};

... and add a 'text-font': ['Source Sans Pro Regular'] to your boundary-symbols layer layout property (plus the filter: 'filter': ['in', 'AREA_CODE', 'CTY', 'GLA']) - then you should see the boundary labels as expected.

If you were to implement the latest version of MapLibre GL JS library (v3.0.1) - you could also investigate the map.setGlyphs() function...

andokai commented 1 year ago

Hi, @tmnnrs, sorry, the example was put together hastily. I'll see if I can put together a more representative one.

If I manually construct the style object, I expect it will work, however what doesn't work is pointing to one of the json files in this repository.

tmnnrs commented 1 year ago

Most of the styles in the repo will still reference the sprites and glyphs from the default (api.os.uk) stylesheet. All of the resources referenced in in the default style require authentication. You can use the transformRequest() callback to resolve this (see http://localhost/public/os-data-hub-examples/os-vector-tile-api/vts-example-custom-style#maplibre-gl-js).

andokai commented 1 year ago

Yeah, I'm aware of the need for the request transformation, I wouldn't have any map tiles otherwise.

Here's a more complete example https://codesandbox.io/s/cool-waterfall-c3xr92?file=/src/index.js

tmnnrs commented 1 year ago

Adding "text-font": ["Source Sans Pro Regular"] to the layout property for clusterCountLayer seemed to work for me.

https://maplibre.org/maplibre-style-spec/layers/#layout-symbol-text-font defaults to ["Open Sans Regular","Arial Unicode MS Regular"] which we [OS] don't use...

andokai commented 1 year ago

Ok, that fixes it, thanks! Are the fonts available listed somewhere?

tmnnrs commented 1 year ago

The default style for the basemap has the following fonts available:

The overlays (Boundaries, Greenspace, etc) simply have Tahoma Regular available.

andokai commented 1 year ago

Awesome, thanks @tmnnrs!