关于使用 D3实现拓扑按地图区域分布的连猜带蒙的一点想法(理解不深,待调整优化)

news/2025/2/9 6:05:06 标签: 前端, D3, 拓扑, 地图

【预期效果】:将地市归属的节点限制在地图上地市所在范围附近
【初步效果】:(各地市节点之间没有关联)节点基本能待在对应的地市区域
在这里插入图片描述
【初步效果】:(各地市节点之间存在关联)因为力的作用节点只能在对应地市区大致的位置方向上
在这里插入图片描述

【代码实现(Vue)】

<template>
  <div class="my-wrapper">
    <!-- 地图 -->
    <svg class="my-map"></svg>
    <!-- 拓扑 -->
    <svg class="my-topo" version="1.1" xmlns="http://www.w3.org/2000/svg">
      <g>
        <!-- 连线 -->
        <line
          v-for="(item, index) in lines"
          :key="item.target + index"
          class="my-line"
          stroke="#CCCCCC"
          stroke-width="1"
          :x1="item.source.x"
          :x2="item.target.x"
          :y1="item.source.y"
          :y2="item.target.y"
          :data-source="item.source.id ? item.source.id : item.source"
          :data-target="item.target.id ? item.target.id : item.target" />
        <!-- 节点 -->
        <g
          v-for="item in nodes"
          :key="item.id"
          :data-id="item.id">
          <circle
            class="my-node"
            :cx="item.x"
            :cy="item.y"
            fill="#169BFA"
            r="6" />
          <foreignObject
            class="my-text"
            :x="item.x"
            :y="item.y"
            width="100"
            height="20">
            <div style="font-size: 12px; color: #169BFA;">{{item.id}}</div>
          </foreignObject>
        </g>
      </g>
    </svg>
  </div>
</template>

https://geojson.cn/ 这里可以拿到各省份地市的 GeoJson数据

<script>
  import * as d3 from "d3";
  import ShanDongGeoJson from './GeoJson/shandong'

  // 预置拓扑数据
  const cities = ['济南市', '青岛市', '淄博市', '枣庄市', '东营市', '烟台市', '潍坊市', '济宁市', '泰安市', '威海市', '日照市', '临沂市', '德州市', '聊城市', '滨州市', '菏泽市']
  const nodes = [] // 节点数据
  const lines = [] // 连线数据
  cities.forEach((city, index) => {
    Array(...Array(3)).forEach((v, i) => {
      nodes.push({ id: `${city}-${i}`, city })
    })
    // 相同地市节点之间连线
    lines.push(
      { source: `${city}-0`, target: `${city}-1` },
      { source: `${city}-0`, target: `${city}-2` },
      { source: `${city}-1`, target: `${city}-2` },
    )
  })
  // 跨地市节点连线
  lines.push(
    { source: '济南市-0', target: '青岛市-0' },
    { source: '青岛市-1', target: '淄博市-1' },
    { source: '淄博市-2', target: '枣庄市-2' },
    { source: '枣庄市-0', target: '东营市-0' },
    { source: '东营市-1', target: '烟台市-1' },
    { source: '烟台市-2', target: '潍坊市-2' },
    { source: '潍坊市-0', target: '济宁市-0' },
    { source: '济宁市-1', target: '泰安市-1' },
    { source: '泰安市-2', target: '威海市-2' },
    { source: '威海市-0', target: '日照市-0' },
    { source: '日照市-1', target: '临沂市-1' },
    { source: '临沂市-2', target: '德州市-2' },
    { source: '德州市-0', target: '聊城市-0' },
    { source: '聊城市-1', target: '滨州市-1' },
    { source: '滨州市-2', target: '菏泽市-2' },
  )
  // 画布宽高
  const width = 1000, height = 600;

  export default {
    data() {
      return {
        nodes: [],
        lines: [],
      }
    },
    mounted() {
      this.drawMap()
      this.drawTopo()
    },
    methods: {
      drawMap() {
        // 画 map
        const svgMap = d3.select('.my-map')
        const projection = d3.geoMercator().fitExtent(
        [[0, 0],[width, height]], ShanDongGeoJson);
        const pathGenerator = d3.geoPath()
          .projection(projection);
        svgMap.selectAll('path')
          .data(ShanDongGeoJson.features)
          .join('path')
          .attr('d', pathGenerator)
          .attr('stroke-width', 0.5)
          .attr('stroke', 'rgba(69, 90, 116, 0.5)')
          .attr('fill', '#ffffff');
        svgMap.selectAll('text')
          .data(ShanDongGeoJson.features)
          .enter()
          .append('text')
          .attr('x', (d) => projection(d.properties.centroid)[0])
          .attr('y', (d) => projection(d.properties.centroid)[1])
          .attr('transform', `translate(${0},${0})`)
          .text((d) => (d.properties.name))
          .attr('fill', 'rgba(69, 90, 116, 0.5)')
          .attr('font-size', '12px');
         
        // 计算节点对应地市在画布中的坐标位置
        this.nodes = nodes.map((n, i) => {
          const cityCenter = ShanDongGeoJson.features.find(v => (v.properties.name === n.city))?.properties?.centroid ?? null;
          if (cityCenter && projection) {
            const [cityX, cityY] = projection(cityCenter);
            return { ...n, cityX, cityY };
          } else {
            return { ...n, cityX: null, cityY: null };
          }
        });
        this.lines = lines
      },
      drawTopo() {
        // 画 topo        
        d3.forceSimulation(this.nodes)
          .force('link', d3.forceLink(this.lines).id(d => d.id).distance(60)) // 根据需要调整以达到比较美观的效果
          .force('charge', d3.forceManyBody().strength(-50)) // 根据需要调整以达到比较美观的效果
          .force('x', d3.forceX((d) => d.cityX || 0))
          .force('y', d3.forceY((d) => d.cityY || 0))
          .force('collision', d3.forceCollide().radius(30)) // 根据需要调整以达到比较美观的效果
          .force('center', d3.forceCenter(width / 2, height / 2))
          .alphaMin(0.0000001);
      },
    },
  };
</script>
<style lang="scss" scoped>
  .my-wrapper {
    position: relative;
    width: 1000px;
    height: 600px;
    overflow: auto;
    > svg {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
    }
  }
</style>

http://www.niftyadmin.cn/n/1370946.html

相关文章

python界面编程和网口通信_Python—网络通信编程之udp通信编程

服务端代码 import socket # 1.创建实例&#xff0c;即数据报套接字 server socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 2.绑定地址&#xff0c;进行监听 server.bind(("127.0.0.1", 3120)) # 3.收发消息 while True: # data server.recv(1024) # 接收数…

WindApi2 , WindOriginalApiLibrary 突然不兼容问题

1. 在新的电脑上从tfs拉下代码后编译&#xff0c; windoriginalapilibrary 这个工程弹出对话框&#xff0c;要求转为vs2013编译&#xff0c;选择同意&#xff0c;编译成功 2.WindApi2 的Reference列表中 WindOriginalApiLibrary 这个工程突然显示 黄色感叹号&#xff0c;同时无…

初学者python编辑器选pycharm_Python初学者用哪个编辑器好,例如komodo edit,vim,pycharm,kite等等?...

自己用过IDLE, Spider, Jupyter, Pycharm。分别说说这几个的特点和应用情景。 IDLE相信对于绝大多数python的用户来说&#xff0c;第一行python代码都是在IDLE里面写的。当然你很快就会不满足于这个最原始的开发环境&#xff0c;毕竟它很多地方都不够智能&#xff0c;比如不能自…

Vue3项目按需引入 view-ui-plus

安装 view-ui-plus npm install view-ui-plus --save安装 babel-plugin-import npm install babel-plugin-import --save-dev配置 babel.config.js module.exports {presets: [vue/cli-plugin-babel/preset,],// ——————————这里plugins: [[import,{libraryName: …

mongodb 由于计算机死机造成的无法启动故障

一次计算机死机&#xff0c;重启后&#xff0c;mongodb无法启动&#xff0c;log显示&#xff1a; exception in initAndListen: 12596 old lock file, terminating Sun Mar 30 20:16:14.619 dbexit: Sun Mar 30 20:16:14.619 [initandlisten] shutdown: going to close listen…

基于Element-ui 封装带分页的下拉选择器组件

使用 Element-ui的 el-select组件时&#xff0c;如果下拉选项过多&#xff0c;一是查找选项困难&#xff0c;二是数据量超大&#xff08;比如1w&#xff09;组件直接会卡死&#xff0c;所以考虑做一个带分页的下拉选择器&#xff08;样式还可以再优化&#xff09; 效果图 组件代…

C# interface 的特性 无法被implement class继承

最近做interface添加特性后,implement class 无法继承。 微软要求class是实现Interface而不是继承,所以我们必须手动添加特性&#xff0c;而不能自动继承。 对于abstract class中的特性&#xff0c;derived class 自动继承父类的特性

python pip使用_python中pip的安装与使用教程

在安装pip前&#xff0c;请确认win系统中已经安装好了python&#xff0c;和easy_install工具&#xff0c;如果系统安装成功&#xff0c;easy_install在目录python的安装盘&#xff08;如C盘&#xff09;:\Python27\Scripts下面2.采用cd命令进入到Scripts 目录下面&#xff1a;G…