AmCharts AmMap - 设置缩放操作的起始位置

AmCharts AmMap - Set starting location for zoom actions

我想在下拉菜单中使用基于 selection 的 "zoomToMapObject" 方法。 由于某种原因,开始缩放位置是地图的中间而不是设置的 geoPoint。

(缩放有效,但起始位置让它看起来有点奇怪。)

我目前的做法是这样的:

const duration = this.chart.zoomToMapObject(selectedPoloygon, this.countryZoom, true).duration;
    setTimeout(() => {
      this.chart.homeGeoPoint = geoPoint;
      this.chart.homeZoomLevel = this.countryZoom;
    }, duration);

this.handleCountrySelection(selectedPoloygon);

不知何故,即使设置 homeGeoPoint / homeZoomLevel 也不会影响下一个缩放操作。

**更新:解决高成本(从 1300 个节点到 9000 多个节点)**

我进一步检查了这个问题。当我将新的 mapImageSeries 推入地图时,似乎设置了中间点。

我目前的解决方法是在地图上绘制所有点并隐藏它们。 然后在我 select 一个国家之后,我将状态更改为可见。

然而,这种方法的成本非常高。 DOM-节点从 1300 上升到 ~ 9100。

我在一个国家 selected 并且缩放动画完成后创建它们的另一种方法是更多 有效的。但是由于地图每次都从中心位置开始,这是不可行的吗?或者我做了 s.th。错了?

这是我当前的代码,但性能不佳:

// map.ts

export class MapComponent implements AfterViewInit, OnDestroy {
    imageSeriesMap = {};

    // ... standard map initialization ( not in zone  of course )

    // creating the "MapImages" which is very costly

    this.dataService.getCountries().forEach(country => {
    const imageSeriesKey = country.id;
    const imageSeriesVal = chart.series.push(new am4maps.MapImageSeries()); // takes arround 1-2 ms -> 300 x 2 ~ 500 ms.
    const addressForCountry = this.dataService.filterAddressToCountry(country.id); // returns "DE" or "FR" for example.
    const imageSeriesTemplate = imageSeriesVal.mapImages.template;
    const circle = imageSeriesTemplate.createChild(am4core.Circle);
    circle.radius = 4;
    circle.fill = am4core.color(this.colorRed);
    circle.stroke = am4core.color('#FFFFFF');
    circle.strokeWidth = 2;
    circle.nonScaling = true;
    circle.tooltipText = '{title}';
    imageSeriesTemplate.propertyFields.latitude = 'latitude';
    imageSeriesTemplate.propertyFields.longitude = 'longitude';
    imageSeriesVal.data = addressForCountry.map(address => {
      return {
            latitude: Number.parseFloat(address.lat),
            longitude: Number.parseFloat(address.long),
            title: address.company
          };
        });
    imageSeriesVal.visible = false;
    this.imageSeriesMap[imageSeriesKey] = imageSeriesVal;
    });

    // clicking on the map
    onSelect(country) {
      this.imageSeriesMap[country].visible = true;
      setTimeout( () => {
          const chartPolygons = <any>this.chart.series.values[0];
          const polygon = chartPolygons.getPolygonById(country);

          const anim = this.chart.zoomToMapObject(polygon, 1, true, 1000);
          anim.events.on('animationended', () => {});
            this.handleCountrySelection(polygon);
          }, 100);

      });
    }

    handleCountrySelection(polygon: am4maps.MapPolygon) {
    if (this.selectedPolygon && this.selectedPolygon !== polygon) {
      this.selectedPolygon.isActive = false;
    }
    polygon.isActive = true;

    const geoPoint: IGeoPoint = {
      latitude: polygon.latitude,
      longitude: polygon.longitude
    };

    this.chart.homeGeoPoint = geoPoint;
    this.chart.homeZoomLevel = this.countryZoom;
    this.selectedPolygon = polygon;
  }
}

多亏了你的彻底跟进,我才能够重现这个问题。您遇到的问题是由以下任一步骤触发的:

  1. MapImageSeries 动态推送到图表
  2. 通过 data 动态创建 MapImage(另请注意,在您提供的粘贴绑定中,data 需要一个数组,我必须在测试时更改它)

在任一步骤中,图表都将完全缩小,就像重置自身一样。我将调查为什么会发生这种情况以及是否可以更改它,所以与此同时让我们看看下面的解决方法是否适合您。

如果我们只使用一个预先设置的 MapImageSeries(我没有特别看到有多个 MapImageSeries 的理由,一个不可以吗?),这就消除了问题 1 的发生.除了 data,我们还可以通过 mapImageSeries.mapImages.create(); create() MapImages manually 然后手动分配它们的 latitudelongitude 属性。至此,问题2也没有出现,我们似乎就可以了。

这是一个修改版 pastebin 的演示:

https://codepen.io/team/amcharts/pen/c460241b0efe9c8f6ab1746f44d666af

变化是 MapImageSeries 代码从 createMarkers 函数中取出,因此它只发生一次:

const mapImageSeries = chart.series.push(new am4maps.MapImageSeries());
const imageSeriesTemplate = mapImageSeries.mapImages.template;
const circle = imageSeriesTemplate.createChild(am4core.Circle);
circle.radius = 10;
circle.fill = am4core.color('#ff0000');
circle.stroke = am4core.color('#FFFFFF');
circle.strokeWidth = 2;
circle.nonScaling = true;
circle.tooltipText = 'hi';

在这种情况下,没有必要将 chart 传递给 createMarkers 和 return,所以我传递 polygon 只是为了演示动态 latitude/longitudes,我还将我们的新 MapImage 分配给多边形的数据 (dataItem.dataContext),以便我们稍后参考。这是 createMarkers 的新正文:

function createMarkers(polygon) {
  console.log('calling createMarkers');

  if ( !polygon.dataItem.dataContext.redDot) {
    const dataItem = polygon.dataItem;

    // Object notation for making a MapImage
    const redDot = mapImageSeries.mapImages.create();
    // Note the lat/long are direct properties
    redDot.id = `reddot-${dataItem.dataContext.id}`;
    // attempt to make a marker in the middle of the country (note how this is inaccurate for US since we're getting the center for a rectangle, but it's not a rectangle)
    redDot.latitude = dataItem.north - (dataItem.north - dataItem.south)/2;
    redDot.longitude = dataItem.west - (dataItem.west - dataItem.east)/2;;    
    dataItem.dataContext.redDot = redDot;
  }

}

不需要 animationended 事件或任何东西,它就可以正常工作,因为不再有任何东西干扰您的代码。你也应该恢复你的表现。

这对你有用吗?

以下问题编辑前的原始答案:


我无法复制您提到的行为。另外,我不知道 this.countryZoom 是什么。

只需在按钮处理程序中使用以下内容...

chart.zoomToMapObject(polygon);

...似乎可以很好地缩放到国家/地区,无论当前地图位置如何/zoomLevel

如果您需要在缩放动画结束后计时,zoomToMapObject return 是一个 Animation, you can use its 'animationended' 事件,例如

const animation = this.chart.zoomToMapObject(selectedPoloygon, this.countryZoom, true);
animation.events.on("animationended", () => {
    // ...
});

这是一个示例,其中包含 2 个外部 <button>s,一个用于缩放到美国,另一个用于缩放到巴西:

https://codepen.io/team/amcharts/pen/c1d1151803799c3d8f51afed0c6eb61d

这有帮助吗?如果没有,您能否提供一个 minimal example 以便我们可以复制您遇到的问题?