2

I'm using geojson-vt to render a large geojson (originated from OSM) file. This file contains all road from a state (extracted with overpass-turbo way[highway="*"]). I'm buinding a domain-specific application that needs to enable the user to add new roads to the existing geojson. However, I'm unable to snap the drawing cursor to the geojson-vt layer.

To illustrate that, consider a simplified demo with this issue (see fiddle below). This demo includes the geojson of countries limitsand add a drawing cursor that should snap to their boundaries.

JSFiddle: http://jsfiddle.net/mju68Lr4/

  var url = 'https://openlayers.org/en/v3.20.1/examples/data/geojson/countries.geojson';
    fetch(url).then(function(response) {
      return response.json();
    }).then(function(json) {
      var tileIndex = geojsonvt(json, {
        extent: 4096,
        debug: 1
      });
      var vectorSource = new ol.source.VectorTile({
        format: new ol.format.GeoJSON(),
        tileGrid: ol.tilegrid.createXYZ(),
        tilePixelRatio: 16,
        tileLoadFunction: function(tile, tileCoord) {
          var format = tile.getFormat();
          var data = tileIndex.getTile.apply(tileIndex, tileCoord);

          var features = format.readFeatures(
            JSON.stringify({
              type: 'FeatureCollection',
              features: data.features
            }, replacer));
          tile.setLoader(function() {
            tile.setFeatures(features);
            tile.setProjection(tilePixels);
          });
        },
        tileUrlFunction: function(tileCoord) {
          return [tileCoord[0], tileCoord[1], -tileCoord[2] - 1];
        }
      });
      var vectorLayer = new ol.layer.VectorTile({
        source: vectorSource
      });
      map.addLayer(vectorLayer);
      var snap = new ol.interaction.Snap({
          source: vectorSource
      });
      map.addInteraction(snap);

    });

I expected that after adding the geojson-vt (tile layer) I would be able to snap to it, as follow (inside the callback):

  var snap = new ol.interaction.Snap({
      source: vectorSource
  });
  map.addInteraction(snap);

However, I'm getting the following error output (in chrome console):

Uncaught (in promise) TypeError: this.source_.getFeatures is not a function
    at ol.interaction.Snap.getFeatures_ (ol-debug.js:70564)
    at ol.interaction.Snap.setMap (ol-debug.js:70669)
    at ol.Map.<anonymous> (ol-debug.js:41259)
    at ol.Collection.boundListener (ol-debug.js:3441)
    at ol.Collection.ol.events.EventTarget.dispatchEvent (ol-debug.js:3859)
    at ol.Collection.insertAt (ol-debug.js:12466)
    at ol.Collection.push (ol-debug.js:12490)
    at ol.Map.addInteraction (ol-debug.js:41313)
    at (index):106

Am I missing something? It seems that snap is not being able to handle the vector tile layer elements since they are "dynamic". Hence, is it possible to fix this issue and snap the drawing cursor to the geojson-vt layer (a tile layer)? If so, how can I do that?

  • VectorTile sources don't have features, the tiles do. The snap interaction can take a collection of feature instead of a source, so maybe you could create an ol.Collection and call .extend(features) in the tileLoadFunction. – Mike Jan 27 at 10:57
  • @Mike, that seems logic. In turn, how can I do that? – Marcos Roriz Junior Jan 27 at 12:53
  • I tried adding that to your jsfiddle but the feature geometry is in tile pixels, so the snap interaction thinks they are all very close to view coordinates [0,0] and that was the only place snapping worked. It would probably have to be done by converting the geometry to EPSG:3857 - there's a discussion about that and some code here github.com/openlayers/openlayers/issues/6273 – Mike Jan 27 at 14:39
2

This works, but it does change the feature geometry projection from tile pixel to EPSG:3857 to do it.

  var url = 'https://openlayers.org/en/v3.20.1/examples/data/geojson/countries.geojson';
  fetch(url).then(function(response) {
    return response.json();
  }).then(function(json) {
    var tileIndex = geojsonvt(json, {
      extent: 4096,
      debug: 1
    });
    var vectorFeatures = new ol.Collection();
    var vectorSource = new ol.source.VectorTile({
      format: new ol.format.GeoJSON(),
      tileGrid: ol.tilegrid.createXYZ(),
      tilePixelRatio: 16,
      tileLoadFunction: function(tile) {
        var format = tile.getFormat();
        var tileCoord = tile.getTileCoord();
        var data = tileIndex.getTile(tileCoord[0], tileCoord[1], -tileCoord[2] - 1);

        var features = format.readFeatures(
            JSON.stringify({
              type: 'FeatureCollection',
              features: data ? data.features : []
            }, replacer));

        var z = tileCoord[0];
        var z2 = 1 << tileCoord[0];
        var tx = tileCoord[1];
        var ty = -tileCoord[2]-1;
        var extent = 4096;

        var transform = function(coordinates, output, dimensions) {
          var dims = dimensions || 2;
          for (var i=0; i<coordinates.length; i+=dims) {
            var x = (coordinates[i] / extent + tx) / z2;
            var y = (coordinates[i+1] / extent + (ty)) / z2;
            var c = [((x - 0.5) * 2) * 6378137 * Math.PI, -((y - 0.5) * 2) * 6378137 * Math.PI];
            coordinates[i] = c[0];
            coordinates[i+1] = c[1];
          }
          return coordinates;
        }

        features.forEach( function(feature) {
          feature.getGeometry().applyTransform(transform);
        });
        console.log(features);
        vectorFeatures.extend(features);

        tile.setLoader(function() {
          tile.setFeatures(features);
          tile.setProjection(ol.proj.get('EPSG:3857'));
        });
      },
      tileUrlFunction: function(tileCoord) {
        return [tileCoord[0], tileCoord[1], -tileCoord[2] - 1];
      }
    });
    var vectorLayer = new ol.layer.VectorTile({
      source: vectorSource
    });
    map.addLayer(vectorLayer);
    var draw = new ol.interaction.Draw({
      type: 'LineString'
    });
    map.addInteraction(draw);
    var snap = new ol.interaction.Snap({
      features: vectorFeatures
    });
    map.addInteraction(snap);
    map.addControl(new ol.control.MousePosition());
  });

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.