measurement widget
事件?
The 4.x Measure widget doesn't have events.
如果想监听完成,应该怎么办?
使用 watch
this.measure = new Measurement({
view: this.view,
container: this.container,
areaUnit: 'square-meters',
linearUnit: 'meters',
});
const handler = this.measure.watch('viewModel.state', (newValue, oldValue, propertyName, target) => {
if (newValue == 'measured') {
const graphics = target.viewModel.activeViewModel.tool._measurementLayer.graphics;
this.saveGraphicToResultLayer(graphics);
}
});
this.measure.addHandles(handler);
saveGraphicToResultLayer(graphics) {
if (this.resultLayer) {
this.view.map.reorder(this.resultLayer, this.view.map.allLayers.length);
} else {
this.resultLayer = new GraphicsLayer({
id: 'measurement-result-layer',
});
this.view?.map.add(this.resultLayer);
}
const cloneGras = graphics.map((gra) => {
const n = gra.clone();
n.visible = false;
return n;
});
this.resultLayer.addMany(cloneGras);
}
参考
Possible events for new Measurement widgets 4.10
Measurement 保存geometry?
需求
api 现在画一下,一个结果,画一下一个结果,不会保存上一个结果的。
所以如果需求是测量一下,再次测量时,保留上一次测量的结果。 需要自己处理一下。。
思路
- 监听测量完成事件,保存结果咯。
实现
this.measure = new Measurement({
view: this.view,
container: this.container,
areaUnit: 'square-meters',
linearUnit: 'meters',
});
const handler = this.measure.watch('viewModel.state', (newValue, oldValue, propertyName, target) => {
if (newValue == 'measured') {
const graphics = target.viewModel.activeViewModel.tool._measurementLayer.graphics;
this.saveGraphicToResultLayer(graphics);
}
});
this.measure.addHandles(handler);
参考
How to get the geometry of measurement Widget 4.26
measurement widget show segment length
需求
希望 Measurement 的 distance只显示总长度,希望能显示每条线段的长度。
源码分析
从DistanceMeasurement2DTool.js
源码可以看出来,在地图上边画线段,边显示长度是在这个文件里执行的。
其实主要就是draw
,监听drawAction
的事件,然后更新_updatePolylines
咯。
_startSketch() {
this._drawActive = !0;
const e = this._draw.create("polyline", { mode: "click" });
e.on(
[
"vertex-add",
"vertex-update",
"vertex-remove",
"cursor-update",
"undo",
"redo",
],
(e) => this._updateSketch(e.vertices)
),
e.on("draw-complete", () => {
this._stopSketch();
});
}
_updatePolylines() {
if (this._vertices.length < 2)
return void this._measurementLayer.removeAll();
// 获取所有顶点的坐标
const e = this._vertices.map(({ coord: e }) => e)
// 使用 O 函数测量折线,得到测量结果、绘图几何和原始折线
const {
measurement: o,
drawing: a,
original: n,
} = O(e, this.view.spatialReference, this.geodesicDistanceThreshold);
this._set("measurement", o);
const l = H(this.messages, o, this.unit);
let m, c;
this._set("measurementLabel", l);
const { graphics: h } = this._measurementLayer,
p = 2 === h.length,
{ effectiveTheme: u } = this.view;
// 如果已有两个图形,分别设置 m 和 c
// 否则,创建新的折线和标签图形
p
? ((m = h.at(0)), (c = h.at(1)))
: ((m = new t({
symbol: new D({
data: {
type: "CIMSymbolReference",
symbol: {
type: "CIMLineSymbol",
symbolLayers: [
{
type: "CIMSolidStroke",
effects: [
{
type: "CIMGeometricEffectDashes",
dashTemplate: [14, 12],
lineDashEnding: "FullGap",
controlPointEnding: "NoConstraint",
},
],
enable: !0,
capStyle: "Butt",
joinStyle: "Round",
width: 3.5,
color: r(u.accentColor).toArray(),
},
{
type: "CIMSolidStroke",
enable: !0,
capStyle: "Butt",
joinStyle: "Round",
width: 5,
color: u.accentColor.toArray(),
},
],
},
},
}),
})),
(c = new t({
symbol: new G({
color: u.textColor,
haloColor: s(r(u.textColor, i.Low), 0.5),
haloSize: 2,
font: new R({ size: 14, family: "sans-serif" }),
}),
})),
h.removeAll(),
h.addMany([m, c])),
(m.geometry = a),
(c.geometry = n.extent?.center),
(c.symbol.text = l);
}
};
思路
本来我想监听一下它的事件,然后添加一个图层用来记录分段长度值。
但是发现好像不可行。
那就直接去修改源码好了。
实现
只需要监听viewModel.activeViewModel.tool
的变化,当它被创建后,直接替换它原来的_updatePolylines
方法咯,加上我自己要加的代码。
addSegmentLength
:
- 只保留
_measurementLayer
中前两个graphics
,其它都删掉。 - 遍历
vertices
,两两算线段长度,添加到_measurementLayer
中。 - (计算线段长度的用的是源码里的方法,我给改了一下而已。)
initMeasure() {
this.showResultLayer();
if (!this.measure) {
this.measure = new Measurement({
view: this.view,
container: this.container,
// areaUnit: 'square-kilometers',
// linearUnit: 'kilometers',
areaUnit: 'square-meters',
linearUnit: 'meters',
});
const handler = this.measure.watch('viewModel.state', (newValue, oldValue, propertyName, target) => {
if (newValue == 'measured') {
const graphics = target.viewModel.activeViewModel.tool._measurementLayer.graphics;
this.saveGraphicToResultLayer(graphics);
}
});
this.measure.addHandles(handler);
const _self = this;
const handler2 = this.measure.watch('viewModel.activeViewModel.tool', (newValue, oldValue, propertyName, target) => {
if (newValue) {
const _updatePolylines = this.measure.viewModel.activeViewModel.tool._updatePolylines;
this.measure.viewModel.activeViewModel.tool._updatePolylines = function () {
_updatePolylines.call(this);
_self.addSegmentLength.call(this);
};
}
});
this.measure.addHandles(handler2);
// window.ahaM = this.measure;
}
}
/**
* Monkey patching
*/
async addSegmentLength() {
const {graphics : h2} = this._measurementLayer;
// 如果图形数量超过 2
if (h2?.length > 2) {
const firstTwoGraphics = [];
firstTwoGraphics.push(h2[0].clone());
firstTwoGraphics.push(h2[1].clone());
this._measurementLayer.removeAll();
this._measurementLayer.addMany(firstTwoGraphics);
}
const _vertices = this._vertices;
if (!_vertices || _vertices.length < 3) {
return;
}
const spatialReference = this.view.spatialReference;
const geodesicDistanceThreshold = this.geodesicDistanceThreshold;
const c2 = h2.at(1);
const symbol = c2.symbol;
symbol.font.size = 10;
for (let i = 1; i < _vertices.length; i++) {
const verticesTwo = [_vertices[i - 1], _vertices[i]];
const tmp = verticesTwo.map(({ coord: e }) => e);
const {
measurement: o2,
drawing: a2,
original: n2,
} = await O2(tmp, spatialReference, geodesicDistanceThreshold);
const l2 = o2.length.toFixed(2) + ' m';
const c3 = new Graphic({
symbol: symbol.clone(),
});
c3.geometry = n2.extent?.center;
c3.symbol.text = l2;
h2.add(c3);
}
}
// 计算线段长度
async function O2(vertices, spatialReference, r) {
const U = 1e5;
const line = new Polyline({ paths: [vertices], spatialReference: spatialReference });
let length, geom;
await projection.load();
if (spatialReference.isGeographic) {
const e = projection.project(line, SpatialReference.WGS84);
const r = geodesicUtils.geodesicDensify(e, U);
length = geodesicUtils.geodesicLengths([e], 'meters')[0];
geom = projection.project(r, spatialReference);
} else if (spatialReference.isWebMercator) {
length = geometryEngine.geodesicLength(line, 'meters');
geom = geometryEngine.geodesicDensify(line, U, 'meters');
} else {
const e = geometryEngine.planarLength(line, 'meters');
if (null != r && e >= r) {
const e = projection.project(line, SpatialReference.WGS84);
const r = geodesicUtils.geodesicDensify(e, U);
length = geodesicUtils.geodesicLengths([e], 'meters')[0];
geom = projection.project(r, spatialReference);
} else {
length = e;
geom = line;
}
}
return {
measurement: { geometry: geom, length },
original: line,
drawing: geom,
};
}
补充知识
计算线段长度
api里计算长度提供的方法,只适用于wgs84和墨卡托投影。
所以,如果是别的坐标系,就需要用先转到wgs84/墨卡托,计算完长度,再转回来。