Skip to content

Commit

Permalink
Convert annotations to polygon lists.
Browse files Browse the repository at this point in the history
  • Loading branch information
manthey committed May 9, 2022
1 parent 37b24bd commit e2821f6
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 7 deletions.
77 changes: 77 additions & 0 deletions src/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,7 @@ var annotation = function (type, args) {
layer = m_this.layer(),
aPP = layer.options('adjacentPointProximity'),
near, atEnd;
// DWM:: more complex

if (start.outer) {
start = vindex ? start.inner[vindex - 1] : start.outer;
Expand Down Expand Up @@ -1425,6 +1426,19 @@ var rectangleAnnotation = function (args, annotationName) {
return features;
};

/**
* Return this annotation as a polygon list.
*
* @param {geo.util.polyop.spec} [opts] Ignored.
* @returns {array[]} A list of polygons.
*/
this.toPolygonList = function (opts) {
if (m_this._coordinates().length < 3) {
return [];
}
return [[m_this._coordinates().map((pt) => [pt.x, pt.y])]];
};

/**
* Get and optionally set coordinates associated with this annotation in the
* map gcs coordinate system.
Expand Down Expand Up @@ -1746,6 +1760,50 @@ var ellipseAnnotation = function (args, annotationName) {
}
return features;
};

/**
* Return this annotation as a polygon list.
*
* @param {geo.util.polyop.spec} [opts] The ``tolerance`` and
* ``pixelTolerance`` parameters are used if set. Otherwise, a polygon is
* approximated to 1/10th of a pixel at the map's current maximum zoom
* level.
* @returns {array[]} A list of polygons.
*/
this.toPolygonList = function (opts) {
const coord = m_this._coorddinates();
if (coord.length < 3) {
return [];
}
let tolerance = (opts && opts.tolerance) || 0;
if (!tolerance) {
const map = m_this.layer().map();
if (opts && opts.pixelTolerance) {
tolerance = map.unitsPerPixel(map.zoom()) * opts.pixelTolerance;
} else {
tolerance = map.unitsPerPixel(map.zoomRange().max) * 0.1;
}
}
const w = ((coord[0].x - coord[1].x) ** 2 + (coord[0].y - coord[1].y) ** 2) ** 0.5;
const h = ((coord[0].x - coord[3].x) ** 2 + (coord[0].y - coord[3].y) ** 2) ** 0.5;
const cx = (coord[0].x + coord[2].x) / 2;
const cy = (coord[0].y - coord[2].y) / 2;
const radius = Math.max(w, h) / 2;
const rotation = -Math.atan2(coord[1].y - coord[0].y, coord[1].x - coord[0].x);
const sides = Math.max(12, Math.ceil(Math.PI / Math.acos((radius - tolerance) / (radius + tolerance))));
const a = w / 2 * (1 + (1 - Math.cos(Math.PI / sides)) / 2);
const b = h / 2 * (1 + (1 - Math.cos(Math.PI / sides)) / 2);
const poly = [];
const cosr = Math.cos(rotation), sinr = Math.sin(rotation);
for (let s = 0; s < sides; s += 1) {
const sa = Math.PI * 2 * s / sides;
const cosa = Math.cos(sa), sina = Math.sin(sa);
const x = cx + a * cosr * cosa - b * sinr * sina;
const y = cy + a * sinr * cosa + b * cosr * sina;
poly.push([x, y]);
}
return [[poly]];
};
};
inherit(ellipseAnnotation, rectangleAnnotation);

Expand Down Expand Up @@ -1904,6 +1962,25 @@ var polygonAnnotation = function (args) {
return features;
};

/**
* Return this annotation as a polygon list.
*
* @param {geo.util.polyop.spec} [opts] Ignored.
* @returns {array[]} A list of polygons.
*/
this.toPolygonList = function (opts) {
const coord = m_this._coordinates();
if (coord.outer) {
const result = [[coord.outer.map((pt) => [pt.x, pt.y])]];
(coord.inner || []).forEach((h) => result.push(h.map((pt) => [pt.x, pt.y])));
return result;
}
if (coord.length < 3) {
return [];
}
return [[coord.map((pt) => [pt.x, pt.y])]];
};

/**
* Get and optionally set coordinates associated with this annotation in the
* map gcs coordinate system.
Expand Down
21 changes: 21 additions & 0 deletions src/annotationLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,27 @@ var annotationLayer = function (arg) {
return m_this;
};

/**
* Return any annotation that has area as a polygon list: an array of
* polygons, each of which is an array of polylines, each of which is an
* array of points, each of which is a 2-tuple of numbers.
*
* @param {geo.util.polyop.spec} [opts] Ignored.
* @returns {array[]} A list of polygons.
*/
this.toPolygonList = function (opts) {
const poly = [];
opts = opts || {};
opts.annotationIndices = [];
m_annotations.forEach((annotation, idx) => {
if (annotation.toPolygonList) {
annotation.toPolygonList(opts).forEach((p) => poly.push(p));
opts.annotationIndices.push(idx);
}
});
return poly;
};

/**
* Initialize.
*
Expand Down
15 changes: 8 additions & 7 deletions src/util/polyops.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,6 @@ var geo_map = require('../map');
/**
* Object specification for polygon operation options.
*
* TODO: should poly1/poly2 accept annotationLayer?
* TODO: should epsilon be configurable from the display pixel size?
*
* TODO: add
* rdp: threshold
* ellipseToPoly: threshold (only if we input features) -- use rdp
*
* @typedef {object} geo.util.polyop.spec
* @property {geo.anyPolygon} [poly1] The first polygon set to operate on.
* @property {geo.anyPolygon} [poly2] The second polygon set to operate on.
Expand Down Expand Up @@ -49,6 +42,14 @@ var geo_map = require('../map');
* array is the length of the input polygon array and each entry is either
* undefined or contains a list of indices of corresponding output polygons.
* Multiple inputs can refer to the same output.
* @property {number} [tolerance] A tolerance value to pass to features,
* annotations, or other object-based polygon generators to specify how
* closely non-polygons are converted to polygons. This is in the feature's
* gcs coordinate system.
* @property {number} [pixelTolerance] A tolerance value to pass to features,
* annotations, or other object-based polygon generators to specify how
* closely non-polygons are converted to polygons. This is interpreted in
* the feature's map's display coordinates.
*/

const AlternateOpNames = {
Expand Down

0 comments on commit e2821f6

Please sign in to comment.