kriging-contour
Version:
基于克里金插值算法,根据离散点位置及其权重,生成等值面矢量数据(GeoJSON格式)和栅格数据(Canvas绘制图片),这些数据在任何WebGIS客户端上都可通用展示。
964 lines (874 loc) • 27.3 kB
JavaScript
// kriging-contour v1.0.4 Copyright 2020 freegis
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.kriging = global.kriging || {}));
}(this, (function (exports) { 'use strict';
function ascending(a, b) {
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}
function bisector(compare) {
if (compare.length === 1) compare = ascendingComparator(compare);
return {
left: function(a, x, lo, hi) {
if (lo == null) lo = 0;
if (hi == null) hi = a.length;
while (lo < hi) {
var mid = lo + hi >>> 1;
if (compare(a[mid], x) < 0) lo = mid + 1;
else hi = mid;
}
return lo;
},
right: function(a, x, lo, hi) {
if (lo == null) lo = 0;
if (hi == null) hi = a.length;
while (lo < hi) {
var mid = lo + hi >>> 1;
if (compare(a[mid], x) > 0) hi = mid;
else lo = mid + 1;
}
return lo;
}
};
}
function ascendingComparator(f) {
return function(d, x) {
return ascending(f(d), x);
};
}
var ascendingBisect = bisector(ascending);
function extent(values, valueof) {
var n = values.length,
i = -1,
value,
min,
max;
if (valueof == null) {
while (++i < n) { // Find the first comparable value.
if ((value = values[i]) != null && value >= value) {
min = max = value;
while (++i < n) { // Compare the remaining values.
if ((value = values[i]) != null) {
if (min > value) min = value;
if (max < value) max = value;
}
}
}
}
}
else {
while (++i < n) { // Find the first comparable value.
if ((value = valueof(values[i], i, values)) != null && value >= value) {
min = max = value;
while (++i < n) { // Compare the remaining values.
if ((value = valueof(values[i], i, values)) != null) {
if (min > value) min = value;
if (max < value) max = value;
}
}
}
}
}
return [min, max];
}
function range(start, stop, step) {
start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
var i = -1,
n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
range = new Array(n);
while (++i < n) {
range[i] = start + i * step;
}
return range;
}
var e10 = Math.sqrt(50),
e5 = Math.sqrt(10),
e2 = Math.sqrt(2);
function tickStep(start, stop, count) {
var step0 = Math.abs(stop - start) / Math.max(0, count),
step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
error = step0 / step1;
if (error >= e10) step1 *= 10;
else if (error >= e5) step1 *= 5;
else if (error >= e2) step1 *= 2;
return stop < start ? -step1 : step1;
}
function thresholdSturges(values) {
return Math.ceil(Math.log(values.length) / Math.LN2) + 1;
}
var array = Array.prototype;
var slice = array.slice;
function ascending$1(a, b) {
return a - b;
}
function area(ring) {
var i = 0, n = ring.length, area = ring[n - 1][1] * ring[0][0] - ring[n - 1][0] * ring[0][1];
while (++i < n) area += ring[i - 1][1] * ring[i][0] - ring[i - 1][0] * ring[i][1];
return area;
}
function constant(x) {
return function() {
return x;
};
}
function contains(ring, hole) {
var i = -1, n = hole.length, c;
while (++i < n) if (c = ringContains(ring, hole[i])) return c;
return 0;
}
function ringContains(ring, point) {
var x = point[0], y = point[1], contains = -1;
for (var i = 0, n = ring.length, j = n - 1; i < n; j = i++) {
var pi = ring[i], xi = pi[0], yi = pi[1], pj = ring[j], xj = pj[0], yj = pj[1];
if (segmentContains(pi, pj, point)) return 0;
if (((yi > y) !== (yj > y)) && ((x < (xj - xi) * (y - yi) / (yj - yi) + xi))) contains = -contains;
}
return contains;
}
function segmentContains(a, b, c) {
var i; return collinear(a, b, c) && within(a[i = +(a[0] === b[0])], c[i], b[i]);
}
function collinear(a, b, c) {
return (b[0] - a[0]) * (c[1] - a[1]) === (c[0] - a[0]) * (b[1] - a[1]);
}
function within(p, q, r) {
return p <= q && q <= r || r <= q && q <= p;
}
function noop() {}
var cases = [
[],
[[[1.0, 1.5], [0.5, 1.0]]],
[[[1.5, 1.0], [1.0, 1.5]]],
[[[1.5, 1.0], [0.5, 1.0]]],
[[[1.0, 0.5], [1.5, 1.0]]],
[[[1.0, 1.5], [0.5, 1.0]], [[1.0, 0.5], [1.5, 1.0]]],
[[[1.0, 0.5], [1.0, 1.5]]],
[[[1.0, 0.5], [0.5, 1.0]]],
[[[0.5, 1.0], [1.0, 0.5]]],
[[[1.0, 1.5], [1.0, 0.5]]],
[[[0.5, 1.0], [1.0, 0.5]], [[1.5, 1.0], [1.0, 1.5]]],
[[[1.5, 1.0], [1.0, 0.5]]],
[[[0.5, 1.0], [1.5, 1.0]]],
[[[1.0, 1.5], [1.5, 1.0]]],
[[[0.5, 1.0], [1.0, 1.5]]],
[]
];
function d3_contours() {
var dx = 1,
dy = 1,
threshold = thresholdSturges,
smooth = smoothLinear;
function contours(values) {
var tz = threshold(values);
// Convert number of thresholds into uniform thresholds.
if (!Array.isArray(tz)) {
var domain = extent(values), start = domain[0], stop = domain[1];
tz = tickStep(start, stop, tz);
tz = range(Math.floor(start / tz) * tz, Math.floor(stop / tz) * tz, tz);
} else {
tz = tz.slice().sort(ascending$1);
}
return tz.map(function(value) {
return contour(values, value);
});
}
// Accumulate, smooth contour rings, assign holes to exterior rings.
// Based on https://github.com/mbostock/shapefile/blob/v0.6.2/shp/polygon.js
function contour(values, value) {
var polygons = [],
holes = [];
isorings(values, value, function(ring) {
smooth(ring, values, value);
if (area(ring) > 0) polygons.push([ring]);
else holes.push(ring);
});
holes.forEach(function(hole) {
for (var i = 0, n = polygons.length, polygon; i < n; ++i) {
if (contains((polygon = polygons[i])[0], hole) !== -1) {
polygon.push(hole);
return;
}
}
});
return {
type: "MultiPolygon",
value: value,
coordinates: polygons
};
}
// Marching squares with isolines stitched into rings.
// Based on https://github.com/topojson/topojson-client/blob/v3.0.0/src/stitch.js
function isorings(values, value, callback) {
var fragmentByStart = new Array,
fragmentByEnd = new Array,
x, y, t0, t1, t2, t3;
// Special case for the first row (y = -1, t2 = t3 = 0).
x = y = -1;
t1 = values[0] >= value;
cases[t1 << 1].forEach(stitch);
while (++x < dx - 1) {
t0 = t1, t1 = values[x + 1] >= value;
cases[t0 | t1 << 1].forEach(stitch);
}
cases[t1 << 0].forEach(stitch);
// General case for the intermediate rows.
while (++y < dy - 1) {
x = -1;
t1 = values[y * dx + dx] >= value;
t2 = values[y * dx] >= value;
cases[t1 << 1 | t2 << 2].forEach(stitch);
while (++x < dx - 1) {
t0 = t1, t1 = values[y * dx + dx + x + 1] >= value;
t3 = t2, t2 = values[y * dx + x + 1] >= value;
cases[t0 | t1 << 1 | t2 << 2 | t3 << 3].forEach(stitch);
}
cases[t1 | t2 << 3].forEach(stitch);
}
// Special case for the last row (y = dy - 1, t0 = t1 = 0).
x = -1;
t2 = values[y * dx] >= value;
cases[t2 << 2].forEach(stitch);
while (++x < dx - 1) {
t3 = t2, t2 = values[y * dx + x + 1] >= value;
cases[t2 << 2 | t3 << 3].forEach(stitch);
}
cases[t2 << 3].forEach(stitch);
function stitch(line) {
var start = [line[0][0] + x, line[0][1] + y],
end = [line[1][0] + x, line[1][1] + y],
startIndex = index(start),
endIndex = index(end),
f, g;
if (f = fragmentByEnd[startIndex]) {
if (g = fragmentByStart[endIndex]) {
delete fragmentByEnd[f.end];
delete fragmentByStart[g.start];
if (f === g) {
f.ring.push(end);
callback(f.ring);
} else {
fragmentByStart[f.start] = fragmentByEnd[g.end] = {start: f.start, end: g.end, ring: f.ring.concat(g.ring)};
}
} else {
delete fragmentByEnd[f.end];
f.ring.push(end);
fragmentByEnd[f.end = endIndex] = f;
}
} else if (f = fragmentByStart[endIndex]) {
if (g = fragmentByEnd[startIndex]) {
delete fragmentByStart[f.start];
delete fragmentByEnd[g.end];
if (f === g) {
f.ring.push(end);
callback(f.ring);
} else {
fragmentByStart[g.start] = fragmentByEnd[f.end] = {start: g.start, end: f.end, ring: g.ring.concat(f.ring)};
}
} else {
delete fragmentByStart[f.start];
f.ring.unshift(start);
fragmentByStart[f.start = startIndex] = f;
}
} else {
fragmentByStart[startIndex] = fragmentByEnd[endIndex] = {start: startIndex, end: endIndex, ring: [start, end]};
}
}
}
function index(point) {
return point[0] * 2 + point[1] * (dx + 1) * 4;
}
function smoothLinear(ring, values, value) {
ring.forEach(function(point) {
var x = point[0],
y = point[1],
xt = x | 0,
yt = y | 0,
v0,
v1 = values[yt * dx + xt];
if (x > 0 && x < dx && xt === x) {
v0 = values[yt * dx + xt - 1];
point[0] = x + (value - v0) / (v1 - v0) - 0.5;
}
if (y > 0 && y < dy && yt === y) {
v0 = values[(yt - 1) * dx + xt];
point[1] = y + (value - v0) / (v1 - v0) - 0.5;
}
});
}
contours.contour = contour;
contours.size = function(_) {
if (!arguments.length) return [dx, dy];
var _0 = Math.ceil(_[0]), _1 = Math.ceil(_[1]);
if (!(_0 > 0) || !(_1 > 0)) throw new Error("invalid size");
return dx = _0, dy = _1, contours;
};
contours.thresholds = function(_) {
return arguments.length ? (threshold = typeof _ === "function" ? _ : Array.isArray(_) ? constant(slice.call(_)) : constant(_), contours) : threshold;
};
contours.smooth = function(_) {
return arguments.length ? (smooth = _ ? smoothLinear : noop, contours) : smooth === smoothLinear;
};
return contours;
}
//数组最大值
Array.prototype.max = function () {
return Math.max.apply(null, this);
};
//数组最小值
Array.prototype.min = function () {
return Math.min.apply(null, this);
};
//数组平均值
Array.prototype.mean = function () {
var i,
sum;
for (i = 0, sum = 0; i < this.length; i++)
sum += this[i];
return sum / this.length;
};
//将数组第一项取出为v,生成长度为n的数组,每个数组item为v
Array.prototype.rep = function (n) {
var arrayn = new Array(n);
var v = this[0];
for (var i = 0; i < n; i++) {
arrayn[i] = v;
}
return arrayn;
};
Array.prototype.pip = function (x, y) {
var i,
j,
c = false;
for (i = 0, j = this.length - 1; i < this.length; j = i++) {
if (((this[i][1] > y) != (this[j][1] > y)) &&
(x < (this[j][0] - this[i][0]) * (y - this[i][1]) / (this[j][1] - this[i][1]) + this[i][0])) {
c = !c;
}
}
return c;
};
// Matrix algebra
function kriging_matrix_diag(c, n) {
var i,
Z = [0].rep(n * n);
for (i = 0; i < n; i++)
Z[i * n + i] = c;
return Z;
}function kriging_matrix_transpose(X, n, m) {
var i,
j,
Z = Array(m * n);
for (i = 0; i < n; i++)
for (j = 0; j < m; j++)
Z[j * n + i] = X[i * m + j];
return Z;
}function kriging_matrix_add(X, Y, n, m) {
var i,
j,
Z = Array(n * m);
for (i = 0; i < n; i++)
for (j = 0; j < m; j++)
Z[i * m + j] = X[i * m + j] + Y[i * m + j];
return Z;
}// Naive matrix multiplication
function kriging_matrix_multiply(X, Y, n, m, p) {
var i,
j,
k,
Z = Array(n * p);
for (i = 0; i < n; i++) {
for (j = 0; j < p; j++) {
Z[i * p + j] = 0;
for (k = 0; k < m; k++)
Z[i * p + j] += X[i * m + k] * Y[k * p + j];
}
}
return Z;
}// Cholesky decomposition
function kriging_matrix_chol(X, n) {
var i,
j,
k,
p = Array(n);
for (i = 0; i < n; i++)
p[i] = X[i * n + i];
for (i = 0; i < n; i++) {
for (j = 0; j < i; j++)
p[i] -= X[i * n + j] * X[i * n + j];
if (p[i] <= 0)
return false;
p[i] = Math.sqrt(p[i]);
for (j = i + 1; j < n; j++) {
for (k = 0; k < i; k++)
X[j * n + i] -= X[j * n + k] * X[i * n + k];
X[j * n + i] /= p[i];
}
}
for (i = 0; i < n; i++)
X[i * n + i] = p[i];
return true;
}// Inversion of cholesky decomposition
function kriging_matrix_chol2inv(X, n) {
var i,
j,
k,
sum;
for (i = 0; i < n; i++) {
X[i * n + i] = 1 / X[i * n + i];
for (j = i + 1; j < n; j++) {
sum = 0;
for (k = i; k < j; k++)
sum -= X[j * n + k] * X[k * n + i];
X[j * n + i] = sum / X[j * n + j];
}
}
for (i = 0; i < n; i++)
for (j = i + 1; j < n; j++)
X[i * n + j] = 0;
for (i = 0; i < n; i++) {
X[i * n + i] *= X[i * n + i];
for (k = i + 1; k < n; k++)
X[i * n + i] += X[k * n + i] * X[k * n + i];
for (j = i + 1; j < n; j++)
for (k = j; k < n; k++)
X[i * n + j] += X[k * n + i] * X[k * n + j];
}
for (i = 0; i < n; i++)
for (j = 0; j < i; j++)
X[i * n + j] = X[j * n + i];
}// Inversion via gauss-jordan elimination
function kriging_matrix_solve(X, n) {
var m = n;
var b = Array(n * n);
var indxc = Array(n);
var indxr = Array(n);
var ipiv = Array(n);
var i,
icol,
irow,
j,
k,
l,
ll;
var big,
dum,
pivinv,
temp;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++) {
if (i == j)
b[i * n + j] = 1;
else
b[i * n + j] = 0;
}
for (j = 0; j < n; j++)
ipiv[j] = 0;
for (i = 0; i < n; i++) {
big = 0;
for (j = 0; j < n; j++) {
if (ipiv[j] != 1) {
for (k = 0; k < n; k++) {
if (ipiv[k] == 0) {
if (Math.abs(X[j * n + k]) >= big) {
big = Math.abs(X[j * n + k]);
irow = j;
icol = k;
}
}
}
}
}
++(ipiv[icol]);
if (irow != icol) {
for (l = 0; l < n; l++) {
temp = X[irow * n + l];
X[irow * n + l] = X[icol * n + l];
X[icol * n + l] = temp;
}
for (l = 0; l < m; l++) {
temp = b[irow * n + l];
b[irow * n + l] = b[icol * n + l];
b[icol * n + l] = temp;
}
}
indxr[i] = irow;
indxc[i] = icol;
if (X[icol * n + icol] == 0)
return false; // Singular
pivinv = 1 / X[icol * n + icol];
X[icol * n + icol] = 1;
for (l = 0; l < n; l++)
X[icol * n + l] *= pivinv;
for (l = 0; l < m; l++)
b[icol * n + l] *= pivinv;
for (ll = 0; ll < n; ll++) {
if (ll != icol) {
dum = X[ll * n + icol];
X[ll * n + icol] = 0;
for (l = 0; l < n; l++)
X[ll * n + l] -= X[icol * n + l] * dum;
for (l = 0; l < m; l++)
b[ll * n + l] -= b[icol * n + l] * dum;
}
}
}
for (l = (n - 1); l >= 0; l--)
if (indxr[l] != indxc[l]) {
for (k = 0; k < n; k++) {
temp = X[k * n + indxr[l]];
X[k * n + indxr[l]] = X[k * n + indxc[l]];
X[k * n + indxc[l]] = temp;
}
}
return true;
}
// Variogram models
function kriging_variogram_gaussian(h, nugget, range, sill, A) {
return nugget + ((sill - nugget) / range) *
(1.0 - Math.exp( - (1.0 / A) * Math.pow(h / range, 2)));
}function kriging_variogram_exponential(h, nugget, range, sill, A) {
return nugget + ((sill - nugget) / range) *
(1.0 - Math.exp( - (1.0 / A) * (h / range)));
}function kriging_variogram_spherical(h, nugget, range, sill, A) {
if (h > range)
return nugget + (sill - nugget) / range;
return nugget + ((sill - nugget) / range) *
(1.5 * (h / range) - 0.5 * Math.pow(h / range, 3));
}
var kriging = {
};
// Train using gaussian processes with bayesian priors
kriging.train = function (t, x, y, model, sigma2, alpha) {
var variogram = {
t : t,
x : x,
y : y,
nugget : 0.0,
range : 0.0,
sill : 0.0,
A : 1 / 3,
n : 0
};
switch (model) {
case "gaussian":
variogram.model = kriging_variogram_gaussian;
break;
case "exponential":
variogram.model = kriging_variogram_exponential;
break;
case "spherical":
variogram.model = kriging_variogram_spherical;
break;
}
// Lag distance/semivariance
var i,
j,
k,
l,
n = t.length;
var distance = Array((n * n - n) / 2);
for (i = 0, k = 0; i < n; i++)
for (j = 0; j < i; j++, k++) {
distance[k] = Array(2);
distance[k][0] = Math.pow(
Math.pow(x[i] - x[j], 2) +
Math.pow(y[i] - y[j], 2), 0.5);
distance[k][1] = Math.abs(t[i] - t[j]);
}
distance.sort(function (a, b) {
return a[0] - b[0];
});
variogram.range = distance[(n * n - n) / 2 - 1][0];
// Bin lag distance
var lags = ((n * n - n) / 2) > 30 ? 30 : (n * n - n) / 2;
var tolerance = variogram.range / lags;
var lag = [0].rep(lags);
var semi = [0].rep(lags);
if (lags < 30) {
for (l = 0; l < lags; l++) {
lag[l] = distance[l][0];
semi[l] = distance[l][1];
}
} else {
for (i = 0, j = 0, k = 0, l = 0; i < lags && j < ((n * n - n) / 2); i++, k = 0) {
while (distance[j][0] <= ((i + 1) * tolerance)) {
lag[l] += distance[j][0];
semi[l] += distance[j][1];
j++;
k++;
if (j >= ((n * n - n) / 2))
break;
}
if (k > 0) {
lag[l] /= k;
semi[l] /= k;
l++;
}
}
if (l < 2)
return variogram; // Error: Not enough points
}
// Feature transformation
n = l;
variogram.range = lag[n - 1] - lag[0];
var X = [1].rep(2 * n);
var Y = Array(n);
var A = variogram.A;
for (i = 0; i < n; i++) {
switch (model) {
case "gaussian":
X[i * 2 + 1] = 1.0 - Math.exp( - (1.0 / A) * Math.pow(lag[i] / variogram.range, 2));
break;
case "exponential":
X[i * 2 + 1] = 1.0 - Math.exp( - (1.0 / A) * lag[i] / variogram.range);
break;
case "spherical":
X[i * 2 + 1] = 1.5 * (lag[i] / variogram.range) -
0.5 * Math.pow(lag[i] / variogram.range, 3);
break;
} Y[i] = semi[i];
}
// Least squares
var Xt = kriging_matrix_transpose(X, n, 2);
var Z = kriging_matrix_multiply(Xt, X, 2, n, 2);
Z = kriging_matrix_add(Z, kriging_matrix_diag(1 / alpha, 2), 2, 2);
var cloneZ = Z.slice(0);
if (kriging_matrix_chol(Z, 2))
kriging_matrix_chol2inv(Z, 2);
else {
kriging_matrix_solve(cloneZ, 2);
Z = cloneZ;
}
var W = kriging_matrix_multiply(kriging_matrix_multiply(Z, Xt, 2, 2, n), Y, 2, n, 1);
// Variogram parameters
variogram.nugget = W[0];
variogram.sill = W[1] * variogram.range + variogram.nugget;
variogram.n = x.length;
// Gram matrix with prior
n = x.length;
var K = Array(n * n);
for (i = 0; i < n; i++) {
for (j = 0; j < i; j++) {
K[i * n + j] = variogram.model(Math.pow(Math.pow(x[i] - x[j], 2) +
Math.pow(y[i] - y[j], 2), 0.5),
variogram.nugget,
variogram.range,
variogram.sill,
variogram.A);
K[j * n + i] = K[i * n + j];
}
K[i * n + i] = variogram.model(0, variogram.nugget,
variogram.range,
variogram.sill,
variogram.A);
}
// Inverse penalized Gram matrix projected to target vector
var C = kriging_matrix_add(K, kriging_matrix_diag(sigma2, n), n, n);
var cloneC = C.slice(0);
if (kriging_matrix_chol(C, n))
kriging_matrix_chol2inv(C, n);
else {
kriging_matrix_solve(cloneC, n);
C = cloneC;
}
// Copy unprojected inverted matrix as K
var K = C.slice(0);
var M = kriging_matrix_multiply(C, t, n, n, 1);
variogram.K = K;
variogram.M = M;
return variogram;
};
// Model prediction
kriging.predict = function (x, y, variogram) {
var i,
k = Array(variogram.n);
for (i = 0; i < variogram.n; i++)
k[i] = variogram.model(Math.pow(Math.pow(x - variogram.x[i], 2) +
Math.pow(y - variogram.y[i], 2), 0.5),
variogram.nugget, variogram.range,
variogram.sill, variogram.A);
return kriging_matrix_multiply(k, variogram.M, 1, variogram.n, 1)[0];
};
kriging.variance = function (x, y, variogram) {
var i,
k = Array(variogram.n);
for (i = 0; i < variogram.n; i++)
k[i] = variogram.model(Math.pow(Math.pow(x - variogram.x[i], 2) +
Math.pow(y - variogram.y[i], 2), 0.5),
variogram.nugget, variogram.range,
variogram.sill, variogram.A);
return variogram.model(0, variogram.nugget, variogram.range,
variogram.sill, variogram.A) +
kriging_matrix_multiply(kriging_matrix_multiply(k, variogram.K,
1, variogram.n, variogram.n),
k, 1, variogram.n, 1)[0];
};
// 生成克里金grid
kriging.getGridInfo = function (bbox,variogram,width) {
var grid = [];
//x方向
var xlim=[bbox[0],bbox[2]];
var ylim=[bbox[1],bbox[3]];
var zlim=[variogram.t.min(), variogram.t.max()];
//xy方向地理跨度
var geoX_width=xlim[1]-xlim[0];
var geoY_width=ylim[1]-ylim[0];
//如果x_width设置,初始基于200计算。
let x_width,y_width;
if(!width)
x_width=200;
else
x_width=Math.ceil(width);
//让图像的xy比例与地理的xy比例保持一致
y_width=Math.ceil(x_width/(geoX_width/geoY_width));
//地理跨度/图像跨度=当前地图图上分辨率
var x_resolution=geoX_width*1.0/x_width;
var y_resolution=geoY_width*1.0/y_width;
var xtarget,ytarget;
for (let j = 0; j < y_width; j++) {
for (let k =0; k <x_width; k++) {
xtarget = bbox[0] + k * x_resolution;
ytarget = bbox[1] + j * y_resolution;
grid.push(kriging.predict(xtarget, ytarget, variogram));
}
}
return {
grid : grid,
n : x_width,
m : y_width,
xlim : xlim,
ylim : ylim,
zlim : zlim,
x_resolution:x_resolution,
y_resolution:y_resolution
};
};
//克里金生成矢量等值面
kriging.getVectorContour = function (gridInfo, breaks) {
//像素坐标系的等值面
var _contours = d3_contours()
.size([gridInfo.n, gridInfo.m])
.thresholds(breaks)
(gridInfo.grid);
//像素坐标系换算地理坐标系
let dataset = {
"type" : "FeatureCollection",
"features" : []
};
var geoX_width=gridInfo.xlim[1]-gridInfo.xlim[0];
var geoY_width=gridInfo.ylim[1]-gridInfo.ylim[0];
_contours.forEach(contour => {
contour.coordinates.forEach(polygon => {
//polygon分内环和外环
let _polygon = polygon.map(ring => {
let _ring = ring.map(function (coor) {
//像素坐标转地理坐标
let lon = gridInfo.xlim[0] + geoX_width * (coor[0]*1.0 / gridInfo.n);
let lat = gridInfo.ylim[0] + geoY_width * (coor[1]*1.0 / gridInfo.m);
return [lon,lat];
});
return _ring;
});
dataset.features.push({
"type" : "Feature",
"properties" : {
"contour_value" : contour.value
},
"geometry" : {
"type" : "Polygon",
"coordinates" : _polygon
}
});
});
});
return dataset;
};
//克里金生成canvas图像
kriging.drawCanvasContour = function(gridInfo,canvas,xlim,ylim,colors) {
//清空画布
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
//开始边界
var range = [xlim[1]-xlim[0], ylim[1]-ylim[0], gridInfo.zlim[1]-gridInfo.zlim[0]];
var n = gridInfo.n;
var m = gridInfo.m;
//根据分辨率,计算每个色块的宽高
var wx = Math.ceil(gridInfo.x_resolution*canvas.width/(xlim[1]-xlim[0]));
var wy = Math.ceil(gridInfo.y_resolution*canvas.height/(ylim[1]-ylim[0]));
for(let i=0;i<m;i++)
for(let j=0;j<n;j++) {
let _index=i*n+j;
if(gridInfo.grid[_index]==undefined)
continue;
let x = canvas.width*(j*gridInfo.x_resolution+gridInfo.xlim[0]-xlim[0])/range[0];
let y = canvas.height*(1-(i*gridInfo.y_resolution+gridInfo.ylim[0]-ylim[0])/range[1]);
let z = (gridInfo.grid[_index]-gridInfo.zlim[0])/range[2];
if(z<0.0)
z = 0.0;
else if(z>1.0)
z = 1.0;
ctx.fillStyle = colors[Math.floor((colors.length-1)*z)];
ctx.fillRect(Math.round(x-wx/2), Math.round(y-wy/2), wx, wy);
}
};
function _getKrigingGridInfo(featureCollection,weight,krigingParams){
//先获取featureCollection的bbox
let values=[],lons=[],lats=[];
let extent=[100000000,100000000,-100000000,-100000000];
featureCollection.features.forEach(feature => {
//提取插值权重字段,准备克里金插值使用
values.push(feature.properties[weight]);
lons.push(feature.geometry.coordinates[0]);
lats.push(feature.geometry.coordinates[1]);
if(extent[0]>feature.geometry.coordinates[0])
extent[0]=feature.geometry.coordinates[0];
if(extent[1]>feature.geometry.coordinates[1])
extent[1]=feature.geometry.coordinates[1];
if(extent[2]<feature.geometry.coordinates[0])
extent[2]=feature.geometry.coordinates[0];
if(extent[3]<feature.geometry.coordinates[1])
extent[3]=feature.geometry.coordinates[1];
});
let variogram=kriging.train(values,lons,lats,krigingParams.model,krigingParams.sigma2,krigingParams.alpha);
let gridinfo=kriging.getGridInfo(extent,variogram,200);
return gridinfo;
}
/*
* 克里金生成矢量等值面,浏览器和node都可以使用
* @param {json} featureCollection:必填,已有点数据,geojson格式
* @param {string} weight:必填,插值所依赖的圈中字段
* @param {object) krigingParams:必填,克里金插值算法参数设置
krigingParams:{
krigingModel:'exponential','gaussian','spherical',三选一
krigingSigma2:
krigingAlpha:
}
* @param {array} breaks:必填,等值面分级区间
*/
function getVectorContour(featureCollection,weight,krigingParams,breaks){
let gridinfo=_getKrigingGridInfo(featureCollection,weight,krigingParams);
let vectorContour=kriging.getVectorContour(gridinfo,breaks);
return vectorContour;
}
/*
* 克里金生成栅格等值面并绘制到canvas上,仅浏览器中使用
* @param {json} featureCollection:必填,已有点数据,geojson格式
* @param {string} weight:必填,插值所依赖的圈中字段
* @param {object) krigingParams:必填,克里金插值算法参数设置
krigingParams:{
krigingModel:'exponential','gaussian','spherical',三选一
krigingSigma2:
krigingAlpha:
}
* @param {dom) canvas:必填,绑定渲染的canvas dom
* @param {array) colors:必填,等值面分级区间
*/
function drawCanvasContour(featureCollection,weight,krigingParams,canvas,xlim,ylim,colors) {
let gridinfo=_getKrigingGridInfo(featureCollection,weight,krigingParams);
kriging.drawCanvasContour(gridinfo,canvas,xlim,ylim,colors);
}
exports.drawCanvasContour = drawCanvasContour;
exports.getVectorContour = getVectorContour;
Object.defineProperty(exports, '__esModule', { value: true });
})));