ArcGIS 中只支持2D、3D的线、面的测量。
体积控件:基于面积测量控件,手动输入高度进行计算;
代码
VolumeMeasurement.vue
<template>
<div class="volume-measurement esri-widget" v-show="widget" ref="parentRef">
<div class="volume-measurement-content">
<div>
<span>面积:</span>
<div class="esri-area-measurement-3d__measurement-item-value">{{ data.area }} m²</div>
</div>
<div>
<span>高度:</span>
<el-input mini v-model="data.height" placeholder="最大:9999.99" :formatter="handleFormatter" />
<i class="esri-area-measurement-3d__measurement-item-value" style="margin-left: 5px; font-style: normal">m</i>
</div>
<div>
<span>体积:</span>
<div class="esri-area-measurement-3d__measurement-item-value">{{ volume }} m³</div>
</div>
</div>
</div>
</template>
<script setup>
import Decimal from "decimal.js";
import AreaMeasurement3D from "@arcgis/core/widgets/AreaMeasurement3D.js";
// 父节点
const parentRef = ref(null);
// 控件实例
const widget = shallowRef(null);
// 体积数据
const data = reactive({
area: 0,
height: 0,
});
// 计算体积
const volume = computed(() => {
const val = new Decimal(data.area).mul(data.height || 0);
return val.toNumber() ? val.toFixed(2) : 0;
});
// 输入校验
function handleFormatter(val) {
// 移除所有非数字字符,除了第一个出现的小数点
let res = val.replace(/^(0|[1-9]\d{1,3})(\.\d{0,2})?.*/, "$1$2");
// 如果结果为空或不是有效的数字,则将其设为''
if (!res.match(/^\d*(\.\d{0,2})?$/)) return "";
return res;
}
// 测量体积
function start(view) {
// 创建节点
const ele = document.createElement("div");
parentRef.value.prepend(ele);
ele.addEventListener("click", reset);
// 创建控件
const w = (widget.value = new AreaMeasurement3D({
view,
container: ele,
unit: "square-meters",
unitOptions: ["square-meters"],
}));
triggerMeasureVolume(ele);
w.when(async () => {
const analysisView = await view.whenAnalysisView(w.analysis);
const handle = analysisView.watch("result", (r) => {
data.area = r.area ? new Decimal(r.area.value).toFixed(2) : 0;
});
w.own(handle);
});
}
// 触发测量
function triggerMeasureVolume(wrapper) {
const esriBtn = wrapper?.querySelector(`button.esri-area-measurement-3d__clear-button`);
if (esriBtn) esriBtn.click();
else setTimeout(() => triggerMeasureVolume(wrapper), 30);
}
// 重置
function reset() {
data.area = 0;
data.height = 0;
}
// 销毁
function destroy() {
widget.value?.destroy();
widget.value = null;
reset();
}
defineExpose({
start,
destroy,
});
</script>
<style lang="scss" scoped>
.volume-measurement {
padding: 15px 12px;
:deep() {
.esri-area-measurement-3d__container {
padding: 0;
.esri-area-measurement-3d__settings,
.esri-area-measurement-3d__measurement {
display: none;
}
}
}
&-content {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 10px;
& > div {
display: flex;
align-items: center;
span {
display: inline-block;
flex-shrink: 0;
width: 42px;
}
}
}
}
</style>
使用
通过点击`体积测量工具`的 div,触发控件的 start 和 destroy 方法;
map.vue
<template>
<div id="map">
<div
class="esri-widget--button esri-interactive esri-icon-elevation-profile m-widget m-widget-volume"
:class="[activeWidget === 'volume' ? 'active' : '']"
title="体积测量工具"
@click="handleMeasureVolume()"
></div>
<!-- 体积控件 -->
<volume-measurement class="m-widget m-widget-measure-volume" ref="volumeMeasureRef" />
</div>
</template>
<script setup>
import VolumeMeasurement from "./widgets/VolumeMeasurement.vue";
// other imports
const view = shallowRef(null);
const activeWidget = ref(null);
onMounted(()=>{
view.value = createSceneView();
})
function handleMeasureVolume() {
activeWidget.value = activeWidget.value === "volume" ? null : "volume";
if (activeWidget.value) volumeMeasureRef.value.start(view.value);
else volumeMeasureRef.value.destroy();
}
</script>