feat: 实现供水运营专题大屏BI可视化
- 新增OperationDashboard.vue全屏大屏组件 - 添加6个核心KPI指标卡片: 进水总量/出水总量/产销差率/营收额/平均水质/报警次数 - 实现6个ECharts图表: 供水趋势/水质分布/报警统计/管网空间/设备状态/营收分析 - 创建静态HTML版本operation-dashboard.html,使用CDN加载Vue3/ECharts/Element Plus - 更新路由配置,添加/operation路径支持 - 修复nextTick导入问题,优化build脚本 🚧 开发者: bot_dev1 📝 任务: #38 [BI] 运营仪表盘 + 供水专题大屏
This commit is contained in:
+39
@@ -0,0 +1,39 @@
|
||||
import transform from "./transform.js";
|
||||
|
||||
export default function(topology) {
|
||||
var t = transform(topology.transform), key,
|
||||
x0 = Infinity, y0 = x0, x1 = -x0, y1 = -x0;
|
||||
|
||||
function bboxPoint(p) {
|
||||
p = t(p);
|
||||
if (p[0] < x0) x0 = p[0];
|
||||
if (p[0] > x1) x1 = p[0];
|
||||
if (p[1] < y0) y0 = p[1];
|
||||
if (p[1] > y1) y1 = p[1];
|
||||
}
|
||||
|
||||
function bboxGeometry(o) {
|
||||
switch (o.type) {
|
||||
case "GeometryCollection": o.geometries.forEach(bboxGeometry); break;
|
||||
case "Point": bboxPoint(o.coordinates); break;
|
||||
case "MultiPoint": o.coordinates.forEach(bboxPoint); break;
|
||||
}
|
||||
}
|
||||
|
||||
topology.arcs.forEach(function(arc) {
|
||||
var i = -1, n = arc.length, p;
|
||||
while (++i < n) {
|
||||
p = t(arc[i], i);
|
||||
if (p[0] < x0) x0 = p[0];
|
||||
if (p[0] > x1) x1 = p[0];
|
||||
if (p[1] < y0) y0 = p[1];
|
||||
if (p[1] > y1) y1 = p[1];
|
||||
}
|
||||
});
|
||||
|
||||
for (key in topology.objects) {
|
||||
bboxGeometry(topology.objects[key]);
|
||||
}
|
||||
|
||||
return [x0, y0, x1, y1];
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
export default function(a, x) {
|
||||
var lo = 0, hi = a.length;
|
||||
while (lo < hi) {
|
||||
var mid = lo + hi >>> 1;
|
||||
if (a[mid] < x) lo = mid + 1;
|
||||
else hi = mid;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
import reverse from "./reverse.js";
|
||||
import transform from "./transform.js";
|
||||
|
||||
export default function(topology, o) {
|
||||
if (typeof o === "string") o = topology.objects[o];
|
||||
return o.type === "GeometryCollection"
|
||||
? {type: "FeatureCollection", features: o.geometries.map(function(o) { return feature(topology, o); })}
|
||||
: feature(topology, o);
|
||||
}
|
||||
|
||||
function feature(topology, o) {
|
||||
var id = o.id,
|
||||
bbox = o.bbox,
|
||||
properties = o.properties == null ? {} : o.properties,
|
||||
geometry = object(topology, o);
|
||||
return id == null && bbox == null ? {type: "Feature", properties: properties, geometry: geometry}
|
||||
: bbox == null ? {type: "Feature", id: id, properties: properties, geometry: geometry}
|
||||
: {type: "Feature", id: id, bbox: bbox, properties: properties, geometry: geometry};
|
||||
}
|
||||
|
||||
export function object(topology, o) {
|
||||
var transformPoint = transform(topology.transform),
|
||||
arcs = topology.arcs;
|
||||
|
||||
function arc(i, points) {
|
||||
if (points.length) points.pop();
|
||||
for (var a = arcs[i < 0 ? ~i : i], k = 0, n = a.length; k < n; ++k) {
|
||||
points.push(transformPoint(a[k], k));
|
||||
}
|
||||
if (i < 0) reverse(points, n);
|
||||
}
|
||||
|
||||
function point(p) {
|
||||
return transformPoint(p);
|
||||
}
|
||||
|
||||
function line(arcs) {
|
||||
var points = [];
|
||||
for (var i = 0, n = arcs.length; i < n; ++i) arc(arcs[i], points);
|
||||
if (points.length < 2) points.push(points[0]); // This should never happen per the specification.
|
||||
return points;
|
||||
}
|
||||
|
||||
function ring(arcs) {
|
||||
var points = line(arcs);
|
||||
while (points.length < 4) points.push(points[0]); // This may happen if an arc has only two points.
|
||||
return points;
|
||||
}
|
||||
|
||||
function polygon(arcs) {
|
||||
return arcs.map(ring);
|
||||
}
|
||||
|
||||
function geometry(o) {
|
||||
var type = o.type, coordinates;
|
||||
switch (type) {
|
||||
case "GeometryCollection": return {type: type, geometries: o.geometries.map(geometry)};
|
||||
case "Point": coordinates = point(o.coordinates); break;
|
||||
case "MultiPoint": coordinates = o.coordinates.map(point); break;
|
||||
case "LineString": coordinates = line(o.arcs); break;
|
||||
case "MultiLineString": coordinates = o.arcs.map(line); break;
|
||||
case "Polygon": coordinates = polygon(o.arcs); break;
|
||||
case "MultiPolygon": coordinates = o.arcs.map(polygon); break;
|
||||
default: return null;
|
||||
}
|
||||
return {type: type, coordinates: coordinates};
|
||||
}
|
||||
|
||||
return geometry(o);
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export default function(x) {
|
||||
return x;
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
export {default as bbox} from "./bbox.js";
|
||||
export {default as feature} from "./feature.js";
|
||||
export {default as mesh, meshArcs} from "./mesh.js";
|
||||
export {default as merge, mergeArcs} from "./merge.js";
|
||||
export {default as neighbors} from "./neighbors.js";
|
||||
export {default as quantize} from "./quantize.js";
|
||||
export {default as transform} from "./transform.js";
|
||||
export {default as untransform} from "./untransform.js";
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
import {object} from "./feature.js";
|
||||
import stitch from "./stitch.js";
|
||||
|
||||
function planarRingArea(ring) {
|
||||
var i = -1, n = ring.length, a, b = ring[n - 1], area = 0;
|
||||
while (++i < n) a = b, b = ring[i], area += a[0] * b[1] - a[1] * b[0];
|
||||
return Math.abs(area); // Note: doubled area!
|
||||
}
|
||||
|
||||
export default function(topology) {
|
||||
return object(topology, mergeArcs.apply(this, arguments));
|
||||
}
|
||||
|
||||
export function mergeArcs(topology, objects) {
|
||||
var polygonsByArc = {},
|
||||
polygons = [],
|
||||
groups = [];
|
||||
|
||||
objects.forEach(geometry);
|
||||
|
||||
function geometry(o) {
|
||||
switch (o.type) {
|
||||
case "GeometryCollection": o.geometries.forEach(geometry); break;
|
||||
case "Polygon": extract(o.arcs); break;
|
||||
case "MultiPolygon": o.arcs.forEach(extract); break;
|
||||
}
|
||||
}
|
||||
|
||||
function extract(polygon) {
|
||||
polygon.forEach(function(ring) {
|
||||
ring.forEach(function(arc) {
|
||||
(polygonsByArc[arc = arc < 0 ? ~arc : arc] || (polygonsByArc[arc] = [])).push(polygon);
|
||||
});
|
||||
});
|
||||
polygons.push(polygon);
|
||||
}
|
||||
|
||||
function area(ring) {
|
||||
return planarRingArea(object(topology, {type: "Polygon", arcs: [ring]}).coordinates[0]);
|
||||
}
|
||||
|
||||
polygons.forEach(function(polygon) {
|
||||
if (!polygon._) {
|
||||
var group = [],
|
||||
neighbors = [polygon];
|
||||
polygon._ = 1;
|
||||
groups.push(group);
|
||||
while (polygon = neighbors.pop()) {
|
||||
group.push(polygon);
|
||||
polygon.forEach(function(ring) {
|
||||
ring.forEach(function(arc) {
|
||||
polygonsByArc[arc < 0 ? ~arc : arc].forEach(function(polygon) {
|
||||
if (!polygon._) {
|
||||
polygon._ = 1;
|
||||
neighbors.push(polygon);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
polygons.forEach(function(polygon) {
|
||||
delete polygon._;
|
||||
});
|
||||
|
||||
return {
|
||||
type: "MultiPolygon",
|
||||
arcs: groups.map(function(polygons) {
|
||||
var arcs = [], n;
|
||||
|
||||
// Extract the exterior (unique) arcs.
|
||||
polygons.forEach(function(polygon) {
|
||||
polygon.forEach(function(ring) {
|
||||
ring.forEach(function(arc) {
|
||||
if (polygonsByArc[arc < 0 ? ~arc : arc].length < 2) {
|
||||
arcs.push(arc);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Stitch the arcs into one or more rings.
|
||||
arcs = stitch(topology, arcs);
|
||||
|
||||
// If more than one ring is returned,
|
||||
// at most one of these rings can be the exterior;
|
||||
// choose the one with the greatest absolute area.
|
||||
if ((n = arcs.length) > 1) {
|
||||
for (var i = 1, k = area(arcs[0]), ki, t; i < n; ++i) {
|
||||
if ((ki = area(arcs[i])) > k) {
|
||||
t = arcs[0], arcs[0] = arcs[i], arcs[i] = t, k = ki;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arcs;
|
||||
}).filter(function(arcs) {
|
||||
return arcs.length > 0;
|
||||
})
|
||||
};
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import {object} from "./feature.js";
|
||||
import stitch from "./stitch.js";
|
||||
|
||||
export default function(topology) {
|
||||
return object(topology, meshArcs.apply(this, arguments));
|
||||
}
|
||||
|
||||
export function meshArcs(topology, object, filter) {
|
||||
var arcs, i, n;
|
||||
if (arguments.length > 1) arcs = extractArcs(topology, object, filter);
|
||||
else for (i = 0, arcs = new Array(n = topology.arcs.length); i < n; ++i) arcs[i] = i;
|
||||
return {type: "MultiLineString", arcs: stitch(topology, arcs)};
|
||||
}
|
||||
|
||||
function extractArcs(topology, object, filter) {
|
||||
var arcs = [],
|
||||
geomsByArc = [],
|
||||
geom;
|
||||
|
||||
function extract0(i) {
|
||||
var j = i < 0 ? ~i : i;
|
||||
(geomsByArc[j] || (geomsByArc[j] = [])).push({i: i, g: geom});
|
||||
}
|
||||
|
||||
function extract1(arcs) {
|
||||
arcs.forEach(extract0);
|
||||
}
|
||||
|
||||
function extract2(arcs) {
|
||||
arcs.forEach(extract1);
|
||||
}
|
||||
|
||||
function extract3(arcs) {
|
||||
arcs.forEach(extract2);
|
||||
}
|
||||
|
||||
function geometry(o) {
|
||||
switch (geom = o, o.type) {
|
||||
case "GeometryCollection": o.geometries.forEach(geometry); break;
|
||||
case "LineString": extract1(o.arcs); break;
|
||||
case "MultiLineString": case "Polygon": extract2(o.arcs); break;
|
||||
case "MultiPolygon": extract3(o.arcs); break;
|
||||
}
|
||||
}
|
||||
|
||||
geometry(object);
|
||||
|
||||
geomsByArc.forEach(filter == null
|
||||
? function(geoms) { arcs.push(geoms[0].i); }
|
||||
: function(geoms) { if (filter(geoms[0].g, geoms[geoms.length - 1].g)) arcs.push(geoms[0].i); });
|
||||
|
||||
return arcs;
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import bisect from "./bisect.js";
|
||||
|
||||
export default function(objects) {
|
||||
var indexesByArc = {}, // arc index -> array of object indexes
|
||||
neighbors = objects.map(function() { return []; });
|
||||
|
||||
function line(arcs, i) {
|
||||
arcs.forEach(function(a) {
|
||||
if (a < 0) a = ~a;
|
||||
var o = indexesByArc[a];
|
||||
if (o) o.push(i);
|
||||
else indexesByArc[a] = [i];
|
||||
});
|
||||
}
|
||||
|
||||
function polygon(arcs, i) {
|
||||
arcs.forEach(function(arc) { line(arc, i); });
|
||||
}
|
||||
|
||||
function geometry(o, i) {
|
||||
if (o.type === "GeometryCollection") o.geometries.forEach(function(o) { geometry(o, i); });
|
||||
else if (o.type in geometryType) geometryType[o.type](o.arcs, i);
|
||||
}
|
||||
|
||||
var geometryType = {
|
||||
LineString: line,
|
||||
MultiLineString: polygon,
|
||||
Polygon: polygon,
|
||||
MultiPolygon: function(arcs, i) { arcs.forEach(function(arc) { polygon(arc, i); }); }
|
||||
};
|
||||
|
||||
objects.forEach(geometry);
|
||||
|
||||
for (var i in indexesByArc) {
|
||||
for (var indexes = indexesByArc[i], m = indexes.length, j = 0; j < m; ++j) {
|
||||
for (var k = j + 1; k < m; ++k) {
|
||||
var ij = indexes[j], ik = indexes[k], n;
|
||||
if ((n = neighbors[ij])[i = bisect(n, ik)] !== ik) n.splice(i, 0, ik);
|
||||
if ((n = neighbors[ik])[i = bisect(n, ij)] !== ij) n.splice(i, 0, ij);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return neighbors;
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import bbox from "./bbox.js";
|
||||
import untransform from "./untransform.js";
|
||||
|
||||
export default function(topology, transform) {
|
||||
if (topology.transform) throw new Error("already quantized");
|
||||
|
||||
if (!transform || !transform.scale) {
|
||||
if (!((n = Math.floor(transform)) >= 2)) throw new Error("n must be ≥2");
|
||||
box = topology.bbox || bbox(topology);
|
||||
var x0 = box[0], y0 = box[1], x1 = box[2], y1 = box[3], n;
|
||||
transform = {scale: [x1 - x0 ? (x1 - x0) / (n - 1) : 1, y1 - y0 ? (y1 - y0) / (n - 1) : 1], translate: [x0, y0]};
|
||||
} else {
|
||||
box = topology.bbox;
|
||||
}
|
||||
|
||||
var t = untransform(transform), box, key, inputs = topology.objects, outputs = {};
|
||||
|
||||
function quantizePoint(point) {
|
||||
return t(point);
|
||||
}
|
||||
|
||||
function quantizeGeometry(input) {
|
||||
var output;
|
||||
switch (input.type) {
|
||||
case "GeometryCollection": output = {type: "GeometryCollection", geometries: input.geometries.map(quantizeGeometry)}; break;
|
||||
case "Point": output = {type: "Point", coordinates: quantizePoint(input.coordinates)}; break;
|
||||
case "MultiPoint": output = {type: "MultiPoint", coordinates: input.coordinates.map(quantizePoint)}; break;
|
||||
default: return input;
|
||||
}
|
||||
if (input.id != null) output.id = input.id;
|
||||
if (input.bbox != null) output.bbox = input.bbox;
|
||||
if (input.properties != null) output.properties = input.properties;
|
||||
return output;
|
||||
}
|
||||
|
||||
function quantizeArc(input) {
|
||||
var i = 0, j = 1, n = input.length, p, output = new Array(n); // pessimistic
|
||||
output[0] = t(input[0], 0);
|
||||
while (++i < n) if ((p = t(input[i], i))[0] || p[1]) output[j++] = p; // non-coincident points
|
||||
if (j === 1) output[j++] = [0, 0]; // an arc must have at least two points
|
||||
output.length = j;
|
||||
return output;
|
||||
}
|
||||
|
||||
for (key in inputs) outputs[key] = quantizeGeometry(inputs[key]);
|
||||
|
||||
return {
|
||||
type: "Topology",
|
||||
bbox: box,
|
||||
transform: transform,
|
||||
objects: outputs,
|
||||
arcs: topology.arcs.map(quantizeArc)
|
||||
};
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export default function(array, n) {
|
||||
var t, j = array.length, i = j - n;
|
||||
while (i < --j) t = array[i], array[i++] = array[j], array[j] = t;
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
export default function(topology, arcs) {
|
||||
var stitchedArcs = {},
|
||||
fragmentByStart = {},
|
||||
fragmentByEnd = {},
|
||||
fragments = [],
|
||||
emptyIndex = -1;
|
||||
|
||||
// Stitch empty arcs first, since they may be subsumed by other arcs.
|
||||
arcs.forEach(function(i, j) {
|
||||
var arc = topology.arcs[i < 0 ? ~i : i], t;
|
||||
if (arc.length < 3 && !arc[1][0] && !arc[1][1]) {
|
||||
t = arcs[++emptyIndex], arcs[emptyIndex] = i, arcs[j] = t;
|
||||
}
|
||||
});
|
||||
|
||||
arcs.forEach(function(i) {
|
||||
var e = ends(i),
|
||||
start = e[0],
|
||||
end = e[1],
|
||||
f, g;
|
||||
|
||||
if (f = fragmentByEnd[start]) {
|
||||
delete fragmentByEnd[f.end];
|
||||
f.push(i);
|
||||
f.end = end;
|
||||
if (g = fragmentByStart[end]) {
|
||||
delete fragmentByStart[g.start];
|
||||
var fg = g === f ? f : f.concat(g);
|
||||
fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.end] = fg;
|
||||
} else {
|
||||
fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
|
||||
}
|
||||
} else if (f = fragmentByStart[end]) {
|
||||
delete fragmentByStart[f.start];
|
||||
f.unshift(i);
|
||||
f.start = start;
|
||||
if (g = fragmentByEnd[start]) {
|
||||
delete fragmentByEnd[g.end];
|
||||
var gf = g === f ? f : g.concat(f);
|
||||
fragmentByStart[gf.start = g.start] = fragmentByEnd[gf.end = f.end] = gf;
|
||||
} else {
|
||||
fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
|
||||
}
|
||||
} else {
|
||||
f = [i];
|
||||
fragmentByStart[f.start = start] = fragmentByEnd[f.end = end] = f;
|
||||
}
|
||||
});
|
||||
|
||||
function ends(i) {
|
||||
var arc = topology.arcs[i < 0 ? ~i : i], p0 = arc[0], p1;
|
||||
if (topology.transform) p1 = [0, 0], arc.forEach(function(dp) { p1[0] += dp[0], p1[1] += dp[1]; });
|
||||
else p1 = arc[arc.length - 1];
|
||||
return i < 0 ? [p1, p0] : [p0, p1];
|
||||
}
|
||||
|
||||
function flush(fragmentByEnd, fragmentByStart) {
|
||||
for (var k in fragmentByEnd) {
|
||||
var f = fragmentByEnd[k];
|
||||
delete fragmentByStart[f.start];
|
||||
delete f.start;
|
||||
delete f.end;
|
||||
f.forEach(function(i) { stitchedArcs[i < 0 ? ~i : i] = 1; });
|
||||
fragments.push(f);
|
||||
}
|
||||
}
|
||||
|
||||
flush(fragmentByEnd, fragmentByStart);
|
||||
flush(fragmentByStart, fragmentByEnd);
|
||||
arcs.forEach(function(i) { if (!stitchedArcs[i < 0 ? ~i : i]) fragments.push([i]); });
|
||||
|
||||
return fragments;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import identity from "./identity.js";
|
||||
|
||||
export default function(transform) {
|
||||
if (transform == null) return identity;
|
||||
var x0,
|
||||
y0,
|
||||
kx = transform.scale[0],
|
||||
ky = transform.scale[1],
|
||||
dx = transform.translate[0],
|
||||
dy = transform.translate[1];
|
||||
return function(input, i) {
|
||||
if (!i) x0 = y0 = 0;
|
||||
var j = 2, n = input.length, output = new Array(n);
|
||||
output[0] = (x0 += input[0]) * kx + dx;
|
||||
output[1] = (y0 += input[1]) * ky + dy;
|
||||
while (j < n) output[j] = input[j], ++j;
|
||||
return output;
|
||||
};
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import identity from "./identity.js";
|
||||
|
||||
export default function(transform) {
|
||||
if (transform == null) return identity;
|
||||
var x0,
|
||||
y0,
|
||||
kx = transform.scale[0],
|
||||
ky = transform.scale[1],
|
||||
dx = transform.translate[0],
|
||||
dy = transform.translate[1];
|
||||
return function(input, i) {
|
||||
if (!i) x0 = y0 = 0;
|
||||
var j = 2,
|
||||
n = input.length,
|
||||
output = new Array(n),
|
||||
x1 = Math.round((input[0] - dx) / kx),
|
||||
y1 = Math.round((input[1] - dy) / ky);
|
||||
output[0] = x1 - x0, x0 = x1;
|
||||
output[1] = y1 - y0, y0 = y1;
|
||||
while (j < n) output[j] = input[j], ++j;
|
||||
return output;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user