source: osm/applications/editors/josm/plugins/seachart/src/render/Renderer.java@ 35392

Last change on this file since 35392 was 35392, checked in by malcolmh, 5 years ago

use XML DOM to parse OSM files

File size: 38.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package render;
3
4import java.awt.BasicStroke;
5import java.awt.Color;
6import java.awt.Font;
7import java.awt.Graphics2D;
8import java.awt.Rectangle;
9import java.awt.RenderingHints;
10import java.awt.TexturePaint;
11import java.awt.font.FontRenderContext;
12import java.awt.font.GlyphVector;
13import java.awt.geom.AffineTransform;
14import java.awt.geom.Arc2D;
15import java.awt.geom.Ellipse2D;
16import java.awt.geom.GeneralPath;
17import java.awt.geom.Line2D;
18import java.awt.geom.Path2D;
19import java.awt.geom.Point2D;
20import java.awt.geom.Rectangle2D;
21import java.awt.geom.RoundRectangle2D;
22import java.awt.image.AffineTransformOp;
23import java.awt.image.BufferedImage;
24import java.util.ArrayList;
25
26import s57.S57map;
27import s57.S57map.Feature;
28import s57.S57map.GeomIterator;
29import s57.S57map.Pflag;
30import s57.S57map.Snode;
31import s57.S57val.UniHLU;
32import symbols.Areas;
33import symbols.Symbols;
34import symbols.Symbols.Caption;
35import symbols.Symbols.Delta;
36import symbols.Symbols.Form;
37import symbols.Symbols.Handle;
38import symbols.Symbols.Instr;
39import symbols.Symbols.LineStyle;
40import symbols.Symbols.Scheme;
41import symbols.Symbols.SubSymbol;
42import symbols.Symbols.Symbol;
43
44/**
45 * @author Malcolm Herring
46 */
47public final class Renderer {
48 private Renderer() {
49 // Hide default constructor for utilities classes
50 }
51
52 public static final double[] symbolScale = {
53 256.0, 128.0, 64.0, 32.0, 16.0, 8.0, 4.0, 2.0, 1.0, 0.61, 0.372, 0.227, 0.138, 0.0843, 0.0514, 0.0313, 0.0191, 0.0117, 0.007};
54
55 public enum LabelStyle { NONE, RRCT, RECT, ELPS, CIRC, VCLR, PCLR, HCLR }
56
57 static ChartContext context;
58 static S57map map;
59 static double sScale;
60 static Graphics2D g2;
61 static int zoom;
62
63 public static void reRender(Graphics2D g, Rectangle rect, int z, double factor, S57map m, ChartContext c) {
64 g2 = g;
65 zoom = z;
66 context = c;
67 map = m;
68 sScale = symbolScale[zoom] * factor;
69 if (map != null) {
70 if (context.clip()) {
71 Point2D tl = context.getPoint(new Snode(map.bounds.maxlat, map.bounds.minlon));
72 Point2D br = context.getPoint(new Snode(map.bounds.minlat, map.bounds.maxlon));
73 g2.clip(new Rectangle2D.Double(tl.getX(), tl.getY(), (br.getX() - tl.getX()), (br.getY() - tl.getY())));
74 }
75 g2.setBackground(context.background(map));
76 g2.clearRect(rect.x, rect.y, rect.width, rect.height);
77 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
78 g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
79 g2.setStroke(new BasicStroke(0, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER));
80 do {} while (!Rules.rules());
81 }
82 grid();
83 }
84
85 public static void symbol(Symbol symbol) {
86 Point2D point = context.getPoint(Rules.feature.geom.centre);
87 Symbols.drawSymbol(g2, symbol, sScale, point.getX(), point.getY(), null, null);
88 }
89
90 public static void symbol(Symbol symbol, Scheme scheme) {
91 Point2D point = context.getPoint(Rules.feature.geom.centre);
92 Symbols.drawSymbol(g2, symbol, sScale, point.getX(), point.getY(), scheme, null);
93 }
94
95 public static void symbol(Symbol symbol, Delta delta) {
96 Point2D point = context.getPoint(Rules.feature.geom.centre);
97 Symbols.drawSymbol(g2, symbol, sScale, point.getX(), point.getY(), null, delta);
98 }
99
100 public static void symbol(Symbol symbol, Scheme scheme, Delta delta) {
101 Point2D point = context.getPoint(Rules.feature.geom.centre);
102 Symbols.drawSymbol(g2, symbol, sScale, point.getX(), point.getY(), scheme, delta);
103 }
104
105 public static void cluster(ArrayList<Symbol> symbols) {
106 Rectangle2D.Double bbox = null;
107 if (symbols.size() > 4) {
108 for (Instr instr : symbols.get(0)) {
109 if (instr.type == Form.BBOX) {
110 bbox = (Rectangle2D.Double) instr.params;
111 break;
112 }
113 }
114 if (bbox == null) return;
115 }
116 switch (symbols.size()) {
117 case 1:
118 symbol(symbols.get(0), new Delta(Handle.CC, new AffineTransform()));
119 break;
120 case 2:
121 symbol(symbols.get(0), new Delta(Handle.RC, new AffineTransform()));
122 symbol(symbols.get(1), new Delta(Handle.LC, new AffineTransform()));
123 break;
124 case 3:
125 symbol(symbols.get(0), new Delta(Handle.BC, new AffineTransform()));
126 symbol(symbols.get(1), new Delta(Handle.TR, new AffineTransform()));
127 symbol(symbols.get(2), new Delta(Handle.TL, new AffineTransform()));
128 break;
129 case 4:
130 symbol(symbols.get(0), new Delta(Handle.BR, new AffineTransform()));
131 symbol(symbols.get(1), new Delta(Handle.BL, new AffineTransform()));
132 symbol(symbols.get(2), new Delta(Handle.TR, new AffineTransform()));
133 symbol(symbols.get(3), new Delta(Handle.TL, new AffineTransform()));
134 break;
135 case 5:
136 symbol(symbols.get(0), new Delta(Handle.BR, new AffineTransform()));
137 symbol(symbols.get(1), new Delta(Handle.BL, new AffineTransform()));
138 symbol(symbols.get(2), new Delta(Handle.TR, AffineTransform.getTranslateInstance(-bbox.width/2, 0)));
139 symbol(symbols.get(3), new Delta(Handle.TC, new AffineTransform()));
140 symbol(symbols.get(4), new Delta(Handle.TL, AffineTransform.getTranslateInstance(bbox.width/2, 0)));
141 break;
142 case 6:
143 symbol(symbols.get(0), new Delta(Handle.BR, AffineTransform.getTranslateInstance(-bbox.width/2, 0)));
144 symbol(symbols.get(1), new Delta(Handle.BC, new AffineTransform()));
145 symbol(symbols.get(2), new Delta(Handle.BL, AffineTransform.getTranslateInstance(bbox.width/2, 0)));
146 symbol(symbols.get(3), new Delta(Handle.TR, AffineTransform.getTranslateInstance(-bbox.width/2, 0)));
147 symbol(symbols.get(4), new Delta(Handle.TC, new AffineTransform()));
148 symbol(symbols.get(5), new Delta(Handle.TL, AffineTransform.getTranslateInstance(bbox.width/2, 0)));
149 break;
150 case 7:
151 symbol(symbols.get(0), new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -bbox.height/2)));
152 symbol(symbols.get(1), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-bbox.width/2, 0)));
153 symbol(symbols.get(2), new Delta(Handle.CC, new AffineTransform()));
154 symbol(symbols.get(3), new Delta(Handle.LC, AffineTransform.getTranslateInstance(bbox.width/2, 0)));
155 symbol(symbols.get(4), new Delta(Handle.TR, AffineTransform.getTranslateInstance(-bbox.width/2, bbox.height/2)));
156 symbol(symbols.get(5), new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, bbox.height/2)));
157 symbol(symbols.get(6), new Delta(Handle.TL, AffineTransform.getTranslateInstance(bbox.width/2, bbox.height/2)));
158 break;
159 case 8:
160 symbol(symbols.get(0), new Delta(Handle.BR, AffineTransform.getTranslateInstance(0, -bbox.height/2)));
161 symbol(symbols.get(1), new Delta(Handle.BL, AffineTransform.getTranslateInstance(0, -bbox.height/2)));
162 symbol(symbols.get(2), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-bbox.width/2, 0)));
163 symbol(symbols.get(3), new Delta(Handle.CC, new AffineTransform()));
164 symbol(symbols.get(4), new Delta(Handle.LC, AffineTransform.getTranslateInstance(bbox.width/2, 0)));
165 symbol(symbols.get(5), new Delta(Handle.TR, AffineTransform.getTranslateInstance(-bbox.width/2, bbox.height/2)));
166 symbol(symbols.get(6), new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, bbox.height/2)));
167 symbol(symbols.get(7), new Delta(Handle.TL, AffineTransform.getTranslateInstance(bbox.width/2, bbox.height/2)));
168 break;
169 case 9:
170 symbol(symbols.get(0), new Delta(Handle.BR, AffineTransform.getTranslateInstance(-bbox.width/2, -bbox.height/2)));
171 symbol(symbols.get(1), new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -bbox.height/2)));
172 symbol(symbols.get(2), new Delta(Handle.BL, AffineTransform.getTranslateInstance(bbox.width/2, -bbox.height/2)));
173 symbol(symbols.get(3), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-bbox.width/2, 0)));
174 symbol(symbols.get(4), new Delta(Handle.CC, new AffineTransform()));
175 symbol(symbols.get(5), new Delta(Handle.LC, AffineTransform.getTranslateInstance(bbox.width/2, 0)));
176 symbol(symbols.get(6), new Delta(Handle.TR, AffineTransform.getTranslateInstance(-bbox.width/2, bbox.height/2)));
177 symbol(symbols.get(7), new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, bbox.height/2)));
178 symbol(symbols.get(8), new Delta(Handle.TL, AffineTransform.getTranslateInstance(bbox.width/2, bbox.height/2)));
179 break;
180 }
181 }
182
183 private static Rectangle2D.Double symbolSize(Symbol symbol) {
184 Symbol ssymb = symbol;
185 while (ssymb != null) {
186 for (Instr item : symbol) {
187 if (item.type == Form.BBOX) {
188 return (Rectangle2D.Double) item.params;
189 }
190 if (item.type == Form.SYMB) {
191 ssymb = ((SubSymbol) item.params).instr;
192 break;
193 }
194 }
195 if (ssymb == symbol)
196 break;
197 }
198 return null;
199 }
200
201 public static void lineSymbols(Symbol prisymb, double space, Symbol secsymb, Symbol tersymb, int ratio, Color col) {
202 if ((Rules.feature.geom.prim == Pflag.NOSP) || (Rules.feature.geom.prim == Pflag.POINT))
203 return;
204 Rectangle2D.Double prect = symbolSize(prisymb);
205 Rectangle2D.Double srect = symbolSize(secsymb);
206 Rectangle2D.Double trect = symbolSize(tersymb);
207 if (srect == null)
208 ratio = 0;
209 if (prect != null) {
210 double psize = Math.abs(prect.getY()) * sScale;
211 double ssize = (srect != null) ? Math.abs(srect.getY()) * sScale : 0;
212 double tsize = (trect != null) ? Math.abs(srect.getY()) * sScale : 0;
213 Point2D prev = new Point2D.Double();
214 Point2D next = new Point2D.Double();
215 Point2D curr = new Point2D.Double();
216 Point2D succ = new Point2D.Double();
217 boolean gap = true;
218 boolean piv = false;
219 double len = 0;
220 double angle = 0;
221 int stcount = ratio;
222 boolean stflag = false;
223 Symbol symbol = prisymb;
224 GeomIterator git = map.new GeomIterator(Rules.feature.geom);
225 while (git.hasComp()) {
226 git.nextComp();
227 boolean first = true;
228 while (git.hasEdge()) {
229 git.nextEdge();
230 while (git.hasNode()) {
231 Snode node = git.next();
232 if (node == null) continue;
233 prev = next;
234 next = context.getPoint(node);
235 angle = Math.atan2(next.getY() - prev.getY(), next.getX() - prev.getX());
236 piv = true;
237 if (first) {
238 curr = succ = next;
239 gap = (space > 0);
240 stcount = ratio - 1;
241 symbol = prisymb;
242 len = gap ? psize * space * 0.5 : psize;
243 first = false;
244 } else {
245 while (curr.distance(next) >= len) {
246 if (piv) {
247 double rem = len;
248 double s = prev.distance(next);
249 double p = curr.distance(prev);
250 if ((s > 0) && (p > 0)) {
251 double n = curr.distance(next);
252 double theta = Math.acos((s * s + p * p - n * n) / 2 / s / p);
253 double phi = Math.asin(p / len * Math.sin(theta));
254 rem = len * Math.sin(Math.PI - theta - phi) / Math.sin(theta);
255 }
256 succ = new Point2D.Double(prev.getX() + (rem * Math.cos(angle)), prev.getY() + (rem * Math.sin(angle)));
257 piv = false;
258 } else {
259 succ = new Point2D.Double(curr.getX() + (len * Math.cos(angle)), curr.getY() + (len * Math.sin(angle)));
260 }
261 if (!gap) {
262 Symbols.drawSymbol(g2, symbol, sScale, curr.getX(), curr.getY(), new Scheme(col),
263 new Delta(Handle.BC, AffineTransform.getRotateInstance(
264 Math.atan2((succ.getY() - curr.getY()), (succ.getX() - curr.getX())) + Math.toRadians(90))));
265 }
266 if (space > 0)
267 gap = !gap;
268 curr = succ;
269 len = gap ? (psize * space) : (--stcount == 0) ? (stflag ? tsize : ssize) : psize;
270 if (stcount == 0) {
271 symbol = stflag ? tersymb : secsymb;
272 if (trect != null)
273 stflag = !stflag;
274 stcount = ratio;
275 } else {
276 symbol = prisymb;
277 }
278 }
279 }
280 }
281 }
282 }
283 }
284 }
285
286 public static void lineVector(LineStyle style) {
287 Path2D.Double p = new Path2D.Double();
288 p.setWindingRule(GeneralPath.WIND_EVEN_ODD);
289 Point2D point;
290 GeomIterator git = map.new GeomIterator(Rules.feature.geom);
291 while (git.hasComp()) {
292 git.nextComp();
293 boolean first = true;
294 while (git.hasEdge()) {
295 git.nextEdge();
296 point = context.getPoint(git.next());
297 if (first) {
298 p.moveTo(point.getX(), point.getY());
299 first = false;
300 } else {
301 p.lineTo(point.getX(), point.getY());
302 }
303 while (git.hasNode()) {
304 Snode node = git.next();
305 if (node == null) continue;
306 point = context.getPoint(node);
307 p.lineTo(point.getX(), point.getY());
308 }
309 }
310 }
311 if ((style.fill != null) && (Rules.feature.geom.prim == Pflag.AREA)) {
312 g2.setPaint(style.fill);
313 g2.fill(p);
314 }
315 if (style.line != null) {
316 if (style.dash != null) {
317 float[] dash = new float[style.dash.length];
318 System.arraycopy(style.dash, 0, dash, 0, style.dash.length);
319 for (int i = 0; i < style.dash.length; i++) {
320 dash[i] *= (float) sScale;
321 }
322 g2.setStroke(new BasicStroke((float) (style.width * sScale), BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1, dash, 0));
323 } else {
324 g2.setStroke(new BasicStroke((float) (style.width * sScale), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
325 }
326 g2.setPaint(style.line);
327 g2.draw(p);
328 }
329 }
330
331 public static void grid() {
332 if (context.grid() > 0) {
333 LineStyle style = new LineStyle(Color.black, (float)2.0);
334 double nspan = 60 * Math.toDegrees(map.bounds.maxlon - map.bounds.minlon) / context.grid();
335 double mult = 1.0;
336 if (nspan < 1.0) {
337 do {
338 nspan *= 10.0;
339 mult *= 10.0;
340 } while (nspan < 1.0);
341 } else if (nspan > 10.0){
342 do {
343 nspan /= 10.0;
344 mult /= 10.0;
345 } while (nspan > 10.0);
346 }
347 if (nspan < 2.0) nspan = 1.0;
348 else if (nspan < 5.0) nspan = 2.0;
349 else nspan = 5.0;
350 nspan = nspan / mult / 60.0;
351 double left = Math.toDegrees(map.bounds.minlon) + 180.0;
352 left = Math.ceil(left / nspan);
353 left = Math.toRadians((left * nspan) - 180.0);
354 g2.setStroke(new BasicStroke((float) (style.width * sScale), BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
355 Path2D.Double p = new Path2D.Double();
356 for (double lon = left; lon < map.bounds.maxlon; lon += Math.toRadians(nspan)) {
357 Point2D point = context.getPoint(new Snode(map.bounds.maxlat, lon));
358 p.moveTo(point.getX(), point.getY());
359 point = context.getPoint(new Snode(map.bounds.minlat, lon));
360 p.lineTo(point.getX(), point.getY());
361 double deg = Math.toDegrees(lon);
362 String ew = (deg < 0) ? "W" : "E";
363 deg = Math.abs(deg);
364 String dstr = String.format("%03d°", (int)Math.floor(deg));
365 double min = (deg - Math.floor(deg)) * 60.0;
366 String mstr = String.format("%05.2f'%s", min, ew);
367 Symbol label = new Symbol();
368 if (point.getX() > 500.0) {
369 label.add(new Instr(Form.TEXT, new Caption(dstr, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.BR, AffineTransform.getTranslateInstance(-10, -20)))));
370 label.add(new Instr(Form.TEXT, new Caption(mstr, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.BL, AffineTransform.getTranslateInstance(20, 0)))));
371 Symbols.drawSymbol(g2, label, sScale, point.getX(), point.getY(), null, null);
372 }
373 }
374 g2.setPaint(style.line);
375 g2.draw(p);
376 double tspan = 60 * Math.toDegrees(map.bounds.maxlat - map.bounds.minlat) / context.grid();
377 mult = 1.0;
378 if (tspan < 1.0) {
379 do {
380 tspan *= 10.0;
381 mult *= 10.0;
382 } while (tspan < 1.0);
383 } else if (tspan > 10.0){
384 do {
385 tspan /= 10.0;
386 mult /= 10.0;
387 } while (tspan > 10.0);
388 }
389 if (tspan < 2.0) tspan = 1.0;
390 else if (tspan < 5.0) tspan = 2.0;
391 else tspan = 5.0;
392 tspan = tspan / mult / 60.0;
393 double bottom = Math.toDegrees(map.bounds.minlat) + 90.0;
394 bottom = Math.ceil(bottom / tspan);
395 bottom = Math.toRadians((bottom * tspan) - 90.0);
396 g2.setStroke(new BasicStroke((float) (style.width * sScale), BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
397 p = new Path2D.Double();
398 for (double lat = bottom; lat < map.bounds.maxlat; lat += Math.toRadians(tspan)) {
399 Point2D point = context.getPoint(new Snode(lat, map.bounds.maxlon));
400 p.moveTo(point.getX(), point.getY());
401 point = context.getPoint(new Snode(lat, map.bounds.minlon));
402 p.lineTo(point.getX(), point.getY());
403 double deg = Math.toDegrees(lat);
404 String ns = (deg < 0) ? "S" : "N";
405 deg = Math.abs(deg);
406 String dstr = String.format("%02d°%s", (int)Math.floor(deg), ns);
407 double min = (deg - Math.floor(deg)) * 60.0;
408 String mstr = String.format("%05.2f'", min);
409 Symbol label = new Symbol();
410 label.add(new Instr(Form.TEXT, new Caption(dstr, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.BL, AffineTransform.getTranslateInstance(10, -10)))));
411 label.add(new Instr(Form.TEXT, new Caption(mstr, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.BL, AffineTransform.getTranslateInstance(0, 50)))));
412 Symbols.drawSymbol(g2, label, sScale, point.getX(), point.getY(), null, null);
413 }
414 g2.setPaint(style.line);
415 g2.draw(p);
416/* Symbol legend = new Symbol();
417 legend.add(new Instr(Form.BBOX, new Rectangle2D.Double(0, 0, 900, 300)));
418 Path2D.Double path = new Path2D.Double(); path.moveTo(0, 0); path.lineTo(900, 0); path.lineTo(900, 300); path.lineTo(0, 300); path.closePath();
419 legend.add(new Instr(Form.FILL, Color.white));
420 legend.add(new Instr(Form.PGON, path));
421 legend.add(new Instr(Form.TEXT, new Caption("© OpenStreetMap contributors", new Font("Arial", Font.PLAIN, 25), Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(450, 300)))));
422 legend.add(new Instr(Form.TEXT, new Caption("Mercator projection", new Font("Arial", Font.PLAIN, 30), Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(450, 250)))));
423 Point2D point = context.getPoint(new Snode(map.bounds.minlat, map.bounds.minlon));
424 Symbols.drawSymbol(g2, legend, sScale, point.getX(), point.getY(), null, new Delta(Handle.BL, AffineTransform.getTranslateInstance(0, 0)));
425 legend = new Symbol();
426 legend.add(new Instr(Form.BBOX, new Rectangle2D.Double(0, 0, 900, 300)));
427 legend.add(new Instr(Form.TEXT, new Caption("Mercator projection", new Font("Arial", Font.PLAIN, 30), Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(450, 250)))));
428 point = context.getPoint(new Snode(map.bounds.minlat, map.bounds.minlon));
429 Symbols.drawSymbol(g2, legend, sScale, point.getX(), point.getY(), null, new Delta(Handle.BL, AffineTransform.getTranslateInstance(0, 0)));
430*/ }
431 }
432
433 public static void lineCircle(LineStyle style, double radius, UniHLU units) {
434 switch (units) {
435 case HLU_FEET:
436 radius /= 6076;
437 break;
438 case HLU_KMTR:
439 radius /= 1.852;
440 break;
441 case HLU_HMTR:
442 radius /= 18.52;
443 break;
444 case HLU_SMIL:
445 radius /= 1.15078;
446 break;
447 case HLU_NMIL:
448 break;
449 default:
450 radius /= 1852;
451 break;
452 }
453 radius *= context.mile(Rules.feature);
454 Symbol circle = new Symbol();
455 if (style.fill != null) {
456 circle.add(new Instr(Form.FILL, style.fill));
457 circle.add(new Instr(Form.RSHP, new Ellipse2D.Double(-radius, -radius, radius*2, radius*2)));
458 }
459 circle.add(new Instr(Form.FILL, style.line));
460 circle.add(new Instr(Form.STRK, new BasicStroke(style.width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1, style.dash, 0)));
461 circle.add(new Instr(Form.ELPS, new Ellipse2D.Double(-radius, -radius, radius*2, radius*2)));
462 Point2D point = context.getPoint(Rules.feature.geom.centre);
463 Symbols.drawSymbol(g2, circle, 1, point.getX(), point.getY(), null, null);
464 }
465
466 public static void fillPattern(BufferedImage image) {
467 Path2D.Double p = new Path2D.Double();
468 p.setWindingRule(GeneralPath.WIND_EVEN_ODD);
469 Point2D point;
470 switch (Rules.feature.geom.prim) {
471 case POINT:
472 point = context.getPoint(Rules.feature.geom.centre);
473 g2.drawImage(image, new AffineTransformOp(AffineTransform.getScaleInstance(sScale, sScale), AffineTransformOp.TYPE_NEAREST_NEIGHBOR),
474 (int) (point.getX() - (50 * sScale)), (int) (point.getY() - (50 * sScale)));
475 break;
476 case AREA:
477 GeomIterator git = map.new GeomIterator(Rules.feature.geom);
478 while (git.hasComp()) {
479 git.nextComp();
480 boolean newComp = true;
481 while (git.hasEdge()) {
482 git.nextEdge();
483 point = context.getPoint(git.next());
484 if (newComp) {
485 p.moveTo(point.getX(), point.getY());
486 newComp = false;
487 } else {
488 p.lineTo(point.getX(), point.getY());
489 }
490 while (git.hasNode()) {
491 Snode node = git.next();
492 if (node == null) continue;
493 point = context.getPoint(node);
494 p.lineTo(point.getX(), point.getY());
495 }
496 }
497 }
498 g2.setPaint(new TexturePaint(image, new Rectangle(0, 0, 1 + (int) (300 * sScale), 1 + (int) (300 * sScale))));
499 g2.fill(p);
500 break;
501 default:
502 break;
503 }
504 }
505
506 public static void labelText(String str, Font font, Color tc) {
507 labelText(str, font, tc, LabelStyle.NONE, null, null, null);
508 }
509
510 public static void labelText(String str, Font font, Color tc, Delta delta) {
511 labelText(str, font, tc, LabelStyle.NONE, null, null, delta);
512 }
513
514 public static void labelText(String str, Font font, Color tc, LabelStyle style, Color fg) {
515 labelText(str, font, tc, style, fg, null, null);
516 }
517
518 public static void labelText(String str, Font font, Color tc, LabelStyle style, Color fg, Color bg) {
519 labelText(str, font, tc, style, fg, bg, null);
520 }
521
522 public static void labelText(String str, Font font, Color tc, LabelStyle style, Color fg, Delta delta) {
523 labelText(str, font, tc, style, fg, null, delta);
524 }
525
526 public static void labelText(String str, Font font, Color tc, LabelStyle style, Color fg, Color bg, Delta delta) {
527 if (delta == null) delta = new Delta(Handle.CC);
528 if (bg == null) bg = new Color(0x00000000, true);
529 if (str == null || str.isEmpty()) str = " ";
530 FontRenderContext frc = g2.getFontRenderContext();
531 GlyphVector gv = font.deriveFont((float) font.getSize()).createGlyphVector(frc, str.equals(" ") ? "M" : str);
532 Rectangle2D bounds = gv.getVisualBounds();
533 double width = bounds.getWidth();
534 double height = bounds.getHeight();
535 Symbol label = new Symbol();
536 double lx, ly, tx, ty;
537 switch (style) {
538 case RRCT:
539 width += height * 1.0;
540 height *= 1.5;
541 if (width < height) width = height;
542 lx = -width / 2;
543 ly = -height / 2;
544 tx = lx + (height * 0.34);
545 ty = ly + (height * 0.17);
546 label.add(new Instr(Form.BBOX, new Rectangle2D.Double(lx, ly, width, height)));
547 label.add(new Instr(Form.FILL, bg));
548 label.add(new Instr(Form.RSHP, new RoundRectangle2D.Double(lx, ly, width, height, height, height)));
549 label.add(new Instr(Form.FILL, fg));
550 label.add(new Instr(Form.STRK, new BasicStroke(1 + (int) (height/10), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
551 label.add(new Instr(Form.RRCT, new RoundRectangle2D.Double(lx, ly, width, height, height, height)));
552 break;
553 case VCLR:
554 width += height * 1.0;
555 height *= 2.0;
556 if (width < height) width = height;
557 lx = -width / 2;
558 ly = -height / 2;
559 tx = lx + (height * 0.27);
560 ty = ly + (height * 0.25);
561 label.add(new Instr(Form.BBOX, new Rectangle2D.Double(lx, ly, width, height)));
562 label.add(new Instr(Form.FILL, bg));
563 label.add(new Instr(Form.RSHP, new RoundRectangle2D.Double(lx, ly, width, height, height, height)));
564 label.add(new Instr(Form.FILL, fg));
565 int sw = 1 + (int) (height/10);
566 double po = sw / 2;
567 label.add(new Instr(Form.STRK, new BasicStroke(sw, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
568 Path2D.Double p = new Path2D.Double(); p.moveTo(-height*0.2, -ly-po);
569 p.lineTo(height*0.2, -ly-po); p.moveTo(0, -ly-po); p.lineTo(0, -ly-po-(height*0.15));
570 p.moveTo(-height*0.2, ly+po); p.lineTo((height*0.2), ly+po); p.moveTo(0, ly+po); p.lineTo(0, ly+po+(height*0.15));
571 label.add(new Instr(Form.PLIN, p));
572 break;
573 case PCLR:
574 width += height * 1.0;
575 height *= 2.0;
576 if (width < height) width = height;
577 lx = -width / 2;
578 ly = -height / 2;
579 tx = lx + (height * 0.27);
580 ty = ly + (height * 0.25);
581 label.add(new Instr(Form.BBOX, new Rectangle2D.Double(lx, ly, width, height)));
582 label.add(new Instr(Form.FILL, bg));
583 label.add(new Instr(Form.RSHP, new RoundRectangle2D.Double(lx, ly, width, height, height, height)));
584 label.add(new Instr(Form.FILL, fg));
585 sw = 1 + (int) (height/10);
586 po = sw / 2;
587 label.add(new Instr(Form.STRK, new BasicStroke(sw, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
588 p = new Path2D.Double();
589 p.moveTo(-height*0.2, -ly-po);
590 p.lineTo(height*0.2, -ly-po);
591 p.moveTo(0, -ly-po);
592 p.lineTo(0, -ly-po-(height*0.15));
593 p.moveTo(-height*0.2, ly+po);
594 p.lineTo((height*0.2), ly+po);
595 p.moveTo(0, ly+po);
596 p.lineTo(0, ly+po+(height*0.15));
597 label.add(new Instr(Form.PLIN, p));
598 label.add(new Instr(Form.SYMB, new Symbols.SubSymbol(Areas.CableFlash, 1, 0, 0, null,
599 new Delta(Handle.CC, new AffineTransform(0, -1, 1, 0, -width/2, 0)))));
600 label.add(new Instr(Form.SYMB, new Symbols.SubSymbol(Areas.CableFlash, 1, 0, 0, null,
601 new Delta(Handle.CC, new AffineTransform(0, -1, 1, 0, width/2, 0)))));
602 break;
603 case HCLR:
604 width += height * 1.5;
605 height *= 1.5;
606 if (width < height) width = height;
607 lx = -width / 2;
608 ly = -height / 2;
609 tx = lx + (height * 0.5);
610 ty = ly + (height * 0.17);
611 label.add(new Instr(Form.BBOX, new Rectangle2D.Double(lx, ly, width, height)));
612 label.add(new Instr(Form.FILL, bg));
613 label.add(new Instr(Form.RSHP, new RoundRectangle2D.Double(lx, ly, width, height, height, height)));
614 label.add(new Instr(Form.FILL, fg));
615 sw = 1 + (int) (height/10);
616 double vo = height / 4;
617 label.add(new Instr(Form.STRK, new BasicStroke(sw, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
618 p = new Path2D.Double();
619 p.moveTo(-width*0.4-sw, -ly-vo);
620 p.lineTo(-width*0.4-sw, ly+vo);
621 p.moveTo(-width*0.4-sw, 0);
622 p.lineTo(-width*0.4+sw, 0);
623 p.moveTo(width*0.4+sw, -ly-vo);
624 p.lineTo(width*0.4+sw, ly+vo);
625 p.moveTo(width*0.4-sw, 0);
626 p.lineTo(width*0.4+sw, 0);
627 label.add(new Instr(Form.PLIN, p));
628 break;
629 default:
630 lx = -width / 2;
631 ly = -height / 2;
632 tx = lx;
633 ty = ly;
634 label.add(new Instr(Form.BBOX, new Rectangle2D.Double(lx, ly, width, height)));
635 break;
636 }
637 label.add(new Instr(Form.TEXT, new Caption(str, font, tc, new Delta(Handle.TL, AffineTransform.getTranslateInstance(tx, ty)))));
638 Point2D point = context.getPoint(Rules.feature.geom.centre);
639 Symbols.drawSymbol(g2, label, sScale, point.getX(), point.getY(), null, delta);
640 }
641
642 public static void lineText(String str, Font font, Color colour, double dy) {
643 if (!str.isEmpty()) {
644 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
645 g2.setPaint(colour);
646 FontRenderContext frc = g2.getFontRenderContext();
647 GlyphVector gv = font.deriveFont(font.getSize2D() * (float) sScale).createGlyphVector(frc, str);
648 double width = gv.getVisualBounds().getWidth();
649 double height = gv.getVisualBounds().getHeight();
650 double offset = (Rules.feature.geom.length * context.mile(Rules.feature) - width) / 2;
651 if (offset > 0) {
652 Point2D before = null;
653 Point2D after = null;
654 ArrayList<Point2D> between = new ArrayList<>();
655 Point2D prev = null;
656 Point2D next = null;
657 double length = 0;
658 double lb = 0;
659 double la = 0;
660 GeomIterator git = map.new GeomIterator(Rules.feature.geom);
661 if (git.hasComp()) {
662 git.nextComp();
663 while (git.hasEdge()) {
664 git.nextEdge();
665 while (git.hasNode()) {
666 Snode node = git.next();
667 if (node == null)
668 continue;
669 prev = next;
670 next = context.getPoint(node);
671 if (prev != null)
672 length += Math.sqrt(Math.pow((next.getX() - prev.getX()), 2) + Math.pow((next.getY() - prev.getY()), 2));
673 if (length < offset) {
674 before = next;
675 lb = la = length;
676 } else if (after == null) {
677 if (length > (offset + width)) {
678 after = next;
679 la = length;
680 break;
681 } else {
682 between.add(next);
683 }
684 }
685 }
686 if (after != null)
687 break;
688 }
689 }
690 if (after != null) {
691 double angle = Math.atan2((after.getY() - before.getY()), (after.getX() - before.getX()));
692 double rotate = Math.abs(angle) < (Math.PI / 2) ? angle : angle + Math.PI;
693 Point2D mid = new Point2D.Double((before.getX() + after.getX()) / 2, (before.getY() + after.getY()) / 2);
694 Point2D centre = context.getPoint(Rules.feature.geom.centre);
695 AffineTransform pos = AffineTransform.getTranslateInstance(-dy * Math.sin(rotate), dy * Math.cos(rotate));
696 pos.rotate(rotate);
697 pos.translate((mid.getX() - centre.getX()), (mid.getY() - centre.getY()));
698 Symbol label = new Symbol();
699 label.add(new Instr(Form.BBOX, new Rectangle2D.Double((-width / 2), -height, width, height)));
700 label.add(new Instr(Form.TEXT, new Caption(str, font, colour, new Delta(Handle.BC))));
701 Symbols.drawSymbol(g2, label, sScale, centre.getX(), centre.getY(), null, new Delta(Handle.BC, pos));
702 }
703 }
704 }
705 }
706
707 public static void lightSector(Color col1, Color col2, double radius, double s1, double s2, Double dir, String str) {
708 double mid = (((s1 + s2) / 2) + (s1 > s2 ? 180 : 0)) % 360;
709 g2.setStroke(new BasicStroke((float) (3.0 * sScale), BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1,
710 new float[] {20 * (float) sScale, 20 * (float) sScale}, 0));
711 g2.setPaint(Color.black);
712 Point2D.Double centre = (Point2D.Double) context.getPoint(Rules.feature.geom.centre);
713 double radial = radius * context.mile(Rules.feature);
714 if (dir != null) {
715 g2.draw(new Line2D.Double(centre.x, centre.y, centre.x - radial * Math.sin(Math.toRadians(dir)),
716 centre.y + radial * Math.cos(Math.toRadians(dir))));
717 } else {
718 if ((s1 != 0.0) || (s2 != 360.0)) {
719 g2.draw(new Line2D.Double(centre.x, centre.y, centre.x - radial * Math.sin(Math.toRadians(s1)),
720 centre.y + radial * Math.cos(Math.toRadians(s1))));
721 g2.draw(new Line2D.Double(centre.x, centre.y, centre.x - radial * Math.sin(Math.toRadians(s2)),
722 centre.y + radial * Math.cos(Math.toRadians(s2))));
723 }
724 }
725 double arcWidth = 10.0 * sScale;
726 g2.setStroke(new BasicStroke((float) arcWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1));
727 g2.setPaint(col1);
728 g2.draw(new Arc2D.Double(centre.x - radial, centre.y - radial, 2 * radial, 2 * radial, -(s1 + 90),
729 ((s1 < s2) ? (s1 - s2) : (s1 - s2 - 360)), Arc2D.OPEN));
730 if (col2 != null) {
731 g2.setPaint(col2);
732 g2.draw(new Arc2D.Double(centre.x - radial + arcWidth, centre.y - radial + arcWidth, 2 * (radial - arcWidth),
733 2 * (radial - arcWidth), -(s1 + 90), ((s1 < s2) ? (s1 - s2) : (s1 - s2 - 360)), Arc2D.OPEN));
734 }
735 if (str != null && !str.isEmpty()) {
736 Font font = new Font("Arial", Font.PLAIN, 40);
737 double arc = (s2 > s1) ? (s2 - s1) : (s2 - s1 + 360);
738 double awidth = (Math.toRadians(arc) * radial);
739 boolean hand = ((mid > 270) || (mid < 90));
740 double phi = Math.toRadians(mid);
741 radial += 30 * sScale;
742 AffineTransform at = AffineTransform.getTranslateInstance(-radial * Math.sin(phi) / sScale, radial * Math.cos(phi) / sScale);
743 if ((font.getSize() * sScale * str.length()) < awidth) {
744 at.rotate(Math.toRadians(mid + (hand ? 0 : 180)));
745 labelText(str, font, Color.black, new Delta(Handle.CC, at));
746 } else if ((font.getSize() * sScale) < awidth) {
747 hand = (mid < 180);
748 at.rotate(Math.toRadians(mid + (hand ? -90 : 90)));
749 labelText(str, font, Color.black, hand ? new Delta(Handle.RC, at) : new Delta(Handle.LC, at));
750 }
751 if (dir != null) {
752 font = new Font("Arial", Font.PLAIN, 30);
753 str = dir + "°";
754 hand = (dir > 180);
755 phi = Math.toRadians(dir + (hand ? -0.5 : 0.5));
756 radial -= 70 * sScale;
757 at = AffineTransform.getTranslateInstance(-radial * Math.sin(phi) / sScale, radial * Math.cos(phi) / sScale);
758 at.rotate(Math.toRadians(dir + (hand ? 90 : -90)));
759 labelText(str, font, Color.black, hand ? new Delta(Handle.BR, at) : new Delta(Handle.BL, at));
760 }
761 }
762 }
763}
Note: See TracBrowser for help on using the repository browser.