Merge pull request #13297 from MoonE/from-render-feature

Add function to convert RenderFeature to Feature
This commit is contained in:
MoonE
2022-01-30 13:53:47 +01:00
committed by GitHub
5 changed files with 331 additions and 28 deletions

View File

@@ -19,7 +19,7 @@ import RenderFeature from '../render/Feature.js';
import Units from '../proj/Units.js';
import {assert} from '../asserts.js';
import {get} from '../proj.js';
import {linearRingIsClockwise} from '../geom/flat/orient.js';
import {inflateEnds} from '../geom/flat/orient.js';
/**
* @typedef {Object} Options
@@ -185,8 +185,8 @@ class MVT extends FeatureFormat {
values[this.layerName_] = rawFeature.layer.name;
const flatCoordinates = [];
const ends = [];
const flatCoordinates = /** @type {Array<number>} */ ([]);
const ends = /** @type {Array<number>} */ ([]);
this.readRawGeometry_(pbf, rawFeature, flatCoordinates, ends);
const geometryType = getGeometryType(type, ends.length);
@@ -203,28 +203,11 @@ class MVT extends FeatureFormat {
} else {
let geom;
if (geometryType == GeometryType.POLYGON) {
const endss = [];
let offset = 0;
let prevEndIndex = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
// classifies an array of rings into polygons with outer rings and holes
if (!linearRingIsClockwise(flatCoordinates, offset, end, 2)) {
endss.push(ends.slice(prevEndIndex, i + 1));
} else {
if (endss.length === 0) {
continue;
}
endss[endss.length - 1].push(ends[prevEndIndex]);
}
prevEndIndex = i + 1;
offset = end;
}
if (endss.length > 1) {
geom = new MultiPolygon(flatCoordinates, GeometryLayout.XY, endss);
} else {
geom = new Polygon(flatCoordinates, GeometryLayout.XY, ends);
}
const endss = inflateEnds(flatCoordinates, ends);
geom =
endss.length > 1
? new MultiPolygon(flatCoordinates, GeometryLayout.XY, endss)
: new Polygon(flatCoordinates, GeometryLayout.XY, ends);
} else {
geom =
geometryType === GeometryType.POINT
@@ -248,7 +231,9 @@ class MVT extends FeatureFormat {
}
const geometry = transformGeometryWithOptions(geom, false, options);
feature.setGeometry(geometry);
feature.setId(id);
if (id !== undefined) {
feature.setId(id);
}
feature.setProperties(values, true);
}

View File

@@ -96,7 +96,9 @@ class OSMXML extends XMLFeature {
}
transformGeometryWithOptions(geometry, false, options);
const feature = new Feature(geometry);
feature.setId(values.id);
if (values.id !== undefined) {
feature.setId(values.id);
}
feature.setProperties(values.tags, true);
state.features.push(feature);
}
@@ -146,7 +148,9 @@ function readNode(node, objectStack) {
const geometry = new Point(coordinates);
transformGeometryWithOptions(geometry, false, options);
const feature = new Feature(geometry);
feature.setId(id);
if (id !== undefined) {
feature.setId(id);
}
feature.setProperties(values.tags, true);
state.features.push(feature);
}

View File

@@ -178,3 +178,31 @@ export function orientLinearRingsArray(
}
return offset;
}
/**
* Return a two-dimensional endss
* @param {Array<number>} flatCoordinates Flat coordinates
* @param {Array<number>} ends Linear ring end indexes
* @return {Array<Array<number>>} Two dimensional endss array that can
* be used to contruct a MultiPolygon
*/
export function inflateEnds(flatCoordinates, ends) {
const endss = [];
let offset = 0;
let prevEndIndex = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
// classifies an array of rings into polygons with outer rings and holes
if (!linearRingIsClockwise(flatCoordinates, offset, end, 2)) {
endss.push(ends.slice(prevEndIndex, i + 1));
} else {
if (endss.length === 0) {
continue;
}
endss[endss.length - 1].push(ends[prevEndIndex]);
}
prevEndIndex = i + 1;
offset = end;
}
return endss;
}

View File

@@ -1,7 +1,17 @@
/**
* @module ol/render/Feature
*/
import Feature from '../Feature.js';
import GeometryLayout from '../geom/GeometryLayout.js';
import GeometryType from '../geom/GeometryType.js';
import {
LineString,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
Polygon,
} from '../geom.js';
import {
compose as composeTransform,
create as createTransform,
@@ -18,6 +28,7 @@ import {
getInteriorPointsOfMultiArray,
} from '../geom/flat/interiorpoint.js';
import {get as getProjection} from '../proj.js';
import {inflateEnds} from '../geom/flat/orient.js';
import {interpolatePoint} from '../geom/flat/interpolate.js';
import {linearRingss as linearRingssCenter} from '../geom/flat/center.js';
import {transform2D} from '../geom/flat/transform.js';
@@ -326,4 +337,70 @@ RenderFeature.prototype.getEndss = RenderFeature.prototype.getEnds;
RenderFeature.prototype.getFlatCoordinates =
RenderFeature.prototype.getOrientedFlatCoordinates;
/**
* Create a geometry from an `ol/render/Feature`
* @param {RenderFeature} renderFeature
* Render Feature
* @return {Point|MultiPoint|LineString|MultiLineString|Polygon|MultiPolygon}
* New geometry instance.
* @api
*/
export function toGeometry(renderFeature) {
const geometryType = renderFeature.getType();
switch (geometryType) {
case GeometryType.POINT:
return new Point(renderFeature.getFlatCoordinates());
case GeometryType.MULTI_POINT:
return new MultiPoint(
renderFeature.getFlatCoordinates(),
GeometryLayout.XY
);
case GeometryType.LINE_STRING:
return new LineString(
renderFeature.getFlatCoordinates(),
GeometryLayout.XY
);
case GeometryType.MULTI_LINE_STRING:
return new MultiLineString(
renderFeature.getFlatCoordinates(),
GeometryLayout.XY,
/** @type {Array<number>} */ (renderFeature.getEnds())
);
case GeometryType.POLYGON:
const flatCoordinates = renderFeature.getFlatCoordinates();
const ends = /** @type {Array<number>} */ (renderFeature.getEnds());
const endss = inflateEnds(flatCoordinates, ends);
return endss.length > 1
? new MultiPolygon(flatCoordinates, GeometryLayout.XY, endss)
: new Polygon(flatCoordinates, GeometryLayout.XY, ends);
default:
throw new Error('Invalid geometry type:' + geometryType);
}
}
/**
* Create an `ol/Feature` from an `ol/render/Feature`
* @param {RenderFeature} renderFeature RenderFeature
* @param {string} [opt_geometryName='geometry'] Geometry name to use
* when creating the Feature.
* @return {Feature} Newly constructed `ol/Feature` with properties,
* geometry, and id copied over.
* @api
*/
export function toFeature(renderFeature, opt_geometryName) {
const id = renderFeature.getId();
const geometry = toGeometry(renderFeature);
const properties = renderFeature.getProperties();
const feature = new Feature();
if (opt_geometryName !== undefined) {
feature.setGeometryName(opt_geometryName);
}
feature.setGeometry(geometry);
if (id !== undefined) {
feature.setId(id);
}
feature.setProperties(properties, true);
return feature;
}
export default RenderFeature;

View File

@@ -0,0 +1,209 @@
import GeometryType from '../../../../src/ol/geom/GeometryType.js';
import RenderFeature, {
toFeature,
toGeometry,
} from '../../../../src/ol/render/Feature.js';
import expect from '../../expect.js';
import {
LineString,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
Polygon,
} from '../../../../src/ol/geom.js';
describe('ol/render/Feature', function () {
describe('ol/render/Feature.toGeometry()', function () {
it('creates a Point', function () {
const geometry = new Point([0, 0]);
const renderFeature = new RenderFeature(
geometry.getType(),
geometry.getFlatCoordinates().slice(),
[]
);
const converted = toGeometry(renderFeature);
expect(converted).to.be.a(Point);
expect(converted.getFlatCoordinates()).to.eql(
geometry.getFlatCoordinates()
);
expect(converted.getProperties()).to.eql({});
});
it('creates a MultiPoint', function () {
const geometry = new MultiPoint([
[0, 0],
[4, 5],
]);
const renderFeature = new RenderFeature(
geometry.getType(),
geometry.getFlatCoordinates().slice(),
[]
);
const converted = toGeometry(renderFeature);
expect(converted).to.be.a(MultiPoint);
expect(converted.getFlatCoordinates()).to.eql(
geometry.getFlatCoordinates()
);
expect(converted.getProperties()).to.eql({});
});
it('creates a LineString', function () {
const geometry = new LineString([
[0, 0],
[4, 5],
]);
const renderFeature = new RenderFeature(
geometry.getType(),
geometry.getFlatCoordinates().slice(),
[]
);
const converted = toGeometry(renderFeature);
expect(converted).to.be.a(LineString);
expect(converted.getFlatCoordinates()).to.eql(
geometry.getFlatCoordinates()
);
expect(converted.getProperties()).to.eql({});
});
it('creates a MultiLineString', function () {
const geometry = new MultiLineString([
[
[0, 0],
[4, 5],
],
[
[0, 0],
[4, 5],
],
]);
const renderFeature = new RenderFeature(
geometry.getType(),
geometry.getFlatCoordinates().slice(),
geometry.getEnds().slice()
);
const converted = toGeometry(renderFeature);
expect(converted).to.be.a(MultiLineString);
expect(converted.getFlatCoordinates()).to.eql(
geometry.getFlatCoordinates()
);
expect(converted.getEnds()).to.eql(geometry.getEnds());
expect(converted.getProperties()).to.eql({});
});
it('creates a Polygon', function () {
const geometry = new Polygon([
[
[0, 0],
[5, 0],
[5, 5],
[0, 0],
],
[
[1, 1],
[4, 4],
[4, 1],
[1, 1],
],
]);
const renderFeature = new RenderFeature(
geometry.getType(),
geometry.getFlatCoordinates().slice(),
geometry.getEnds().slice()
);
const converted = toGeometry(renderFeature);
expect(converted).to.be.a(Polygon);
expect(converted.getFlatCoordinates()).to.eql(
geometry.getFlatCoordinates()
);
expect(converted.getEnds()).to.eql(geometry.getEnds());
expect(converted.getProperties()).to.eql({});
});
it('creates a MultiPolygon from oriented polygon rings', function () {
const geometry = new MultiPolygon([
[
[
[0, 0],
[5, 0],
[5, 5],
[0, 0],
],
[
[1, 1],
[4, 4],
[4, 1],
[1, 1],
],
],
[
[
[-0, -0],
[-5, -0],
[-5, -5],
[-0, -0],
],
],
]);
const renderFeature = new RenderFeature(
GeometryType.POLYGON,
geometry.getFlatCoordinates().slice(),
geometry.getEndss().flat(1)
);
const converted = toGeometry(renderFeature);
expect(converted).to.be.a(MultiPolygon);
expect(converted.getFlatCoordinates()).to.eql(
geometry.getFlatCoordinates()
);
expect(converted.getEndss()).to.eql(geometry.getEndss());
expect(converted.getProperties()).to.eql({});
});
});
describe('ol/render/Feature.toFeature()', function () {
it('creates a Feature<Point>', function () {
const id = 'asdf';
const properties = {test: '123'};
const geometry = new Point([0, 0]);
const renderFeature = new RenderFeature(
geometry.getType(),
geometry.getFlatCoordinates().slice(),
[],
properties,
id
);
const feature = toFeature(renderFeature);
const converted = feature.getGeometry();
expect(converted).to.be.a(Point);
expect(converted.getFlatCoordinates()).to.eql(
geometry.getFlatCoordinates()
);
expect(feature.getId()).to.be(id);
const props = feature.getProperties();
delete props.geometry;
expect(props).to.eql(properties);
});
});
it('creates a Feature<LineString> with non-default geometry name', function () {
const id = 'asdf';
const properties = {geometry: '123'};
const geometry = new LineString([
[0, 0],
[5, 5],
]);
const renderFeature = new RenderFeature(
geometry.getType(),
geometry.getFlatCoordinates().slice(),
[],
properties,
id
);
const geometryName = 'geom';
const feature = toFeature(renderFeature, geometryName);
const converted = feature.getGeometry();
expect(converted).to.be.a(LineString);
expect(feature.get(geometryName)).to.be(converted);
expect(converted.getFlatCoordinates()).to.eql(
geometry.getFlatCoordinates()
);
expect(feature.getId()).to.be(id);
const props = feature.getProperties();
delete props.geom;
expect(props).to.eql(properties);
});
});