VertexLabelStyle.java
/*
* Copyright (c) 2019 Martin Davis.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jtstest.testbuilder.ui.style;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import org.locationtech.jts.awt.FontGlyphReader;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.OrdinateFormat;
import org.locationtech.jtstest.testbuilder.ui.GraphicsUtil;
import org.locationtech.jtstest.testbuilder.ui.Viewport;
public class VertexLabelStyle implements Style
{
private static final int DEFAULT_FONT_SIZE = 11;
private static final int LABEL_OFFSET = 4;
private static final double MIN_VEW_DIST_SQ = 900;
private Font font = new Font(FontGlyphReader.FONT_SERIF, Font.PLAIN, DEFAULT_FONT_SIZE);
private Color color;
private int size;
// reuse point objects to avoid creation overhead
private Point2D pM = new Point2D.Double();
private Point2D pV = new Point2D.Double();
public VertexLabelStyle(Color color) {
this.color = color;
// create basic rectangle shape
init();
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
init();
}
private void init() {
}
public void paint(Geometry geom, Viewport viewport, Graphics2D g)
{
g.setPaint(color);
g.setFont(font);
Coordinate[] coordinates = geom.getCoordinates();
int len = coordinates.length;
// don't label duplicate closing point
if (len > 1 && coordinates[0].equals2D(coordinates[len - 1])) len--;
Point2D lastDrawnPV = new Point2D.Double();;
for (int i = 0; i < len; i++) {
Coordinate pt = coordinates[i];
if (! viewport.containsInModel(pt)) {
continue;
}
pM.setLocation(pt.x, pt.y);
viewport.toView(pM, pV);
if (i > 0 && isTooClose(lastDrawnPV, pV)) continue;
lastDrawnPV.setLocation(pV);
String label = format(pt);
int dir = 2; // Use N for points
if (len > 1) {
Coordinate p1 = coordinates[ i <= 0 ? 0 : i-1 ];
Coordinate p2 = coordinates[ (i >= len - 1) ? len - 2 : i+1 ];
dir = labelDirection(pt, p1, p2);
}
/*
System.out.println( pt + " dir= " + dir + " "
+ DIR_ALIGN[dir][0] + " " + DIR_ALIGN[dir][1] );
*/
GraphicsUtil.drawStringAlign(g, label, (int) pV.getX(), (int) pV.getY(),
DIR_ALIGN[dir][0], DIR_ALIGN[dir][1], LABEL_OFFSET );
}
}
private static String format(Coordinate p) {
return OrdinateFormat.DEFAULT.format(p.x) + "," + OrdinateFormat.DEFAULT.format(p.y);
}
private boolean isTooClose(Point2D p0, Point2D p1) {
double dx = p1.getX() - p0.getX();
double dy = p1.getY() - p0.getY();
double len = dx * dx + dy * dy;
return len < MIN_VEW_DIST_SQ;
}
private static float[][] DIR_ALIGN = {
{ 0, 0.5f }, // 0 - E
{ 0, 0 }, // 1 - NE
{ 0.5f, 0 }, // 2 - N
{ 1, 0 }, // 3 - NW
{ 1, 0.5f }, // 4 - W
{ 1, 1 }, // 5 - SW
{ 0.5f, 1 }, // 6 - S
{ 0, 1 } // 7 - SE
};
private int labelDirection(Coordinate pt, Coordinate p1, Coordinate p2) {
double dx1 = p1.getX() - pt.getX();
double dy1 = p1.getY() - pt.getY();
double dx2 = p2.getX() - pt.getX();
double dy2 = p2.getY() - pt.getY();
if (dx1 <= 0 && dx2 <= 0) return 0;
if (dx1 >= 0 && dx2 >= 0) return 4;
if (dy1 <= 0 && dy2 <= 0) return 2;
if (dy1 >= 0 && dy2 >= 0) return 6;
// diagonal
// TODO: better positioning for diagonals
return 0;
}
}