Three.js 实战【4】—— 3D地图渲染

news2024/9/20 1:40:37

初始化场景&准备工作

在vue3+threejs当中,初始化场景的代码基本上是一样的,可以参考前面几篇文章的初始化场景代码。在这里进行渲染3D地图还需要用到d3这个库,所以需要安装一下d3,直接npm i即可。

再从阿里云这里提供的全国各个省市的地图json数据下载一份自己需要展示的json数据。初始化的区别在于需要创建一个渲染器

  • CSS3DRenderer用于通过CSS3的transform属性, 将层级的3D变换应用到DOM元素上。 如果你希望不借助基于canvas的渲染来在你的网站上应用3D变换,那么这一渲染器十分有趣。 同时,它也可以将DOM元素与WebGL的内容相结合
  • CSS2DRenderer是CSS3DRenderer的简化版本,唯一支持的变换是位移
// 创建渲染器
const labelRenderer = new CSS2DRenderer();
const addRenderer = () => {
  labelRenderer.domElement.style.position = 'absolute';
  labelRenderer.domElement.style.top = '0px';
  labelRenderer.domElement.style.pointerEvents = 'none';
  labelRenderer.setSize(window.innerWidth, window.innerHeight);
  document.getElementById('map').appendChild(labelRenderer.domElement);
};

创建材质

这里封装一个方法,用来处理不同块的数据。这是在json文件当中他会按照不同的省市进行区分不同的块。

Shape:使用路径以及可选的孔洞来定义一个二维形状平面

  • 首先创建了一个多边形对象,之后循环data数组(这个data数组就对应着json里面的coordinates数组的下一层数组,这个等下调用这个方法的时候组装进来)
  • 然后循环创建点。offsetXY是d3当中的一个方法,他的作用是将经纬度给转换成xy坐标,因为json和three里面的坐标系不是一样的所以需要转换一层
  • 再进行判断,如果是第一个点就是起点,那么就用moveTo方法移动到这个点开始绘制,如果不是第一个点就用lineTo绘制一条线过去。这里使用了负的y值,因为Three.js的坐标系与地理坐标系的y轴方向相反

ExtrudeGeometry:挤压缓冲几何体(从一个形状路径中,挤压出一个BufferGeometry)他也是BufferGeometry的一个子类

  • 上一步已经创建好了一个Shape形状了之后,需要再进一步创建一个几何体,使用到是这个ExtrudeGeometry(看名字我们也知道是要做什么了)
  • 直接构造
    • depth:float类型,挤出的形状的深度,默认值为1
    • bevelEnabled:boolean类型,对挤出的形状应用是否斜角,默认值为true

MeshStandardMaterial:标准网格材质

  • color:材质颜色
  • emissive:材质的放射(光)颜色,基本上是不受其他光照影响的固有颜色。默认为黑色
  • roughness:材质的粗糙程度。0.0表示平滑的镜面反射,1.0表示完全漫反射
  • metalness:材质与金属的相似度。非金属材质,如木材或石材,使用0.0,金属使用1.0
  • transparent:定义此材质是否透明。这对渲染有影响,因为透明对象需要特殊处理,并在非透明对象之后渲染。设置为true时,通过设置材质的opacity属性来控制材质透明的程度
  • side:定义将要渲染哪一面 。正面,背面或两者。 默认为THREE.FrontSide。其他选项有THREE.BackSide 和 THREE.DoubleSide

最后通过Mesh将几何体和材质添加到一起进行返回

const offsetXY = d3.geoMercator();
/**
 * 绘制每个市的区域
 * @param data 坐标数据
 * @param color 颜色
 * @param depth 深度
 * */
const createMesh = (data, color, depth) => {
  const shape = new THREE.Shape();
  data.forEach((item, idx) => {
    const [x, y] = offsetXY(item);

    if (idx === 0) shape.moveTo(x, -y);
    else shape.lineTo(x, -y);
  });

  const extrudeSettings = {
    depth,
    bevelEnabled: false
  };
  const materialSettings = {
    color: color,
    emissive: 0x000000,
    roughness: 0.45,
    metalness: 0.8,
    transparent: true,
    side: THREE.DoubleSide
  };
  const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
  const material = new THREE.MeshStandardMaterial(materialSettings);

  return new THREE.Mesh(geometry, material);
};

渲染地图

在上一步创建材质的时候就把所需要渲染的材质都给创建好了,这一步只需要导入json,然后把json对应的data经纬度数据传给createMesh方法就大功告成了。接下来我们看一下渲染地图的方法

  • 先取json文件里面第一个子对象的经纬度出来作为默认的中心点
  • Object3D这是Three.js中大部分对象的基类,提供了一系列的属性和方法来对三维空间中的物体进行操纵,后续所有创建的mesh等等都加在这个里面
  • 然后遍历json内容去找需要的data数据,这里需要通过MultiPolygon多重多边形 和 Polygon多边形,两者的区别在于找到对应的数据层级是不一样的,相差一层,可以对比一个内蒙古和其他省的数据。
  • 最后找到那一层数据就调用createMesh创建材质的方法,得到材质之后加到Object3D对象里面,最后加到场景里面就完成了
/**
* @param data 完整的json数据
*/
const createMap = (data) => {
  const map = new THREE.Object3D();
  const center = data.features[0].properties.centroid;
  // d3的方法表示将center作为xy坐标系的0,0点
  offsetXY.center(center).translate([0, 0]);
  data.features.forEach((feature) => {
    const unit = new THREE.Object3D();
    const {name, adcode} = feature.properties;
    const {coordinates, type} = feature.geometry;

    const depth = 1;

    coordinates.forEach((coordinate) => {
      if (type === 'MultiPolygon') coordinate.forEach((item) => fn(item));
      if (type === 'Polygon') fn(coordinate);

      function fn(coordinate) {
        unit.name = name;
        unit.adcode = adcode;
        const mesh = createMesh(coordinate, '#63bbd0', depth);
        unit.add(mesh);
      }
    });
    map.add(unit);
  });
  scene.add(map);
};

结果这一步我们就可以直接查看效果了。但是由于最开始指定的是第一个数据的点为中心点也就是北京,渲染出来的并没有居中,并且最开始给设置的相机位置是(0,64,64)这样看起来也不对劲。

在这里插入图片描述

居中处理

在上一步scene.add(map)之前调用该方法。

  • roration 先给map对象旋转-90度,让整个地图正面朝上
  • 创建Box3对象,用setFromObject方法根据地图对象计算出其包围盒,再通过getCenter拿到中心点
  • 因为最开始我们设置的中心点是(0,0,0)也就是重新设置中心点直接等于负center即可,后续如果设置的中心点不是(0,0,0)就这样计算一遍,这样整个地图也就居中展示了
const setCenter = (map) => {
  map.rotation.x = -Math.PI / 2;
  const box = new THREE.Box3().setFromObject(map);
  const center = box.getCenter(new THREE.Vector3());
  map.position.x = map.position.x - center.x;
  map.position.z = map.position.z - center.z;
};

在这里插入图片描述

添加行政区边界线

在创建各个块元素材质的时候就已经可以拿到边界线的数据了,这里封装一个方法。

  • 先把经纬度转成xy坐标然后存在point里面
  • 创建BufferGeometry,可以直接通过setFromPoints将所有的点数据给塞进去,这样就创建了一个Geometry
  • 再创建两个材质只要指定一下线的颜色,然后调整一下他们的z坐标,返回出去即可
  • 最后在初始化地图的地方一起调用即可
const createLine = (data, depth) => {
  const points = [];
  data.forEach((item) => {
    const [x, y] = offsetXY(item);
    points.push(new THREE.Vector3(x, -y, 0));
  });
  const lineGeometry = new THREE.BufferGeometry().setFromPoints(points);
  // 地图上面的线
  const upLineMaterial = new THREE.LineBasicMaterial({color: '#000000'});
  // 地图下面的线
  const downLineMaterial = new THREE.LineBasicMaterial({color: '#000000'});

  const upLine = new THREE.Line(lineGeometry, upLineMaterial);
  const downLine = new THREE.Line(lineGeometry, downLineMaterial);
  downLine.position.z = -0.1;
  upLine.position.z = depth + 0.1;
  return [upLine, downLine];
};

// 调用这个:创建面、创建线,然后统统加到Object3D对象当中
const mesh = createMesh(coordinate, '#63bbd0', depth);
const line = createLine(coordinate, depth);
unit.add(mesh, ...line);

在这里插入图片描述

添加省市信息

还是一样的,在json文件当中我们可以拿到省市的经纬度数据和名称,在这里创建一个div通过CSS2DObject将div加到three当中,然后就是把经纬度坐标转换成xy坐标,其中y坐标是反的所以去一个反(在前面有提到过了),然后z也要+depth(地图的高度)这样lable标签也就完整的渲染到地图上了。

const createLabel = (name, point, depth) => {
  const div = document.createElement('div');
  div.style.color = '#000';
  div.style.fontSize = '12px';
  // 这个见仁见智哈,感觉加上整个都变模糊了
  // div.style.textShadow = '1px 1px 2px #047cd6';
  div.textContent = name;
  const label = new CSS2DObject(div);
  label.scale.set(0.01, 0.01, 0.01);
  const [x, y] = offsetXY(point);
  label.position.set(x, -y, depth);
  return label;
};

在这里插入图片描述

添加纹理贴图

在实际开发过程当中,如果是要做那种山脉、地形的图我们就需要添加纹理到MeshStandardMaterial当中,上面只是简单的用颜色来控制展示,举一反三:上面的颜色值是固定的,定义一个获取随机颜色的方法替换掉固定的颜色值就是随机颜色的地图了。

这里还有一个小缺陷的,在这里加载纹理图片上来(图片可以随便来个),然后给纹理进行相对应的配置,直接给mesh加上,但是感觉纹理贴图贴在了边缘,没有贴到正面。这个也有点懵逼,不知道到底贴到了正面了没。正面看起来的效果倒也还行主要是

const mapTexture = new THREE.TextureLoader().load(mapTextureImage);
mapTexture.ratation = Math.PI;
mapTexture.wrapS = THREE.RepeatWrapping;
mapTexture.wrapT = THREE.RepeatWrapping;
mapTexture.repeat.set(1, 1);
mapTexture.needsUpdate = true;

const materialSettings = {
  map: mapTexture,
  bumpMap: mapTexture,
  bumpScale: 0.01,
  transparent: true,
  opacity: 0.8,
  side: THREE.DoubleSide
};
const material = new THREE.MeshStandardMaterial(materialSettings);

添加图标

这个和前面添加label文字是一样的,我们可以拿到同样的经纬度坐标再转换成xy坐标,之后加个texture进行渲染

  • Sprite是一个总是面朝着摄像机的平面,通常含有使用一个半透明的纹理
  • 注意点:在遍历json里面的数据记得过滤掉不存在的数据(name为空的)
const createIcon = (point, depth) => {
  const texture = new THREE.TextureLoader().load(CityImage);
  const material = new THREE.SpriteMaterial({
    map: texture,
    transparent: true
  });
  const sprite = new THREE.Sprite(material);
  const [x, y] = offsetXY(point);
  // 因为这里地图、贴图也是有高度的,所以位置往上拉高点
  sprite.position.set(x, -y, depth + 0.5);
  sprite.renderOrder = 1;
  return sprite;
};

在这里插入图片描述

监听点击

在这里由于所有的经纬度都转换成了xy坐标,我们就可以通过拿到鼠标点击的xy位置再去反推点击的是哪一个three对象

  • Raycaster(光线投射):用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)
  • setFromCamera:入参(在标准化设备坐标中鼠标的二维坐标、射线所来源的摄像机)
  • intersectObjects:检查与射线相交的物体
  • 到这一步再过滤掉线数据,之后看点击的类型是什么,这个里面也就有我们前面通过unit.name = name;unit.adcode = adcode;存的名称编码信息了
window.addEventListener('click', (event) => {
  const mouse = new THREE.Vector2();
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  const raycaster = new THREE.Raycaster();
  raycaster.setFromCamera(mouse, camera);
  const intersects = raycaster
    .intersectObjects(map.children)
    .filter((item) => item.object.type !== 'Line');
  if (intersects.length > 0) {
    // 点击市
    if (intersects[0].object.type === 'Mesh') {
      console.log(intersects[0]);
    }
    // 点击icon
    if (intersects[0].object.type === 'Sprite') {
      console.log(intersects[0]);
    }
  }
});

完整代码

<template>
  <div id="map" class="w-full h-full"></div>
</template>

<script setup>
import * as THREE from 'three';
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
import {CSS2DObject, CSS2DRenderer} from 'three/examples/jsm/renderers/CSS2DRenderer.js';
import {onMounted, ref} from 'vue';
import * as d3 from 'd3';
import ChinaData from '@/assets/mapJson/china.json';
import CityImage from '@/assets/image/city.png';
import mapTextureImage from '@/assets/image/map-texture.png';

const mapElement = ref();

// 创建场景
const scene = new THREE.Scene();

// 添加坐标轴
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

// 如果用金属反光材质就用这个光
const colorLight = () => {
  const ambientLight = new THREE.AmbientLight(0xd4e7fd, 4);
  scene.add(ambientLight);
  const directionalLight = new THREE.DirectionalLight(0xe8eaeb, 0.2);
  directionalLight.position.set(0, 10, 5);
  const directionalLight2 = directionalLight.clone();
  directionalLight2.position.set(0, 10, -5);
  const directionalLight3 = directionalLight.clone();
  directionalLight3.position.set(5, 10, 0);
  const directionalLight4 = directionalLight.clone();
  directionalLight4.position.set(-5, 10, 0);
  scene.add(directionalLight);
  scene.add(directionalLight2);
  scene.add(directionalLight3);
  scene.add(directionalLight4);
};

// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.y = 64;
camera.position.z = 64;

// 如果用地图纹理就用这个光
const mapTextureLight = () => {
  const ambientLight = new THREE.AmbientLight('white', 0.5);
  ambientLight.position.set(5, 10, 5);
  scene.add(ambientLight);

  const light = new THREE.DirectionalLight('#fff', 1);
  light.position.set(5, 10, 5);
  camera.add(light);
};

// 创建渲染器
const labelRenderer = new CSS2DRenderer();
const addRenderer = () => {
  labelRenderer.domElement.style.position = 'absolute';
  labelRenderer.domElement.style.top = '0px';
  labelRenderer.domElement.style.pointerEvents = 'none';
  labelRenderer.setSize(window.innerWidth, window.innerHeight);
  const ele = document.getElementById('map');
  ele.appendChild(labelRenderer.domElement);
};

// 窗口大小变化监听器
const renderer = new THREE.WebGLRenderer({alpha: true});
renderer.setSize(window.innerWidth, window.innerHeight);

// 控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.update();

const animate = () => {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
  labelRenderer.render(scene, camera);
};

onMounted(() => {
  addRenderer();
  document.getElementById('map')?.appendChild(renderer.domElement);
  animate();
  mapTextureLight();
  createMap(ChinaData);
});

// 矫正坐标
const offsetXY = d3.geoMercator();

const getRandomColor = () => {
  return '#' + Math.floor(Math.random() * 16777215).toString(16);
};

// 根据省市的json数据创建地图
const createMap = (data) => {
  const map = new THREE.Object3D();
  const center = data.features[0].properties.centroid;
  offsetXY.center(center).translate([0, 0]);
  data.features.forEach((feature) => {
    const unit = new THREE.Object3D();
    const {centroid, center, name, adcode} = feature.properties;
    const {coordinates, type} = feature.geometry;
    const depth = 1;
    // 绘制每个市的名称和图标
    const label = createLabel(name, centroid || center || [0, 0], depth);
    const icon = name ? createIcon(centroid || center || [0, 0], 1.11) : null;
    coordinates.forEach((coordinate) => {
      if (type === 'MultiPolygon') coordinate.forEach((item) => fn(item));
      if (type === 'Polygon') fn(coordinate);

      function fn(coordinate) {
        // 添加自定义属性,点击的时候可以打印出来
        unit.name = name;
        unit.adcode = adcode;
        // 绘制每个市的区域(传入颜色和深度)
        const mesh = createMesh(coordinate, '#63bbd0', depth);
        // 绘制每个市的边界
        const line = createLine(coordinate, depth);
        unit.add(mesh, ...line);
      }
    });
    map.add(unit, label);
    icon ? unit.add(icon) : null;
  });
  setCenter(map);
  scene.add(map);

  window.addEventListener('click', (event) => {
    const mouse = new THREE.Vector2();
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
    const raycaster = new THREE.Raycaster();
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster
        .intersectObjects(map.children)
        .filter((item) => item.object.type !== 'Line');
    if (intersects.length > 0) {
      // 点击市
      if (intersects[0].object.type === 'Mesh') {
        console.log(intersects[0]);
      }
      // 点击icon
      if (intersects[0].object.type === 'Sprite') {
        console.log(intersects[0]);
      }
    }
  });
};

/**
 * 绘制每个市的区域
 * @param data 坐标数据
 * @param color 颜色
 * @param depth 深度
 * */
const mapTexture = new THREE.TextureLoader().load(mapTextureImage);
mapTexture.ratation = Math.PI;
mapTexture.wrapS = THREE.RepeatWrapping;
mapTexture.wrapT = THREE.RepeatWrapping;
mapTexture.repeat.set(1, 1);
mapTexture.needsUpdate = true;


const createMesh = (data, color, depth) => {

  const shape = new THREE.Shape();
  data.forEach((item, idx) => {
    const [x, y] = offsetXY(item);

    if (idx === 0) shape.moveTo(x, -y);
    else shape.lineTo(x, -y);
  });

  const extrudeSettings = {
    depth: depth,
    bevelEnabled: false
  };
  // 图片纹理贴图配置
  const materialSettings = {
    map: mapTexture,
    bumpMap: mapTexture,
    bumpScale: 0.01,
    transparent: true,
    opacity: 0.8,
    side: THREE.DoubleSide
  };
  // 金属反射配置
  const materialSettings1 = {
    color: color,
    emissive: 0x000000,
    roughness: 0.45,
    metalness: 0.8,
    transparent: true,
    side: THREE.DoubleSide
  };
  const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
  const material = new THREE.MeshStandardMaterial(materialSettings);
  return new THREE.Mesh(geometry, material);
};

// 绘制每个市的边界
const createLine = (data, depth) => {
  const points = [];
  data.forEach((item) => {
    const [x, y] = offsetXY(item);
    points.push(new THREE.Vector3(x, -y, 0));
  });
  const lineGeometry = new THREE.BufferGeometry().setFromPoints(points);
  const upLineMaterial = new THREE.LineBasicMaterial({color: '#ffffff'});
  const downLineMaterial = new THREE.LineBasicMaterial({color: '#ffffff'});

  const upLine = new THREE.Line(lineGeometry, upLineMaterial);
  const downLine = new THREE.Line(lineGeometry, downLineMaterial);
  downLine.position.z = -0.1;
  upLine.position.z = depth + 0.1;
  return [upLine, downLine];
};

// 绘制每个市的名称
const createLabel = (name, point, depth) => {
  const div = document.createElement('div');
  div.style.color = '#fff';
  div.style.fontSize = '14px';
  div.textContent = name;
  const label = new CSS2DObject(div);
  label.scale.set(0.01, 0.01, 0.01);
  const [x, y] = offsetXY(point);
  label.position.set(x, -y, depth);
  return label;
};

// 创建图标
const createIcon = (point, depth) => {
  const texture = new THREE.TextureLoader().load(CityImage);
  const material = new THREE.SpriteMaterial({
    map: texture,
    transparent: true
  });
  const sprite = new THREE.Sprite(material);
  const [x, y] = offsetXY(point);
  sprite.position.set(x, -y, depth + 0.5);
  sprite.renderOrder = 1;
  return sprite;
};

// 设置地图中心
const setCenter = (map) => {
  map.rotation.x = -Math.PI / 2;
  const box = new THREE.Box3().setFromObject(map);
  const center = box.getCenter(new THREE.Vector3());
  map.position.x = map.position.x - center.x;
  map.position.z = map.position.z - center.z;
};

</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2134706.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SQL server 6.5升级到SQL server 2019的方法

背景&#xff1a; 对日项目&#xff0c;客户的旧系统的数据库用的是SQL server 6.5&#xff0c;操作系统是windows NT。新系统要求升级到SQL server 2019&#xff0c;查了下资料发现旧系统的版本实在是太久远了&#xff0c;90年代的。 数据库部分的升级思路是这样的&#xff…

git 更新LingDongGui问题解决

今天重新更新灵动gui的代码&#xff0c;以便使用最新的arm-2d&#xff0c;本来以为是比较简单的一件事情&#xff08;因为以前已经更新过一次&#xff09;&#xff0c;却搞了大半天&#xff0c;折腾不易啊&#xff0c;简单记录下来&#xff0c;有同样遇到问题的同学参考&#x…

AI算法部署方式对比分析:哪种方案性价比最高?

随着人工智能技术的飞速发展&#xff0c;AI算法在各个领域的应用日益广泛。AI算法的部署方式直接关系到系统的性能、实时性、成本及安全性等多个方面。本文将探讨AI算法分析的三种主要部署方式&#xff1a;本地计算、边缘计算和云计算&#xff0c;并详细分析它们的优劣性。 一、…

基于vue框架的宠物交流平台1n2n3(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;会员,宠物信息,宠物类型,团队信息,申请领养,团队申请,领养宠物 开题报告内容 基于Vue框架的宠物交流平台开题报告 一、项目背景 随着现代生活节奏的加快与人们情感需求的日益增长&#xff0c;宠物已成为众多家庭不可或缺的重要成员。…

基于Python的影视数据可视化---附源码75141

摘 要 本文基于Python语言&#xff0c;设计并实现了一个影视数据可视化系统&#xff0c;包括首页、公告通知、新闻资讯和电影信息等功能模块。通过对影视数据的采集、处理和可视化展示&#xff0c;该系统旨在为用户提供全面的影视信息和数据分析服务。在研究背景中&#xff0c…

编译运行 webAssembly(wasm)

环境准备&#xff1a; lunix下docker 参考https://hub.docker.com/r/emscripten/emsdk 拉编译环境 docker pull emscripten/emsdk 编译 随便找个目录&#xff0c;敲下面命令&#xff0c;编译一个webAssembly 程序 # create helloworld.cpp cat << EOF > hellowo…

Android Studio 新生成key store 打包apk报 Invalid keystore format

Android Studio 新生成key store 打包apk报错 Execution failed for task :app:packageDebug. > A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade > com.android.ide.common.signing.KeytoolException: Failed …

充电宝什么品牌比较好用?2024年最值得推荐充电宝品牌!

近年来&#xff0c;随着电子设备使用需求的增加&#xff0c;充电宝市场呈现出蓬勃发展的态势。优秀的充电宝产品不仅能够提供稳定的充电速度&#xff0c;还具备方便携带的体验&#xff0c;深受用户喜爱。然而&#xff0c;面对市场上众多品牌和型号的选择&#xff0c;如何找到最…

C++库std::clamp

C库std::clamp std::clamp: 轻松掌握值的范围限制 目录 1. 引言2. std::clamp 基本概念2.1 函数签名2.2 参数说明2.3 返回值 3. 基本用法4. 深入理解 std::clamp4.1 实现原理4.2 注意事项 5. 高级用法5.1 自定义比较函数5.2 与 lambda 表达式结合 6. 实际应用场景6.1 图形编程…

全球安防监控、工业检测摄像机市场规模情况一览

一、全球安防监控市场规模情况综合分析 &#xff08;1&#xff09;全球安防监控摄像机市场规模 全球市场研究公司Research Nester统计&#xff0c;2023年全球安防监控摄像机市场规模为811.1亿元&#xff0c;预测到2028年&#xff0c;全球安全与监控市场规模预计将达到1869.3亿…

将 Parallels Desktop(PD虚拟机)安装在移动硬盘上,有影响吗?

当我们谈论在移动硬盘上安装 Parallels Desktop&#xff08;简称PD虚拟机&#xff09;及其对性能的影响时&#xff0c;特别是在运行如Unigraphics这样的资源密集型软件时&#xff0c;用户需要在便携性与性能之间找到最佳平衡。本文将深入探讨PD虚拟机装在移动硬盘有影响吗&…

(javaweb)mysql---DDL

一.数据模型&#xff0c;数据库操作 1.二维表&#xff1a;有行有列 2. 3.客户端连接数据库&#xff0c;发送sql语句给DBMS&#xff08;数据库管理系统&#xff09;&#xff0c;DBMS创建--以文件夹显示 二.表结构操作--创建 database和schema含义一样。 这样就显示出了之前的内容…

类和对象(中)【上篇】(构造,析构,拷贝函数)

&#x1f31f;个人主页&#xff1a;落叶 目录 类的默认成员函数 构造函数 无参构造 带参构造函数 全缺省构造函数 析构函数 对⽐C和C解决括号匹配问题 C语言版的Stack C版的Stack 拷⻉构造函数 类的默认成员函数 默认成员函数就是⽤⼾没有显式实现&#xff0c;编译器会…

如何查看微信聊天记录,防员工私单(有效监管员工电脑微信聊天的方法)

在企业管理中&#xff0c;防止员工私单&#xff08;即员工绕过公司直接与客户交易&#xff09;是管理中的一大难题。 许多员工使用微信进行日常工作沟通&#xff0c;而如果管理不到位&#xff0c;容易产生私单问题&#xff0c;影响企业的利益。 为了解决这一问题&#xff0c;…

【自费2W真机测评】三款热门/旗舰宠物空气净化器米家、希喂、352对比试用!

我家老大是三个月大的时候接回来的&#xff0c;接回来前就是家教好的小猫咪一只&#xff0c;不乱尿、不掉毛的。看朋友家都被猫咪掉毛困扰着&#xff0c;我还嘚瑟觉得自己养可好了&#xff0c;根本不掉毛。养了三个月老大长成大猫猫了&#xff0c;我又觉得我可以了&#xff0c;…

浏览器插件快速开启/关闭IDM接管下载

假设你已经为浏览器安装了IDM扩展&#xff0c;那么按下图的点击顺序&#xff0c;可以快速开启或关闭IDM的下载接管&#xff0c;而不必在IDM软件的设置->选项中&#xff0c;临时作调整。

再次进阶 舞台王者 第八季完美童模全球赛代言人【孟梓娴】赛场+秀场超燃合集!

7月20-23日&#xff0c;2024第八季完美童模全球总决赛在青岛圆满落幕。在盛大的颁奖典礼上&#xff0c;一位才能出众的少女——孟梓娴迎来了她舞台生涯的璀璨时刻。 代言人——孟梓娴&#xff0c;以璀璨童星之姿&#xff0c;优雅地踏上完美童模盛宴的绚丽舞台&#xff0c;作为开…

Qt-QLabel显示类控件(25)

目录 描述 相关属性介绍 使用 纯文本、富文本、markdown 显示图片 实时修改图片尺寸 文本对齐 Box框 水平 垂直对其 其他的文本对齐 设置伙伴 描述 用来显示文本的&#xff0c;如果看了之前的&#xff0c;那么对这个控件应该不陌生&#xff0c;这里我们就详细介绍…

初步了解python数据分析

首先输入 pip install pandas 安装pandas&#xff0c;安装完成如下&#xff0c; 一个示例&#xff0c; import pandas as pddata {A: [11, 22, 33, 44, 55], B: [51, 4, 33, 2, 1]} df1 pd.DataFrame(data)matrix1 df1.corr() print(matrix1) 输出如下&#xff0c; Pandas …

ppt怎么做的好看?这些技巧让你的演示更上一层楼

当中秋佳节来临&#xff0c;准备一场生动有趣的中秋节科普活动时&#xff0c;一套精美的PPT模板无疑是点睛之笔。它不仅能让你的科普内容更加引人入胜&#xff0c;还能在视觉上给观众留下深刻印象。 为了帮助你快速打造出这样一份PPT&#xff0c;以下是几款优质的ppt模板免费软…