Search code examples
javascriptopenlayersamchartsopenlayers-5amcharts4

amCharts cursor zoom not working in OpenLayers map overlay


I added amCharts chart to OpenLayers map overlay but chart cursor zoom not work like the image provided below:

enter image description here

The example provided below:

// Overlays init variables and function
var container;
var content;
var closer = document.getElementById('popup-closer');
var overlay;
function createOverlay(width) {
    container = document.getElementById('popup');
    container.style.width = width;
    content = document.getElementById('popup-content');
    closer = document.getElementById('popup-closer');
    return container;
}

var map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  view: new ol.View({
    center: ol.proj.fromLonLat([51.338076, 35.699756]),
    zoom: 12
  })
});

map.on("click", function(e) {
	let coordinates = e.coordinate;
  
  createOverlay("500px");
  content.innerHTML = '<div id="chartdiv" class="ltr"></div>';
  
  am4core.ready(function() {

// Themes begin
am4core.useTheme(am4themes_animated);
// Themes end

var chart = am4core.create("chartdiv", am4charts.XYChart);

var data = [];
var value = 50;
for(let i = 0; i < 300; i++){
  let date = new Date();
  date.setHours(0,0,0,0);
  date.setDate(i);
  value -= Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 10);
  data.push({date:date, value: value});
}

chart.data = data;

// Create axes
var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
dateAxis.renderer.minGridDistance = 60;

var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());

// Create series
var series = chart.series.push(new am4charts.LineSeries());
series.dataFields.valueY = "value";
series.dataFields.dateX = "date";
series.tooltipText = "{value}"

series.tooltip.pointerOrientation = "vertical";

chart.cursor = new am4charts.XYCursor();
chart.cursor.snapToSeries = series;
chart.cursor.xAxis = dateAxis;

//chart.scrollbarY = new am4core.Scrollbar();
chart.scrollbarX = new am4core.Scrollbar();

}); // end am4core.ready()
  
  $(".ol-popup").show();
  overlay = new ol.Overlay({
    element: container,
    autoPan: true,
    autoPanMargin: 20,
    autoPanAnimation: {
      duration: 50
    }
  });
  map.addOverlay(overlay);
  overlay.setPosition(coordinates);
 
  
});
/* ol PopUp */
.ol-popup {
    text-align: right;
    position: absolute;
    background-color: white;
    -webkit-filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2));
    filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2));
    border-radius: 10px;
    bottom: 12px;
    transform: translateX(50%);
    display: none;
}
.ol-popup:after, .ol-popup:before {
    top: 100%;
    border: solid transparent;
    content: " ";
    height: 0;
    width: 0;
    position: absolute;
    pointer-events: none;
}
.ol-popup:after {
    border-top-color: white;
    border-width: 10px;
    left: 50%;
    transform: translateX(-50%);
}
.ol-popup:before {
    border-top-color: #cccccc;
    border-width: 11px;
    left: 50%;
    transform: translateX(-50%);
}
.ol-popup-closer {
    text-decoration: none !important;
    font-size: 16px;
    position: absolute;
    top: 5px;
    right: 8px;
    cursor: pointer;
}

.map {
  height: 400px;
  width: 100%;
}

#chartdiv {
  width: 100%;
  height: 300px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet"/>
<script src="https://www.amcharts.com/lib/4/core.js"></script>
<script src="https://www.amcharts.com/lib/4/charts.js"></script>
<script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>

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

<div id="popup" class="ol-popup">
  <i class="fas fa-times ol-popup-closer" id="popup-closer"></i>
  <div id="popup-content" class="p-4"></div>
</div>

As you can see, I created a dynamic overlay and add it to map and when user click on map then overlay popup will be shown to the user after that chart created and the chart cursor zoom not work but in the other place of my website it works perfectly.


Solution

  • This occurs due to OpenLayers Overlay entity stopping the event propagation (e.g. click & drag event on chart)

    This can be disabled very easily via stopEvent: false;

    overlay = new ol.Overlay({
      element: container,
      stopEvent: false,
      autoPan: true,
      autoPanMargin: 20,
      autoPanAnimation: {
        duration: 50
      }
    });
    

    More on OpenLayers Overlay

    The problem with this is that, the same click & drag event will be propagated to the map in the back, and the selection of the chart will be impossible to do. Example of this trouble can be seen on this fiddle. Also there is a ticket on Github regarding this exact issue.


    To resolve this I've used a very simple idea, to track when the cursor is on the overlay, and disable map events during those times;

    var mouseOver = false;
    function createOverlay(width) {
      container = document.getElementById('popup');
      container.style.width = width;
      container.onmouseover = function() {
        mouseOver = true;   // when cursor is targeting overlay we enable this boolean
      };
      container.onmouseout = function() {
        mouseOver = false;  // and disable when out
      };
      ...
    }
    

    Then using this boolean as following;

    map.on("pointerdrag", function(e) {
      if (mouseOver) {
        e.stopPropagation();
        return;
      }
    });
    

    to disable dragging event, and;

    map.on("click", function(e) {
      if (mouseOver) {
        return;
      }
      // rest of chart creation logic here
    }
    

    to disable a new overlay creation event.

    Final perfectly working fiddle can be found here

    Pirooz bashi buddy