Угловой D3 TopoJSON: Функция масштабирования

#html #angular #typescript #d3.js #topojson

#HTML #угловой #машинописный текст #d3.js #топойсон

Вопрос:

Я боролся с созданием функциональной карты choropleth в D3 на Angular, и до сих пор мне удавалось собрать воедино всплывающую подсказку и функцию масштабирования по округу при нажатии. В настоящее время я пытаюсь выяснить:

  1. Как сбросить масштаб при втором щелчке, но в большинстве примеров в Интернете, похоже, используется совершенно другая функция масштабирования.
  2. Как включить панорамирование, которое может повлиять на общий масштаб, как указано в:
  3. Как включить кнопку масштабирования для выполнения общего масштабирования в центре текущего представления

Я прокомментировал свой машинописный текст ниже вопросами, связанными с этим.

map.component.html

 lt;div id="map"gt;  lt;div id="button-row"gt;  lt;button id="zoom-in"gt; lt;/buttongt;  lt;button id="zoom-out"gt;-lt;/buttongt;  lt;button id="zoom-reset"gt;$lt;/buttongt;  lt;/divgt; lt;/divgt;  

map.component.ts

 import { Component, OnInit } from '@angular/core'; import {FetchTopoService} from '../fetch-topo.service'; // Service that simply returns the following topo json: 'https://cdn.jsdelivr.net/npm/us-atlas@3/counties-albers-10m.json' import * as d3 from 'd3'; import * as topojson from 'topojson-client'; // Is this the current best approach? import { GeometryObject, GeometryCollection, Topology, Objects } from 'topojson-specification'; // Is this different from the topojson-client? How?   @Component({  selector: 'app-map',  templateUrl: './map.component.html',  styleUrls: ['./map.component.css'] }) export class MapComponent implements OnInit {  // Note: ideally the map initialization would be reusable (in a directive?) and I would not have to specify any features related to it within the component.  topo: any = null  svg: any = null  path: any = d3.geoPath()  nation: any = null  states: any = null  counties: any = null  tooltip: any = null  zoom: any = null  zoomIn: any = null   constructor( private topoService: FetchTopoService ) { }   async ngOnInit(): Promiselt;voidgt; {  this.topo = await this.topoService.getData()  this.initMap("#map"). // Initializing to the #map id element.  }   initMap(divId:any) {   // Instantiate tooltip.  this.tooltip = d3.select(divId)  .append('div')  .attr('class', 'map-tooltip')  .style('visibility', 'hidden')  .style('background-color', 'white')  .style('padding', '5px')  .style('position', 'absolute')  .style('opacity', '0.5')  .on('mouseover', (event) =gt; {  this.tooltip.style('visibility', 'hidden');  });   // Instantiate svg.  this.svg = d3.select(divId).append('svg')  .attr("preserveAspectRatio", "xMidYMid meet")  .attr("viewBox", "0 0 960 600")  .attr("height", "98%")  .attr("width", "100%")   // Draw U.S. nation object.  this.nation = this.svg.append("path")  .datum(topojson.feature(this.topo, this.topo["objects"]["nation"]))  .attr("fill", "#f5f5f5")  .attr("class", "nation")  .attr("d", this.path)   // Draw county objects.  this.counties = this.svg.selectAll("path.county")  .data(topojson.feature(this.topo, this.topo["objects"]["counties"] as GeometryCollection)["features"])  .join("path")  .attr("id", function(d:any) {return d["id"]})  .attr("class", "county")  .attr("fill", "#E7E7E8")  .attr("stroke", "#ffffff")  .attr("stroke-linejoin", "round")  .attr("stroke-width", "0.25")  .attr("d", this.path)   // Show tooltip on mouseover.  .on("mouseover", (event:any, d:any) =gt; {  this.tooltip.style('visibility', 'visible')  d3.select(`[id="${d['id']}"]`)  .attr("stroke-width", "2.0")  })   // Hide tooltip on mouse leave.  .on("mouseleave", (event:any, d:any) =gt; {  this.tooltip.style('visibility', 'hidden')  d3.select(`[id="${d['id']}"]`)  .attr("stroke-width", "0.25")  })   // Have tooltip follow mouse movement.  .on('mousemove', (event:any, d:any) =gt; {  this.tooltip  .html(d["id"]   'lt;brgt;'   d["properties"]["name"])  .style('left', (event.x   10)   'px')  .style('top', (event.y   10)   'px')  .style('z-index', '2')  })   // On county click, zoom to county.  .on('click', (event:any, d:any) =gt; {  this.clicked(d)  })   // Overlay state objects (no fill) to get thicker borders — this causes issues in clicked.  this.states = this.svg.selectAll("path.state")  .data(topojson.feature(this.topo, this.topo["objects"]["states"] as GeometryCollection)["features"])  .join("path")  .attr("id", function(d:any) {return d["id"]})  .attr("class", "state")  .attr("fill", "none")  .attr("stroke", "#ffffff")  .attr("stroke-linejoin", "round")  .attr("stroke-width", "1.25")  .attr("d", this.path)   // This does not work — ideally zooms on click zoom button.  this.zoomIn = d3.select('#zoom-in').on('click', (event:any, d:any) =gt; {  d3.zoom()  .scaleExtent([1, 8])  .scaleBy(this.svg.transition().duration(750), 1.3)  })   // On click county, remove states, then translate — there must be an easier way to do this.  clicked(d:any) {  this.states  .attr("stroke", "none")  let bounds = this.path.bounds(d)  let dx = bounds[1][0] - bounds[0][0]  let dy = bounds[1][1] - bounds[0][1]  let x = (bounds[0][0]   bounds[1][0]) / 2  let y = (bounds[0][1]   bounds[1][1]) / 2  let scale = .9 / Math.max(dx / 960, dy / 600)  let translate = [960 / 2 - scale * x, 600 / 2 - scale * y]  this.counties.transition()  .duration(1000)  .style("stroke-width", 1.5 / scale   "px")  .attr("transform", "translate("   translate   ")scale("   scale   ")")  d3.select(`[id="${d['id']}"]`)  .attr("stroke-width", "2.0")  }  }