0

Using Openlayers (v6.1.1.) I'm trying to create simple FlyTo animation between several polygons. FlyTo animation works fine when there is somehow small distance between them but with increase in distance there is decrease in user experience.

On larger distance I'm just seeing fast panning through map in low zoom. I tried with ahocevar answer from this link but without desired effect.

As per instructions, I combined a center animation with two zoom animations and started them at the same time. Example:

let view = map.getView();
let extentOfPolygon = feature.getGeometry().getExtent();
let resolution = view.getResolutionForExtent(extentOfPolygon);
let zoom = view.getZoomForResolution(resolution);
let center = ol.extent.getCenter(extentOfPolygon);

view.animate({
  center: center,
  duration: duration
});
view.animate({
  zoom: zoom - 1,
  duration: duration / 2
}, {
  zoom: zoom,
  duration: duration / 2
});

This FlyTo animation works fine when polygons are in vicinity but with the increase in distance, FlyTo animation is transforming into fast panning across map.

I've created small app for testing purposes. When we click on list item map will animate and zoom to clicked field. In example when awesome, another and super field are in vicinity and FlyTo animations works as expected but when we click from super to epic field (which is in far south. Then map is just fast panning to location).

So, my question is this. Is it possible to achieve same FlyTo effect for polygons independently of polygon location (leaflet has this nicely set https://regionbound.com/leaflet-fly-demo)

Here is jsfiddle example: https://jsfiddle.net/Svinjica/1kjfp4ds/

Svinjica
  • 2,389
  • 2
  • 37
  • 66
  • I'm not sure what exactly you need? Do you need to decrease FlyTo speed in bigger distances? – Mahdi Mahmoodian Dec 17 '19 at 09:50
  • if you need to increase the time (decrease animate speed) you should write another method to calculate time. subtract current center to polygon center then multiply to some coefficient. so a dynamic animate time can solve your problem – Mahdi Mahmoodian Dec 17 '19 at 09:59
  • @MahdiMahmoodian As polygon distance increases map doesn't zoom out. it's just fast panning through map. I would like to zoom out and then again, zoom in. Thank you for answer but i believe that time is not the problem here, I believe that problem lies in zoom – Svinjica Dec 17 '19 at 10:02
  • 2
    I checked the source code in GitHub. animate function changes the zoom in the leaner coefficient. it means in every frame increase/decrease a bit to zoom. so I don't think you can use animate in regular way. maybe you should split animate into some part and in each decrease/increase zoom like what you did in 2 phases. You can write a method for this. – Mahdi Mahmoodian Dec 17 '19 at 10:17
  • Another way that I just find is first animate to median of the new and old center with lower zoom (like the initial zoom of map) then animate to destination. but you should check distance. in lower distance you dont need this option. – Mahdi Mahmoodian Dec 17 '19 at 10:20
  • @MahdiMahmoodian Very nicely pointed out. I ll go check it out. Meanwhile if anyone else has better solutions or example can give it a go :) (starting jsfiddle is provided) – Svinjica Dec 17 '19 at 10:25
  • 1
    I think you need more refinement in how you zoom. For example zoom out just enough so both start and destination are visible at peak altitude. You can do that by calculating distance and dividing by map width. Make some adjustment to duration for overall up and down changes, but not too much, and divide the up and down durations proportionately https://jsfiddle.net/b4mL9s28/5/ – Mike Jan 17 '20 at 15:15
  • @Mike Thank you Mike. This is the closes to the solution that I'm looking for. I'm just wondering if it is possible to speed up initial zoom (first clicked field). Of course, you can provide comment as answer and ll accept it. – Svinjica Jan 17 '20 at 15:28

1 Answers1

1

I think you need more refinement in how you zoom. For example zoom out just enough so both start and destination are visible at peak altitude. You can do that by calculating distance and dividing by map width. Make some adjustment to duration for overall up and down changes, but not too much, and divide the up and down durations proportionately. To reduce the initial duration if the current resolution is already greater than the calculated peak altitude resolution 100% of duration should be downward, and you should also open the map close to where your features are located by fitting it to the vectorSource extent https://jsfiddle.net/b4mL9s28/8/

let geojsonData = new ol.format.GeoJSON().readFeatures(fieldData, {
  featureProjection: 'EPSG:3857'
});
let vectorSource = new ol.source.Vector({
  features: geojsonData
});

let vectorLayer = new ol.layer.Vector({
  source: vectorSource
});

var map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    }),
    vectorLayer
  ],
  view: new ol.View({
    center: [0, 0],
    zoom: 4
  })
});

map.getView().fit(vectorSource.getExtent());

const duration = 2000;

var featureListElement = document.getElementById("vectorFeatures");

var features = vectorLayer.getSource().getFeatures();
for (let feature of features) {
  let aElement = document.createElement("a");
  aElement.classList.add("list-group-item", "list-group-item-action");
  featureListElement.appendChild(aElement);
  aElement.innerHTML = feature.get("Name");
  aElement.onclick = function() {

    let view = map.getView();
    var extentOfPolygon = feature.getGeometry().getExtent();
    let resolution = view.getResolutionForExtent(extentOfPolygon);
    var center = ol.extent.getCenter(extentOfPolygon);
    var currentCenter = map.getView().getCenter();
    var currentResolution = map.getView().getResolution();
    var distance = Math.sqrt(Math.pow(center[0] - currentCenter[0], 2) + Math.pow(center[1] - currentCenter[1], 2));
    var maxResolution = Math.max(distance/ map.getSize()[0], currentResolution);
    var up = Math.abs(maxResolution - currentResolution);
    var down = Math.abs(maxResolution - resolution);
    var adjustedDuration = duration + Math.sqrt(up + down) * 100;

    view.animate({
      center: center,
      duration: adjustedDuration
    });
    view.animate({
      resolution: maxResolution,
      duration: adjustedDuration * up / (up + down)
    }, {
      resolution: resolution,
      duration: adjustedDuration * down / (up + down)
    });
  }
}
Mike
  • 16,042
  • 2
  • 14
  • 30