Merge pull request #11215 from ahocevar/flat-multipolygon
Fix MVT multipolygons with featureClass: Feature
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
@@ -0,0 +1,47 @@
|
|||||||
|
import {Feature, Map, View} from '../../../src/ol/index.js';
|
||||||
|
import {GeoJSON, MVT} from '../../../src/ol/format.js';
|
||||||
|
import {VectorTile as VectorTileLayer} from '../../../src/ol/layer.js';
|
||||||
|
import {VectorTile as VectorTileSource} from '../../../src/ol/source.js';
|
||||||
|
import {fromLonLat} from '../../../src/ol/proj.js';
|
||||||
|
|
||||||
|
const center = fromLonLat([0.26, 24.08]);
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
layers: [
|
||||||
|
new VectorTileLayer({
|
||||||
|
source: new VectorTileSource({
|
||||||
|
format: new MVT(),
|
||||||
|
url: '/data/{z}-{x}-{y}.mvt',
|
||||||
|
minZoom: 7,
|
||||||
|
maxZoom: 7,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
new VectorTileLayer({
|
||||||
|
source: new VectorTileSource({
|
||||||
|
format: new MVT({
|
||||||
|
featureClass: Feature,
|
||||||
|
}),
|
||||||
|
url: '/data/{z}-{x}-{y}.mvt',
|
||||||
|
minZoom: 7,
|
||||||
|
maxZoom: 7,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
new VectorTileLayer({
|
||||||
|
source: new VectorTileSource({
|
||||||
|
format: new GeoJSON(),
|
||||||
|
url: '/data/{z}-{x}-{y}.geojson',
|
||||||
|
minZoom: 7,
|
||||||
|
maxZoom: 7,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
target: 'map',
|
||||||
|
view: new View({
|
||||||
|
center: center,
|
||||||
|
zoom: 10,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
map.getTargetElement().style.background = 'gray';
|
||||||
|
|
||||||
|
render();
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"name": "7-64-55",
|
||||||
|
"features": [
|
||||||
|
{ "type": "Feature", "properties": { "mvt_id": 1 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 0.11810302734375, 24.046463999666589 ], [ 0.237579345703125, 24.046463999666589 ], [ 0.237579345703125, 23.956136333969283 ], [ 0.11810302734375, 23.956136333969283 ], [ 0.11810302734375, 24.046463999666589 ] ], [ [ 0.153121948242188, 24.01949779624486 ], [ 0.153121948242188, 23.979978958263413 ], [ 0.2032470703125, 23.979978958263413 ], [ 0.2032470703125, 24.01949779624486 ], [ 0.153121948242188, 24.01949779624486 ] ] ] ] } },
|
||||||
|
{ "type": "Feature", "properties": { "mvt_id": 26 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 0.293197631835938, 24.036430724667376 ], [ 0.389328002929688, 24.036430724667376 ], [ 0.3570556640625, 23.95864629158493 ], [ 0.260238647460938, 23.95864629158493 ], [ 0.293197631835938, 24.036430724667376 ] ], [ [ 0.342636108398438, 24.0332951655089 ], [ 0.32684326171875, 23.988761970899695 ], [ 0.352935791015625, 23.988761970899695 ], [ 0.369415283203125, 24.0332951655089 ], [ 0.342636108398438, 24.0332951655089 ] ], [ [ 0.291824340820312, 24.018870607907278 ], [ 0.291824340820312, 23.971195346707443 ], [ 0.319290161132813, 23.971195346707443 ], [ 0.319290161132813, 24.018870607907278 ], [ 0.291824340820312, 24.018870607907278 ] ] ] ] } },
|
||||||
|
{ "type": "Feature", "properties": { "mvt_id": 30 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 0.287704467773438, 24.219414393426444 ], [ 0.350875854492188, 24.219414393426444 ], [ 0.33233642578125, 24.147380157655896 ], [ 0.268478393554688, 24.147380157655896 ], [ 0.287704467773438, 24.219414393426444 ] ] ], [ [ [ 0.3460693359375, 24.166802085303235 ], [ 0.372848510742188, 24.166802085303235 ], [ 0.383148193359375, 24.144873887414654 ], [ 0.355682373046875, 24.144873887414654 ], [ 0.3460693359375, 24.166802085303235 ] ] ], [ [ [ 0.352249145507812, 24.218161971731128 ], [ 0.377655029296875, 24.218161971731128 ], [ 0.383834838867187, 24.186847428521244 ], [ 0.358428955078125, 24.186847428521244 ], [ 0.352249145507812, 24.218161971731128 ] ] ] ] } },
|
||||||
|
{ "type": "Feature", "properties": { "mvt_id": 33 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 0.10986328125, 24.168055011483165 ], [ 0.199813842773438, 24.168055011483165 ], [ 0.199813842773438, 24.075305297879073 ], [ 0.10986328125, 24.075305297879073 ], [ 0.10986328125, 24.168055011483165 ] ] ], [ [ [ 0.202560424804687, 24.16742854993004 ], [ 0.240325927734375, 24.16742854993004 ], [ 0.240325927734375, 24.128581933124689 ], [ 0.202560424804687, 24.128581933124689 ], [ 0.202560424804687, 24.16742854993004 ] ] ] ] } },
|
||||||
|
{ "type": "Feature", "properties": { "mvt_id": 48 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 0.268478393554688, 24.136101554583817 ], [ 0.336456298828125, 24.136101554583817 ], [ 0.31585693359375, 24.047718103928766 ], [ 0.247879028320312, 24.047718103928766 ], [ 0.268478393554688, 24.136101554583817 ] ], [ [ 0.27191162109375, 24.123568606548453 ], [ 0.27191162109375, 24.07279761626851 ], [ 0.31585693359375, 24.07279761626851 ], [ 0.31585693359375, 24.123568606548453 ], [ 0.27191162109375, 24.123568606548453 ] ] ], [ [ [ 0.328216552734375, 24.077812930451806 ], [ 0.357742309570313, 24.077812930451806 ], [ 0.3680419921875, 24.05085331099432 ], [ 0.339202880859375, 24.05085331099432 ], [ 0.328216552734375, 24.077812930451806 ] ] ] ] } }
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -203,10 +203,16 @@ class MVT extends FeatureFormat {
|
|||||||
let prevEndIndex = 0;
|
let prevEndIndex = 0;
|
||||||
for (let i = 0, ii = ends.length; i < ii; ++i) {
|
for (let i = 0, ii = ends.length; i < ii; ++i) {
|
||||||
const end = ends[i];
|
const end = ends[i];
|
||||||
|
// classifies an array of rings into polygons with outer rings and holes
|
||||||
if (!linearRingIsClockwise(flatCoordinates, offset, end, 2)) {
|
if (!linearRingIsClockwise(flatCoordinates, offset, end, 2)) {
|
||||||
endss.push(ends.slice(prevEndIndex, i));
|
endss.push(ends.slice(prevEndIndex, i + 1));
|
||||||
prevEndIndex = i;
|
} else {
|
||||||
|
if (endss.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
endss[endss.length - 1].push(ends[prevEndIndex]);
|
||||||
}
|
}
|
||||||
|
prevEndIndex = i + 1;
|
||||||
offset = end;
|
offset = end;
|
||||||
}
|
}
|
||||||
if (endss.length > 1) {
|
if (endss.length > 1) {
|
||||||
|
|||||||
+65
-12
@@ -4,6 +4,9 @@
|
|||||||
import {coordinates as reverseCoordinates} from './reverse.js';
|
import {coordinates as reverseCoordinates} from './reverse.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Is the linear ring oriented clockwise in a coordinate system with a bottom-left
|
||||||
|
* coordinate origin? For a coordinate system with a top-left coordinate origin,
|
||||||
|
* the ring's orientation is clockwise when this function returns false.
|
||||||
* @param {Array<number>} flatCoordinates Flat coordinates.
|
* @param {Array<number>} flatCoordinates Flat coordinates.
|
||||||
* @param {number} offset Offset.
|
* @param {number} offset Offset.
|
||||||
* @param {number} end End.
|
* @param {number} end End.
|
||||||
@@ -11,19 +14,69 @@ import {coordinates as reverseCoordinates} from './reverse.js';
|
|||||||
* @return {boolean} Is clockwise.
|
* @return {boolean} Is clockwise.
|
||||||
*/
|
*/
|
||||||
export function linearRingIsClockwise(flatCoordinates, offset, end, stride) {
|
export function linearRingIsClockwise(flatCoordinates, offset, end, stride) {
|
||||||
// http://tinyurl.com/clockwise-method
|
// https://stackoverflow.com/a/1180256/2389327
|
||||||
// https://github.com/OSGeo/gdal/blob/trunk/gdal/ogr/ogrlinearring.cpp
|
// https://en.wikipedia.org/wiki/Curve_orientation#Orientation_of_a_simple_polygon
|
||||||
let edge = 0;
|
|
||||||
let x1 = flatCoordinates[end - stride];
|
let firstVertexRepeated = true;
|
||||||
let y1 = flatCoordinates[end - stride + 1];
|
for (let i = 0; i < stride; ++i) {
|
||||||
for (; offset < end; offset += stride) {
|
if (flatCoordinates[offset + i] !== flatCoordinates[end - stride + i]) {
|
||||||
const x2 = flatCoordinates[offset];
|
firstVertexRepeated = false;
|
||||||
const y2 = flatCoordinates[offset + 1];
|
break;
|
||||||
edge += (x2 - x1) * (y2 + y1);
|
}
|
||||||
x1 = x2;
|
|
||||||
y1 = y2;
|
|
||||||
}
|
}
|
||||||
return edge > 0;
|
if (firstVertexRepeated) {
|
||||||
|
end -= stride;
|
||||||
|
}
|
||||||
|
const iMinVertex = findCornerVertex(flatCoordinates, offset, end, stride);
|
||||||
|
// Orientation matrix:
|
||||||
|
// [ 1 xa ya ]
|
||||||
|
// O = | 1 xb yb |
|
||||||
|
// [ 1 xc yc ]
|
||||||
|
let iPreviousVertex = iMinVertex - stride;
|
||||||
|
if (iPreviousVertex < offset) {
|
||||||
|
iPreviousVertex = end - stride;
|
||||||
|
}
|
||||||
|
let iNextVertex = iMinVertex + stride;
|
||||||
|
if (iNextVertex >= end) {
|
||||||
|
iNextVertex = offset;
|
||||||
|
}
|
||||||
|
const aX = flatCoordinates[iPreviousVertex];
|
||||||
|
const aY = flatCoordinates[iPreviousVertex + 1];
|
||||||
|
const bX = flatCoordinates[iMinVertex];
|
||||||
|
const bY = flatCoordinates[iMinVertex + 1];
|
||||||
|
const cX = flatCoordinates[iNextVertex];
|
||||||
|
const cY = flatCoordinates[iNextVertex + 1];
|
||||||
|
const determinant =
|
||||||
|
bX * cY + aX * bY + aY * cX - (aY * bX + bY * cX + aX * cY);
|
||||||
|
|
||||||
|
return determinant < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find vertex along one edge of bounding box.
|
||||||
|
// In this case, we find smallest y; in case of tie also smallest x.
|
||||||
|
function findCornerVertex(flatCoordinates, offset, end, stride) {
|
||||||
|
let iMinVertex = -1;
|
||||||
|
let minY = Infinity;
|
||||||
|
let minXAtMinY = Infinity;
|
||||||
|
for (let i = offset; i < end; i += stride) {
|
||||||
|
const x = flatCoordinates[i];
|
||||||
|
const y = flatCoordinates[i + 1];
|
||||||
|
if (y > minY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (y == minY) {
|
||||||
|
if (x >= minXAtMinY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimum so far.
|
||||||
|
iMinVertex = i;
|
||||||
|
minY = y;
|
||||||
|
minXAtMinY = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iMinVertex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ describe('ol.format.MVT', function () {
|
|||||||
flatCoordinates,
|
flatCoordinates,
|
||||||
ends
|
ends
|
||||||
) {
|
) {
|
||||||
flatCoordinates.push(0, 0, 3, 0, 3, 3, 3, 0, 0, 0);
|
flatCoordinates.push(0, 0, 3, 0, 3, 3, 0, 3, 0, 0);
|
||||||
flatCoordinates.push(1, 1, 1, 2, 2, 2, 2, 1, 1, 1);
|
flatCoordinates.push(1, 1, 1, 2, 2, 2, 2, 1, 1, 1);
|
||||||
ends.push(10, 20);
|
ends.push(10, 20);
|
||||||
};
|
};
|
||||||
@@ -207,8 +207,8 @@ describe('ol.format.MVT', function () {
|
|||||||
flatCoordinates,
|
flatCoordinates,
|
||||||
ends
|
ends
|
||||||
) {
|
) {
|
||||||
flatCoordinates.push(0, 0, 1, 0, 1, 1, 1, 0, 0, 0);
|
flatCoordinates.push(0, 0, 1, 0, 1, 1, 0, 1, 0, 0);
|
||||||
flatCoordinates.push(1, 1, 2, 1, 2, 2, 2, 1, 1, 1);
|
flatCoordinates.push(1, 1, 2, 1, 2, 2, 1, 2, 1, 1);
|
||||||
ends.push(10, 20);
|
ends.push(10, 20);
|
||||||
};
|
};
|
||||||
const feature = format.createFeature_({}, rawFeature);
|
const feature = format.createFeature_({}, rawFeature);
|
||||||
|
|||||||
Reference in New Issue
Block a user