package com.revolsys.swing.map.layer.record.style.marker; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.measure.Measure; import javax.measure.quantity.Length; import javax.swing.Icon; import javax.swing.ImageIcon; import com.revolsys.swing.map.Viewport2D; import com.revolsys.swing.map.layer.record.style.MarkerStyle; import com.revolsys.swing.map.symbol.Symbol; import com.revolsys.swing.map.symbol.SymbolLibrary; public class ShapeMarker extends AbstractMarker { private static final Map<String, Shape> SHAPES = new TreeMap<>(); static { final SymbolLibrary symbolLibrary = SymbolLibrary.newSymbolLibrary("shapes", "Shapes"); for (final Method method : ShapeMarker.class.getDeclaredMethods()) { final String methodName = method.getName(); if (Modifier.isStatic(method.getModifiers())) { if (method.getReturnType().equals(Shape.class)) { try { final Shape shape = (Shape)method.invoke(null, 1); SHAPES.put(methodName, shape); symbolLibrary.addSymbolShape(methodName); } catch (final Throwable e) { e.printStackTrace(); } } } SHAPES.put("rectangle", square(1)); SHAPES.put("ellipse", circle(1)); } } /** * Get an arrow shape pointing right for the size of the graphic. * * @return The shape. */ public static Shape arrow(final double size) { final GeneralPath path = new GeneralPath(); path.moveTo(0, size); path.lineTo(size, size * .5); path.lineTo(0, 0); return path; } public static Shape circle(final double size) { return new Ellipse2D.Double(0, 0, size, size); } public static Shape cross(final double size) { final GeneralPath path = new GeneralPath(); path.moveTo(size / 3, size); path.lineTo(size * 2 / 3, size); path.lineTo(size * 2 / 3, size * 2 / 3); path.lineTo(size, size * 2 / 3); path.lineTo(size, size / 3); path.lineTo(size * 2 / 3, size / 3); path.lineTo(size * 2 / 3, 0); path.lineTo(size / 3, 0); path.lineTo(size / 3, size / 3); path.lineTo(0, size / 3); path.lineTo(0, size * 2 / 3); path.lineTo(size / 3, size * 2 / 3); path.closePath(); return path; } public static Shape crossLine(final double size) { final GeneralPath path = new GeneralPath(); path.moveTo(size / 2, size); path.lineTo(size / 2, 0); path.moveTo(size, size / 2); path.lineTo(0, size / 2); return path; } public static Shape diamond(final double size) { final GeneralPath path = new GeneralPath(); path.moveTo(size / 2, 0); path.lineTo(size, size / 2); path.lineTo(size / 2, size); path.lineTo(0, size / 2); path.closePath(); return path; } public static List<ShapeMarker> getMarkers() { final List<ShapeMarker> markers = new ArrayList<>(); for (final String markerName : SHAPES.keySet()) { final ShapeMarker marker = new ShapeMarker(markerName); markers.add(marker); } return markers; } public static void init() { } /** * Get a solid arrow shape pointing right for the size of the graphic. * * @return The shape. */ public static Shape solidArrow(final double size) { final GeneralPath path = new GeneralPath(); path.moveTo(0, size); path.lineTo(size, size * .5); path.lineTo(0, 0); path.closePath(); return path; } public static Shape square(final double size) { return new Rectangle2D.Double(0, 0, size, size); } public static Shape star(final double size) { final GeneralPath path = new GeneralPath(); path.moveTo(size / 2, 0); path.lineTo(size * 0.64, size / 3); path.lineTo(size, size / 3); path.lineTo(size * .7, size * .57); path.lineTo(size * .8, size * .9); path.lineTo(size / 2, size * .683); path.lineTo(size * .2, size * .9); path.lineTo(size * .3, size * .57); path.lineTo(0, size / 3); path.lineTo(size * 0.36, size / 3); path.closePath(); return path; } public static Shape triangle(final double size) { final GeneralPath path = new GeneralPath(); path.moveTo(0, size); path.lineTo(size / 2, 0); path.lineTo(size, size); path.closePath(); return path; } /** * Get an X shape for the size of the graphic. * * @return The shape. */ public static Shape x(final double size) { final GeneralPath path = new GeneralPath(); path.moveTo(size * .25, size); path.lineTo(size * .5, size * .75); path.lineTo(size * .75, size); path.lineTo(size, size * .75); path.lineTo(size * .75, size * .5); path.lineTo(size, size * .25); path.lineTo(size * .75, 0); path.lineTo(size * .5, size * .25); path.lineTo(size * .25, 0); path.lineTo(0, size * .25); path.lineTo(size * .25, size * .5); path.lineTo(0, size * .75); path.closePath(); return path; } public static Shape xLine(final double size) { final GeneralPath path = new GeneralPath(); path.moveTo(0, 0); path.lineTo(size, size); path.moveTo(0, size); path.lineTo(size, 0); return path; } private Shape shape; private Symbol symbol; public ShapeMarker(final Shape shape) { this.shape = shape; if (shape != null) { final Rectangle bounds = shape.getBounds(); final double width = bounds.width; final double height = bounds.height; double scale; if (width > height) { scale = 1 / width; } else { scale = 1 / height; } final AffineTransform transform = AffineTransform.getTranslateInstance(-bounds.x, -bounds.y); transform.concatenate(AffineTransform.getScaleInstance(scale, scale)); this.shape = new GeneralPath(shape).createTransformedShape(transform); } } public ShapeMarker(final String markerType) { this(SHAPES.get(markerType)); setMarkerType(markerType); if (this.getShape() == null) { throw new IllegalArgumentException("Unknown shape " + markerType); } } @Override public boolean equals(final Object object) { if (object instanceof ShapeMarker) { final ShapeMarker marker = (ShapeMarker)object; return getMarkerType().equals(marker.getMarkerType()); } else { return false; } } public Shape getShape() { return this.shape; } @Override public boolean isUseMarkerType() { return true; } @Override public Icon newIcon(final MarkerStyle style) { final Shape shape = getShape(); final AffineTransform shapeTransform = AffineTransform.getScaleInstance(15, 15); final BufferedImage image = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); final Graphics2D graphics = image.createGraphics(); graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); final Shape newShape = new GeneralPath(shape).createTransformedShape(shapeTransform); if (style.setMarkerFillStyle(null, graphics)) { graphics.fill(newShape); } if (style.setMarkerLineStyle(null, graphics)) { graphics.draw(newShape); } graphics.dispose(); return new ImageIcon(image); } @Override protected void postSetMarkerType() { final String markerType = getMarkerType(); this.symbol = SymbolLibrary.findSymbol(markerType); } @Override public void render(final Viewport2D viewport, final Graphics2D graphics, final MarkerStyle style, final double modelX, final double modelY, double orientation) { final AffineTransform savedTransform = graphics.getTransform(); try { final Measure<Length> markerWidth = style.getMarkerWidth(); final double mapWidth = Viewport2D.toDisplayValue(viewport, markerWidth); final Measure<Length> markerHeight = style.getMarkerHeight(); final double mapHeight = Viewport2D.toDisplayValue(viewport, markerHeight); final String orientationType = style.getMarkerOrientationType(); if ("none".equals(orientationType)) { orientation = 0; } translateMarker(viewport, graphics, style, modelX, modelY, mapWidth, mapHeight, orientation); final AffineTransform shapeTransform = AffineTransform.getScaleInstance(mapWidth, mapHeight); final Shape newShape = new GeneralPath(this.getShape()) .createTransformedShape(shapeTransform); if (style.setMarkerFillStyle(viewport, graphics)) { graphics.fill(newShape); } if (style.setMarkerLineStyle(viewport, graphics)) { graphics.draw(newShape); } } finally { graphics.setTransform(savedTransform); } } @Override public String toString() { if (this.symbol == null) { return super.toString(); } else { return this.symbol.getTitle(); } } }