parent
a322768f8a
commit
ca841f5181
@ -145,7 +145,8 @@
|
||||
<xs:attribute name="stroke-width" default="0" type="tns:nonNegativeFloat" use="optional" />
|
||||
<!-- priority for label placement, 0 = highest priority -->
|
||||
<xs:attribute name="priority" default="0" type="xs:integer" use="optional" />
|
||||
|
||||
<!-- polygon area expressed as a ratio to tile area, e.g. 0.1 for 10% of tile area -->
|
||||
<xs:attribute name="area-size" default="0" type="tns:nonNegativeFloat" use="optional" />
|
||||
<!-- symbol src name in atlas -->
|
||||
<xs:attribute name="symbol" type="tns:src" use="optional" />
|
||||
</xs:complexType>
|
||||
|
@ -260,6 +260,7 @@
|
||||
-->
|
||||
<m v="parking" zoom-min="15">
|
||||
<area fill="#f4f4f4" stroke="#d4d4d4" stroke-width="0.2" />
|
||||
<symbol src="assets:symbols/transport/parking.svg" />
|
||||
</m>
|
||||
<m closed="yes" v="fountain">
|
||||
<area fill="#b4cbdc" stroke="#000080" stroke-width="0.15" />
|
||||
@ -401,6 +402,7 @@
|
||||
<m e="way">
|
||||
<m closed="yes" k="natural" v="water">
|
||||
<area use="water" />
|
||||
<caption k="name" fill="#777744" size="16" stroke="#aaffffff" stroke-width="2.0" area-size="0.4" />
|
||||
<!--<line use="water:outline" />-->
|
||||
</m>
|
||||
|
||||
@ -521,7 +523,18 @@
|
||||
<!-- runways areas -->
|
||||
<m k="aeroway">
|
||||
<m closed="yes" v="aerodrome">
|
||||
<area fill="#e8ecde" />
|
||||
<m zoom-min="12">
|
||||
<area fill="#e8ecde" />
|
||||
</m>
|
||||
<m zoom-min="12">
|
||||
<caption dy="18" fill="#000000" k="name" priority="5" size="19" stroke="#ffffff"
|
||||
stroke-width="2.0" area-size="0.1"/>
|
||||
</m>
|
||||
<m zoom-max="11">
|
||||
<caption dy="18" fill="#000000" k="ref" priority="5" size="19" stroke="#ffffff"
|
||||
stroke-width="2.0" />
|
||||
</m>
|
||||
<symbol src="assets:symbols/transport/airport.svg" />
|
||||
</m>
|
||||
<!-- A place where planes are parked -->
|
||||
<m v="apron">
|
||||
@ -554,7 +567,7 @@
|
||||
<m zoom-min="17">
|
||||
<caption style="bold" fill="#4040ff" k="name" priority="9" size="14"
|
||||
stroke="#ffffff" stroke-width="2.0" />
|
||||
<caption style="bold" fill="#606060" k="addr:housenumber" priority="10" size="10"
|
||||
<caption style="bold" fill="#606060" k="addr:housenumber" priority="10" size="16"
|
||||
stroke="#ffffff" stroke-width="2.0" />
|
||||
</m>
|
||||
</m>
|
||||
@ -929,7 +942,7 @@
|
||||
</m>
|
||||
|
||||
<m k="highway" v="track">
|
||||
<m k="areay" v="yes">
|
||||
<m k="area" v="yes">
|
||||
<area fill="#aaff0000" />
|
||||
</m>
|
||||
</m>
|
||||
@ -984,6 +997,7 @@
|
||||
|
||||
<!-- place -->
|
||||
<m k="place">
|
||||
<circle radius="5.0" fill="#ff0000"/>
|
||||
<m v="suburb" zoom-max="14">
|
||||
<caption style="italic" fill="#606060" k="name" priority="4" size="17"
|
||||
stroke="#ffffff" stroke-width="2.0" />
|
||||
@ -998,7 +1012,7 @@
|
||||
</m>
|
||||
<m v="town">
|
||||
<caption dy="14" fill="#000000" k="name" priority="2" size="19" stroke="#ffffff"
|
||||
stroke-width="2.0" symbol="assets:symbols/dot_white.svg" />
|
||||
stroke-width="2.0" />
|
||||
</m>
|
||||
<m v="city">
|
||||
<m zoom-min="7">
|
||||
@ -1039,7 +1053,7 @@
|
||||
<m k="aeroway" v="helipad" zoom-min="16">
|
||||
<symbol src="assets:symbols/transport/helicopter_pad.svg" />
|
||||
</m>
|
||||
<m k="aeroway" v="aerodrome|airport">
|
||||
<m k="aeroway" v="aerodrome|airport" zoom-min="9">
|
||||
<symbol src="assets:symbols/transport/airport.svg" />
|
||||
</m>
|
||||
</m>
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
* Copyright 2016 Andrey Novikov
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@ -411,6 +412,27 @@ public class GeometryBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates geometry area, only polygon outer ring is taken into account.
|
||||
*
|
||||
* @return polygon area, 0 for other geometries
|
||||
*/
|
||||
public float area() {
|
||||
if (isPoint() || isLine() || getNumPoints() < 3)
|
||||
return 0f;
|
||||
|
||||
float area = 0f;
|
||||
// use only outer ring
|
||||
int n = index[0];
|
||||
|
||||
for (int i = 0; i < n - 2; i += 2) {
|
||||
area = area + (points[i] * points[i+3]) - (points[i+1] * points[i+2]);
|
||||
}
|
||||
area = area + (points[n-2] * points[1]) - (points[n-1] * points[0]);
|
||||
|
||||
return 0.5f * area;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int o = 0;
|
||||
|
@ -33,6 +33,8 @@ public class MapElement extends GeometryBuffer {
|
||||
|
||||
public final TagSet tags = new TagSet();
|
||||
|
||||
public PointF labelPosition;
|
||||
|
||||
public MapElement() {
|
||||
super(1024, 16);
|
||||
}
|
||||
@ -45,6 +47,10 @@ public class MapElement extends GeometryBuffer {
|
||||
this.layer = layer;
|
||||
}
|
||||
|
||||
public void setLabelPosition(float x, float y) {
|
||||
labelPosition = new PointF(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapElement clear() {
|
||||
layer = 5;
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2016 devemux86
|
||||
* Copyright 2016 Andrey Novikov
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@ -18,6 +19,7 @@ package org.oscim.layers.tile.vector.labeling;
|
||||
|
||||
import org.oscim.core.MapElement;
|
||||
import org.oscim.core.PointF;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.layers.tile.MapTile;
|
||||
import org.oscim.layers.tile.vector.VectorTileLayer.TileLoaderThemeHook;
|
||||
import org.oscim.renderer.bucket.RenderBuckets;
|
||||
@ -26,6 +28,7 @@ import org.oscim.renderer.bucket.TextItem;
|
||||
import org.oscim.theme.styles.RenderStyle;
|
||||
import org.oscim.theme.styles.SymbolStyle;
|
||||
import org.oscim.theme.styles.TextStyle;
|
||||
import org.oscim.utils.geom.PolyLabel;
|
||||
|
||||
import static org.oscim.core.GeometryBuffer.GeometryType.LINE;
|
||||
import static org.oscim.core.GeometryBuffer.GeometryType.POINT;
|
||||
@ -70,23 +73,25 @@ public class LabelTileLoaderHook implements TileLoaderThemeHook {
|
||||
offset += length;
|
||||
}
|
||||
} else if (element.type == POLY) {
|
||||
// TODO place somewhere on polygon
|
||||
String value = element.tags.getValue(text.textKey);
|
||||
if (value == null || value.length() == 0)
|
||||
return false;
|
||||
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
int n = element.index[0];
|
||||
|
||||
for (int i = 0; i < n; ) {
|
||||
x += element.points[i++];
|
||||
y += element.points[i++];
|
||||
if (text.areaSize > 0f) {
|
||||
float area = element.area();
|
||||
float ratio = area / (Tile.SIZE * Tile.SIZE); // we can't use static as it's recalculated based on dpi
|
||||
if (ratio < text.areaSize)
|
||||
return false;
|
||||
}
|
||||
x /= (n / 2);
|
||||
y /= (n / 2);
|
||||
|
||||
ld.labels.push(TextItem.pool.get().set(x, y, value, text));
|
||||
PointF label = element.labelPosition;
|
||||
if (label == null)
|
||||
label = PolyLabel.get(element, 5f);
|
||||
|
||||
if (label.x < 0 || label.x > Tile.SIZE || label.y < 0 || label.y > Tile.SIZE)
|
||||
return false;
|
||||
|
||||
ld.labels.push(TextItem.pool.get().set(label.x, label.y, value, text));
|
||||
} else if (element.type == POINT) {
|
||||
String value = element.tags.getValue(text.textKey);
|
||||
if (value == null || value.length() == 0)
|
||||
@ -97,7 +102,7 @@ public class LabelTileLoaderHook implements TileLoaderThemeHook {
|
||||
ld.labels.push(TextItem.pool.get().set(p.x, p.y, value, text));
|
||||
}
|
||||
}
|
||||
} else if ((element.type == POINT) && (style instanceof SymbolStyle)) {
|
||||
} else if (style instanceof SymbolStyle) {
|
||||
SymbolStyle symbol = (SymbolStyle) style;
|
||||
|
||||
if (symbol.bitmap == null && symbol.texture == null)
|
||||
@ -105,14 +110,32 @@ public class LabelTileLoaderHook implements TileLoaderThemeHook {
|
||||
|
||||
LabelTileData ld = get(tile);
|
||||
|
||||
for (int i = 0, n = element.getNumPoints(); i < n; i++) {
|
||||
PointF p = element.getPoint(i);
|
||||
if (element.type == POINT) {
|
||||
for (int i = 0, n = element.getNumPoints(); i < n; i++) {
|
||||
PointF p = element.getPoint(i);
|
||||
|
||||
SymbolItem it = SymbolItem.pool.get();
|
||||
if (symbol.bitmap != null)
|
||||
it.set(p.x, p.y, symbol.bitmap, true);
|
||||
else
|
||||
it.set(p.x, p.y, symbol.texture, true);
|
||||
ld.symbols.push(it);
|
||||
}
|
||||
} else if (element.type == LINE) {
|
||||
//TODO: implement
|
||||
} else if (element.type == POLY) {
|
||||
PointF centroid = element.labelPosition;
|
||||
if (centroid == null)
|
||||
return false;
|
||||
|
||||
if (centroid.x < 0 || centroid.x > Tile.SIZE || centroid.y < 0 || centroid.y > Tile.SIZE)
|
||||
return false;
|
||||
|
||||
SymbolItem it = SymbolItem.pool.get();
|
||||
if (symbol.bitmap != null)
|
||||
it.set(p.x, p.y, symbol.bitmap, true);
|
||||
it.set(centroid.x, centroid.y, symbol.bitmap, true);
|
||||
else
|
||||
it.set(p.x, p.y, symbol.texture, true);
|
||||
it.set(centroid.x, centroid.y, symbol.texture, true);
|
||||
ld.symbols.push(it);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
* Copyright 2016 devemux86
|
||||
* Copyright 2016 Longri
|
||||
* Copyright 2016 Andrey Novikov
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@ -863,6 +864,9 @@ public class XmlThemeBuilder extends DefaultHandler {
|
||||
else if ("priority".equals(name))
|
||||
b.priority = Integer.parseInt(value);
|
||||
|
||||
else if ("area-size".equals(name))
|
||||
b.areaSize = Float.parseFloat(value);
|
||||
|
||||
else if ("dy".equals(name))
|
||||
// NB: minus..
|
||||
b.dy = -Float.parseFloat(value) * CanvasAdapter.dpi / 160;
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
* Copyright 2016 devemux86
|
||||
* Copyright 2016 Andrey Novikov
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@ -36,6 +37,7 @@ public final class TextStyle extends RenderStyle<TextStyle> {
|
||||
public boolean caption;
|
||||
public float dy;
|
||||
public int priority;
|
||||
public float areaSize;
|
||||
public Bitmap bitmap;
|
||||
public TextureRegion texture;
|
||||
public FontFamily fontFamily;
|
||||
@ -49,6 +51,7 @@ public final class TextStyle extends RenderStyle<TextStyle> {
|
||||
fontSize = 0;
|
||||
caption = false;
|
||||
priority = Integer.MAX_VALUE;
|
||||
areaSize = 0f;
|
||||
bitmap = null;
|
||||
texture = null;
|
||||
fillColor = Color.BLACK;
|
||||
@ -98,6 +101,11 @@ public final class TextStyle extends RenderStyle<TextStyle> {
|
||||
return self();
|
||||
}
|
||||
|
||||
public T areaSize(float areaSize) {
|
||||
this.areaSize = areaSize;
|
||||
return self();
|
||||
}
|
||||
|
||||
public T bitmap(Bitmap bitmap) {
|
||||
this.bitmap = bitmap;
|
||||
return self();
|
||||
@ -126,6 +134,7 @@ public final class TextStyle extends RenderStyle<TextStyle> {
|
||||
fontSize = other.fontSize;
|
||||
caption = other.caption;
|
||||
priority = other.priority;
|
||||
areaSize = other.areaSize;
|
||||
bitmap = other.bitmap;
|
||||
texture = other.texture;
|
||||
fillColor = other.fillColor;
|
||||
@ -141,6 +150,7 @@ public final class TextStyle extends RenderStyle<TextStyle> {
|
||||
this.caption = style.caption;
|
||||
this.dy = style.dy;
|
||||
this.priority = style.priority;
|
||||
this.areaSize = style.areaSize;
|
||||
this.bitmap = style.bitmap;
|
||||
this.texture = style.texture;
|
||||
this.fillColor = style.paint.getColor();
|
||||
@ -159,6 +169,7 @@ public final class TextStyle extends RenderStyle<TextStyle> {
|
||||
this.caption = tb.caption;
|
||||
this.dy = tb.dy;
|
||||
this.priority = tb.priority;
|
||||
this.areaSize = tb.areaSize;
|
||||
this.bitmap = tb.bitmap;
|
||||
this.texture = tb.texture;
|
||||
|
||||
@ -193,6 +204,7 @@ public final class TextStyle extends RenderStyle<TextStyle> {
|
||||
public final boolean caption;
|
||||
public final float dy;
|
||||
public final int priority;
|
||||
public final float areaSize;
|
||||
|
||||
public float fontHeight;
|
||||
public float fontDescent;
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2013, 2014 Hannes Janetzek
|
||||
* Copyright 2016 devemux86
|
||||
* Copyright 2016 Andrey Novikov
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@ -840,9 +841,11 @@ public class MapDatabase implements ITileDataSource {
|
||||
e.tags.add(new Tag(Tag.KEY_REF, str, false));
|
||||
}
|
||||
}
|
||||
if ((featureByte & WAY_FEATURE_LABEL_POSITION) != 0)
|
||||
// labelPosition =
|
||||
readOptionalLabelPosition();
|
||||
|
||||
int[] labelPosition = null;
|
||||
if ((featureByte & WAY_FEATURE_LABEL_POSITION) != 0) {
|
||||
labelPosition = readOptionalLabelPosition();
|
||||
}
|
||||
|
||||
if ((featureByte & WAY_FEATURE_DATA_BLOCKS_BYTE) != 0) {
|
||||
wayDataBlocks = mReadBuffer.readUnsignedInt();
|
||||
@ -870,6 +873,8 @@ public class MapDatabase implements ITileDataSource {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (labelPosition != null && wayDataBlock == 0)
|
||||
e.setLabelPosition(e.points[0] + labelPosition[0], e.points[1] + labelPosition[1]);
|
||||
mTileProjection.project(e);
|
||||
|
||||
if (!e.tags.containsKey("building"))
|
||||
@ -879,6 +884,7 @@ public class MapDatabase implements ITileDataSource {
|
||||
e.simplify(1, true);
|
||||
|
||||
e.setLayer(layer);
|
||||
|
||||
mapDataSink.process(e);
|
||||
}
|
||||
}
|
||||
@ -886,14 +892,14 @@ public class MapDatabase implements ITileDataSource {
|
||||
return true;
|
||||
}
|
||||
|
||||
private float[] readOptionalLabelPosition() {
|
||||
float[] labelPosition = new float[2];
|
||||
private int[] readOptionalLabelPosition() {
|
||||
int[] labelPosition = new int[2];
|
||||
|
||||
/* get the label position latitude offset (VBE-S) */
|
||||
labelPosition[1] = mTileLatitude + mReadBuffer.readSignedInt();
|
||||
labelPosition[1] = mReadBuffer.readSignedInt();
|
||||
|
||||
/* get the label position longitude offset (VBE-S) */
|
||||
labelPosition[0] = mTileLongitude + mReadBuffer.readSignedInt();
|
||||
labelPosition[0] = mReadBuffer.readSignedInt();
|
||||
|
||||
return labelPosition;
|
||||
}
|
||||
@ -1021,6 +1027,10 @@ public class MapDatabase implements ITileDataSource {
|
||||
indices[idx] = (short) cnt;
|
||||
}
|
||||
}
|
||||
if (e.labelPosition != null) {
|
||||
e.labelPosition.x = projectLon(e.labelPosition.x);
|
||||
e.labelPosition.y = projectLat(e.labelPosition.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
198
vtm/src/org/oscim/utils/geom/PolyLabel.java
Normal file
198
vtm/src/org/oscim/utils/geom/PolyLabel.java
Normal file
@ -0,0 +1,198 @@
|
||||
/**
|
||||
* Copyright 2016 Andrey Novikov
|
||||
* Java implementation of
|
||||
* https://github.com/mapbox/polylabel
|
||||
*
|
||||
* ISC License
|
||||
* Copyright (c) 2016 Mapbox
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
* with or without fee is hereby granted, provided that the above copyright notice
|
||||
* and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||
* THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
|
||||
* IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package org.oscim.utils.geom;
|
||||
|
||||
import org.oscim.core.GeometryBuffer;
|
||||
import org.oscim.core.PointF;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
public class PolyLabel {
|
||||
private static float SQRT2 = (float) Math.sqrt(2);
|
||||
|
||||
/**
|
||||
* Returns pole of inaccessibility, the most distant internal point from the polygon outline.
|
||||
* @param polygon polygon geometry
|
||||
* @param precision calculation precision
|
||||
* @return optimal label placement point
|
||||
*/
|
||||
public static PointF get(GeometryBuffer polygon, float precision) {
|
||||
// find the bounding box of the outer ring
|
||||
float minX = Float.MAX_VALUE, minY = Float.MAX_VALUE, maxX = Float.MIN_VALUE, maxY = Float.MIN_VALUE;
|
||||
|
||||
int n = polygon.index[0];
|
||||
|
||||
for (int i = 0; i < n; ) {
|
||||
float x = polygon.points[i++];
|
||||
float y = polygon.points[i++];
|
||||
if (x < minX) minX = x;
|
||||
if (y < minY) minY = y;
|
||||
if (x > maxX) maxX = x;
|
||||
if (y > maxY) maxY = y;
|
||||
}
|
||||
|
||||
float width = maxX - minX;
|
||||
float height = maxY - minY;
|
||||
float cellSize = Math.min(width, height);
|
||||
float h = cellSize / 2;
|
||||
|
||||
// a priority queue of cells in order of their "potential" (max distance to polygon)
|
||||
PriorityQueue<Cell> cellQueue = new PriorityQueue<>(1, new MaxComparator());
|
||||
|
||||
// cover polygon with initial cells
|
||||
for (float x = minX; x < maxX; x += cellSize) {
|
||||
for (float y = minY; y < maxY; y += cellSize) {
|
||||
cellQueue.add(new Cell(x + h, y + h, h, polygon));
|
||||
}
|
||||
}
|
||||
|
||||
// take centroid as the first best guess
|
||||
Cell bestCell = getCentroidCell(polygon);
|
||||
|
||||
// special case for rectangular polygons
|
||||
Cell bboxCell = new Cell(minX + width / 2, minY + height / 2, 0, polygon);
|
||||
if (bboxCell.d > bestCell.d) bestCell = bboxCell;
|
||||
|
||||
while (!cellQueue.isEmpty()) {
|
||||
// pick the most promising cell from the queue
|
||||
Cell cell = cellQueue.remove();
|
||||
|
||||
// update the best cell if we found a better one
|
||||
if (cell.d > bestCell.d)
|
||||
bestCell = cell;
|
||||
|
||||
// do not drill down further if there's no chance of a better solution
|
||||
if (cell.max - bestCell.d <= precision) continue;
|
||||
|
||||
// split the cell into four cells
|
||||
h = cell.h / 2;
|
||||
cellQueue.add(new Cell(cell.x - h, cell.y - h, h, polygon));
|
||||
cellQueue.add(new Cell(cell.x + h, cell.y - h, h, polygon));
|
||||
cellQueue.add(new Cell(cell.x - h, cell.y + h, h, polygon));
|
||||
cellQueue.add(new Cell(cell.x + h, cell.y + h, h, polygon));
|
||||
}
|
||||
|
||||
return new PointF(bestCell.x, bestCell.y);
|
||||
}
|
||||
|
||||
private static class MaxComparator implements Comparator<Cell>
|
||||
{
|
||||
@Override
|
||||
public int compare(Cell a, Cell b) {
|
||||
return Float.compare(b.max, a.max);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Cell {
|
||||
final float x;
|
||||
final float y;
|
||||
final float h;
|
||||
final float d;
|
||||
final float max;
|
||||
|
||||
Cell(float x, float y, float h, GeometryBuffer polygon) {
|
||||
this.x = x; // cell center x
|
||||
this.y = y; // cell center y
|
||||
this.h = h; // half the cell size
|
||||
this.d = pointToPolygonDist(x, y, polygon); // distance from cell center to polygon
|
||||
this.max = this.d + this.h * SQRT2; // max distance to polygon within a cell
|
||||
}
|
||||
}
|
||||
|
||||
// signed distance from point to polygon outline (negative if point is outside)
|
||||
private static float pointToPolygonDist(float x, float y, GeometryBuffer polygon) {
|
||||
boolean inside = false;
|
||||
float minDistSq = Float.POSITIVE_INFINITY;
|
||||
|
||||
int pos = 0;
|
||||
|
||||
for (int k = 0; k < polygon.index.length; k++) {
|
||||
if (polygon.index[k] < 0)
|
||||
break;
|
||||
if (polygon.index[k] == 0)
|
||||
continue;
|
||||
|
||||
for (int i = 0, n = polygon.index[k], j = n - 2; i < n; j = i, i += 2) {
|
||||
float ax = polygon.points[pos+i];
|
||||
float ay = polygon.points[pos+i+1];
|
||||
float bx = polygon.points[pos+j];
|
||||
float by = polygon.points[pos+j+1];
|
||||
|
||||
if (((ay > y) ^ (by > y)) &&
|
||||
(x < (bx - ax) * (y - ay) / (by - ay) + ax)) inside = !inside;
|
||||
|
||||
minDistSq = Math.min(minDistSq, getSegDistSq(x, y, ax, ay, bx, by));
|
||||
}
|
||||
|
||||
pos += polygon.index[k];
|
||||
}
|
||||
|
||||
return (float) ((inside ? 1 : -1) * Math.sqrt(minDistSq));
|
||||
}
|
||||
|
||||
// get polygon centroid
|
||||
private static Cell getCentroidCell(GeometryBuffer polygon) {
|
||||
float area = 0f;
|
||||
float x = 0f;
|
||||
float y = 0f;
|
||||
|
||||
for (int i = 0, n = polygon.index[0], j = n - 2; i < n; j = i, i += 2) {
|
||||
float ax = polygon.points[i];
|
||||
float ay = polygon.points[i+1];
|
||||
float bx = polygon.points[j];
|
||||
float by = polygon.points[j+1];
|
||||
float f = ax * by - bx * ay;
|
||||
x += (ax + bx) * f;
|
||||
y += (ay + by) * f;
|
||||
area += f * 3;
|
||||
}
|
||||
return new Cell(x / area, y / area, 0f, polygon);
|
||||
}
|
||||
|
||||
// get squared distance from a point to a segment
|
||||
private static float getSegDistSq(float px, float py, float ax, float ay, float bx, float by) {
|
||||
|
||||
float x = ax;
|
||||
float y = ay;
|
||||
float dx = bx - x;
|
||||
float dy = by - y;
|
||||
|
||||
if (dx != 0f || dy != 0f) {
|
||||
|
||||
float t = ((px - x) * dx + (py - y) * dy) / (dx * dx + dy * dy);
|
||||
|
||||
if (t > 1) {
|
||||
x = bx;
|
||||
y = by;
|
||||
|
||||
} else if (t > 0) {
|
||||
x += dx * t;
|
||||
y += dy * t;
|
||||
}
|
||||
}
|
||||
|
||||
dx = px - x;
|
||||
dy = py - y;
|
||||
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user