Merge pull request #13297 from MoonE/from-render-feature
Add function to convert RenderFeature to Feature
This commit is contained in:
@@ -19,7 +19,7 @@ import RenderFeature from '../render/Feature.js';
|
|||||||
import Units from '../proj/Units.js';
|
import Units from '../proj/Units.js';
|
||||||
import {assert} from '../asserts.js';
|
import {assert} from '../asserts.js';
|
||||||
import {get} from '../proj.js';
|
import {get} from '../proj.js';
|
||||||
import {linearRingIsClockwise} from '../geom/flat/orient.js';
|
import {inflateEnds} from '../geom/flat/orient.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Options
|
* @typedef {Object} Options
|
||||||
@@ -185,8 +185,8 @@ class MVT extends FeatureFormat {
|
|||||||
|
|
||||||
values[this.layerName_] = rawFeature.layer.name;
|
values[this.layerName_] = rawFeature.layer.name;
|
||||||
|
|
||||||
const flatCoordinates = [];
|
const flatCoordinates = /** @type {Array<number>} */ ([]);
|
||||||
const ends = [];
|
const ends = /** @type {Array<number>} */ ([]);
|
||||||
this.readRawGeometry_(pbf, rawFeature, flatCoordinates, ends);
|
this.readRawGeometry_(pbf, rawFeature, flatCoordinates, ends);
|
||||||
|
|
||||||
const geometryType = getGeometryType(type, ends.length);
|
const geometryType = getGeometryType(type, ends.length);
|
||||||
@@ -203,28 +203,11 @@ class MVT extends FeatureFormat {
|
|||||||
} else {
|
} else {
|
||||||
let geom;
|
let geom;
|
||||||
if (geometryType == GeometryType.POLYGON) {
|
if (geometryType == GeometryType.POLYGON) {
|
||||||
const endss = [];
|
const endss = inflateEnds(flatCoordinates, ends);
|
||||||
let offset = 0;
|
geom =
|
||||||
let prevEndIndex = 0;
|
endss.length > 1
|
||||||
for (let i = 0, ii = ends.length; i < ii; ++i) {
|
? new MultiPolygon(flatCoordinates, GeometryLayout.XY, endss)
|
||||||
const end = ends[i];
|
: new Polygon(flatCoordinates, GeometryLayout.XY, ends);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
geom =
|
geom =
|
||||||
geometryType === GeometryType.POINT
|
geometryType === GeometryType.POINT
|
||||||
@@ -248,7 +231,9 @@ class MVT extends FeatureFormat {
|
|||||||
}
|
}
|
||||||
const geometry = transformGeometryWithOptions(geom, false, options);
|
const geometry = transformGeometryWithOptions(geom, false, options);
|
||||||
feature.setGeometry(geometry);
|
feature.setGeometry(geometry);
|
||||||
|
if (id !== undefined) {
|
||||||
feature.setId(id);
|
feature.setId(id);
|
||||||
|
}
|
||||||
feature.setProperties(values, true);
|
feature.setProperties(values, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,9 @@ class OSMXML extends XMLFeature {
|
|||||||
}
|
}
|
||||||
transformGeometryWithOptions(geometry, false, options);
|
transformGeometryWithOptions(geometry, false, options);
|
||||||
const feature = new Feature(geometry);
|
const feature = new Feature(geometry);
|
||||||
|
if (values.id !== undefined) {
|
||||||
feature.setId(values.id);
|
feature.setId(values.id);
|
||||||
|
}
|
||||||
feature.setProperties(values.tags, true);
|
feature.setProperties(values.tags, true);
|
||||||
state.features.push(feature);
|
state.features.push(feature);
|
||||||
}
|
}
|
||||||
@@ -146,7 +148,9 @@ function readNode(node, objectStack) {
|
|||||||
const geometry = new Point(coordinates);
|
const geometry = new Point(coordinates);
|
||||||
transformGeometryWithOptions(geometry, false, options);
|
transformGeometryWithOptions(geometry, false, options);
|
||||||
const feature = new Feature(geometry);
|
const feature = new Feature(geometry);
|
||||||
|
if (id !== undefined) {
|
||||||
feature.setId(id);
|
feature.setId(id);
|
||||||
|
}
|
||||||
feature.setProperties(values.tags, true);
|
feature.setProperties(values.tags, true);
|
||||||
state.features.push(feature);
|
state.features.push(feature);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,3 +178,31 @@ export function orientLinearRingsArray(
|
|||||||
}
|
}
|
||||||
return offset;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
/**
|
/**
|
||||||
* @module ol/render/Feature
|
* @module ol/render/Feature
|
||||||
*/
|
*/
|
||||||
|
import Feature from '../Feature.js';
|
||||||
|
import GeometryLayout from '../geom/GeometryLayout.js';
|
||||||
import GeometryType from '../geom/GeometryType.js';
|
import GeometryType from '../geom/GeometryType.js';
|
||||||
|
import {
|
||||||
|
LineString,
|
||||||
|
MultiLineString,
|
||||||
|
MultiPoint,
|
||||||
|
MultiPolygon,
|
||||||
|
Point,
|
||||||
|
Polygon,
|
||||||
|
} from '../geom.js';
|
||||||
import {
|
import {
|
||||||
compose as composeTransform,
|
compose as composeTransform,
|
||||||
create as createTransform,
|
create as createTransform,
|
||||||
@@ -18,6 +28,7 @@ import {
|
|||||||
getInteriorPointsOfMultiArray,
|
getInteriorPointsOfMultiArray,
|
||||||
} from '../geom/flat/interiorpoint.js';
|
} from '../geom/flat/interiorpoint.js';
|
||||||
import {get as getProjection} from '../proj.js';
|
import {get as getProjection} from '../proj.js';
|
||||||
|
import {inflateEnds} from '../geom/flat/orient.js';
|
||||||
import {interpolatePoint} from '../geom/flat/interpolate.js';
|
import {interpolatePoint} from '../geom/flat/interpolate.js';
|
||||||
import {linearRingss as linearRingssCenter} from '../geom/flat/center.js';
|
import {linearRingss as linearRingssCenter} from '../geom/flat/center.js';
|
||||||
import {transform2D} from '../geom/flat/transform.js';
|
import {transform2D} from '../geom/flat/transform.js';
|
||||||
@@ -326,4 +337,70 @@ RenderFeature.prototype.getEndss = RenderFeature.prototype.getEnds;
|
|||||||
RenderFeature.prototype.getFlatCoordinates =
|
RenderFeature.prototype.getFlatCoordinates =
|
||||||
RenderFeature.prototype.getOrientedFlatCoordinates;
|
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;
|
export default RenderFeature;
|
||||||
|
|||||||
209
test/node/ol/render/Feature.test.js
Normal file
209
test/node/ol/render/Feature.test.js
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user