source: osm/applications/editors/josm/plugins/smed2/src/seamap/Renderer.java@ 30025

Last change on this file since 30025 was 30025, checked in by malcolmh, 12 years ago

save

File size: 15.0 KB
Line 
1/* Copyright 2013 Malcolm Herring
2 *
3 * This is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License.
6 *
7 * For a copy of the GNU General Public License, see <http://www.gnu.org/licenses/>.
8 */
9
10package seamap;
11
12import java.awt.*;
13import java.awt.font.*;
14import java.awt.geom.*;
15import java.awt.image.*;
16import java.util.*;
17
18import s57.S57att.*;
19import s57.S57obj.*;
20import s57.S57val.*;
21import s57.S57val;
22import seamap.SeaMap;
23import seamap.SeaMap.*;
24import seamap.SeaMap.Area;
25import symbols.Symbols;
26import symbols.Symbols.*;
27
28public class Renderer {
29
30 public static final EnumMap<ColCOL, Color> bodyColours = new EnumMap<ColCOL, Color>(ColCOL.class);
31 static {
32 bodyColours.put(ColCOL.COL_UNK, new Color(0, true));
33 bodyColours.put(ColCOL.COL_WHT, new Color(0xffffff));
34 bodyColours.put(ColCOL.COL_BLK, new Color(0x000000));
35 bodyColours.put(ColCOL.COL_RED, new Color(0xd40000));
36 bodyColours.put(ColCOL.COL_GRN, new Color(0x00d400));
37 bodyColours.put(ColCOL.COL_BLU, Color.blue);
38 bodyColours.put(ColCOL.COL_YEL, new Color(0xffd400));
39 bodyColours.put(ColCOL.COL_GRY, Color.gray);
40 bodyColours.put(ColCOL.COL_BRN, new Color(0x8b4513));
41 bodyColours.put(ColCOL.COL_AMB, new Color(0xfbf00f));
42 bodyColours.put(ColCOL.COL_VIO, new Color(0xee82ee));
43 bodyColours.put(ColCOL.COL_ORG, Color.orange);
44 bodyColours.put(ColCOL.COL_MAG, new Color(0xf000f0));
45 bodyColours.put(ColCOL.COL_PNK, Color.pink);
46 }
47
48 public static final EnumMap<ColPAT, Patt> pattMap = new EnumMap<ColPAT, Patt>(ColPAT.class);
49 static {
50 pattMap.put(ColPAT.PAT_UNKN, Patt.Z);
51 pattMap.put(ColPAT.PAT_HORI, Patt.H);
52 pattMap.put(ColPAT.PAT_VERT, Patt.V);
53 pattMap.put(ColPAT.PAT_DIAG, Patt.D);
54 pattMap.put(ColPAT.PAT_BRDR, Patt.B);
55 pattMap.put(ColPAT.PAT_SQUR, Patt.S);
56 pattMap.put(ColPAT.PAT_CROS, Patt.C);
57 pattMap.put(ColPAT.PAT_SALT, Patt.X);
58 pattMap.put(ColPAT.PAT_STRP, Patt.H);
59 }
60
61 public static final double symbolScale[] = { 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, 0.138 };
62
63// public static final double textScale[] = { 256.0, 128.0, 64.0, 32.0, 16.0, 8.0, 4.0, 2.0, 1.0, 0.5556, 0.3086, 0.1714, 0.0953, 0.0529, 0.0294, 0.0163, 0.0091, 0.0050, 0.0028, 0.0163 };
64
65 public enum LabelStyle { NONE, RRCT, RECT, ELPS, CIRC }
66
67 static MapContext context;
68 static SeaMap map;
69 static double sScale;
70// static double tScale;
71 static Graphics2D g2;
72 static int zoom;
73
74 public static void reRender(Graphics2D g, int z, double factor, SeaMap m, MapContext c) {
75 g2 = g;
76 zoom = z;
77 context = c;
78 map = m;
79 sScale = symbolScale[zoom] * factor;
80// tScale = textScale[zoom] * factor;
81 if (map != null) {
82 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
83 g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
84 Rules.rules(map, zoom);
85 }
86 }
87
88 public static AttMap getAtts(Feature feature, Obj obj, int idx) {
89 HashMap<Integer, AttMap> objs = feature.objs.get(obj);
90 if (objs == null)
91 return null;
92 else
93 return objs.get(idx);
94 }
95
96 public static Object getAttVal(Feature feature, Obj obj, int idx, Att att) {
97 AttMap atts = getAtts(feature, obj, idx);
98 if (atts == null)
99 return S57val.nullVal(att);
100 else {
101 AttItem item = atts.get(att);
102 if (item == null)
103 return S57val.nullVal(att);
104 return item.val;
105 }
106 }
107
108 public static void symbol(Feature feature, Symbol symbol, Obj obj, Delta delta, Scheme scheme) {
109 Point2D point = context.getPoint(feature.centre);
110 if (obj == null) {
111 Symbols.drawSymbol(g2, symbol, sScale, point.getX(), point.getY(), delta, scheme);
112 } else {
113 ArrayList<Color> colours = new ArrayList<Color>();
114 for (ColCOL col : (ArrayList<ColCOL>)getAttVal(feature, obj, 0, Att.COLOUR)) {
115 colours.add(bodyColours.get(col));
116 }
117 ArrayList<Patt> patterns = new ArrayList<Patt>();
118 for(ColPAT pat: (ArrayList<ColPAT>) getAttVal(feature, obj, 0, Att.COLPAT)) {
119 patterns.add(pattMap.get(pat));
120 }
121 Symbols.drawSymbol(g2, symbol, sScale, point.getX(), point.getY(), delta, new Scheme(patterns, colours));
122 }
123 }
124
125 private static Rectangle symbolSize(Symbol symbol) {
126 Symbol ssymb = symbol;
127 while (ssymb != null) {
128 for (Instr item : symbol) {
129 if (item.type == Prim.BBOX) {
130 return (Rectangle) item.params;
131 }
132 if (item.type == Prim.SYMB) {
133 ssymb = ((SubSymbol) item.params).instr;
134 break;
135 }
136 }
137 if (ssymb == symbol)
138 break;
139 }
140 return null;
141 }
142
143 public static void lineSymbols(Feature feature, Symbol prisymb, double space, Symbol secsymb, int ratio, Color col) {
144 Area area;
145 switch (feature.flag) {
146 case LINE:
147 Edge edge = map.edges.get(feature.refs);
148 area = map.new Area();
149 area.add(map.new Bound(map.new Side(edge, true), true));
150 break;
151 case AREA:
152 area = map.areas.get(feature.refs);
153 break;
154 default:
155 return;
156 }
157 Rectangle prect = symbolSize(prisymb);
158 Rectangle srect = symbolSize(secsymb);
159 if (srect == null)
160 ratio = 0;
161 if (prect != null) {
162 double psize = Math.abs(prect.getY()) * sScale;
163 double ssize = (srect != null) ? Math.abs(srect.getY()) * sScale : 0;
164 Point2D prev = new Point2D.Double();
165 Point2D next = new Point2D.Double();
166 Point2D curr = new Point2D.Double();
167 Point2D succ = new Point2D.Double();
168 boolean gap = true;
169 boolean piv = false;
170 double len = 0;
171 double angle = 0;
172 int scount = ratio;
173 Symbol symbol = prisymb;
174 for (Bound bound : area) {
175 BoundIterator bit = map.new BoundIterator(bound);
176 boolean first = true;
177 while (bit.hasNext()) {
178 prev = next;
179 next = context.getPoint(bit.next());
180 angle = Math.atan2(next.getY() - prev.getY(), next.getX() - prev.getX());
181 piv = true;
182 if (first) {
183 curr = succ = next;
184 gap = (space > 0);
185 scount = ratio;
186 symbol = prisymb;
187 len = gap ? psize * space * 0.5 : psize;
188 first = false;
189 } else {
190 while (curr.distance(next) >= len) {
191 if (piv) {
192 double rem = len;
193 double s = prev.distance(next);
194 double p = curr.distance(prev);
195 if ((s > 0) && (p > 0)) {
196 double n = curr.distance(next);
197 double theta = Math.acos((s * s + p * p - n * n) / 2 / s / p);
198 double phi = Math.asin(p / len * Math.sin(theta));
199 rem = len * Math.sin(Math.PI - theta - phi) / Math.sin(theta);
200 }
201 succ = new Point2D.Double(prev.getX() + (rem * Math.cos(angle)), prev.getY() + (rem * Math.sin(angle)));
202 piv = false;
203 } else {
204 succ = new Point2D.Double(curr.getX() + (len * Math.cos(angle)), curr.getY() + (len * Math.sin(angle)));
205 }
206 if (!gap) {
207 Symbols.drawSymbol(g2, symbol, sScale, curr.getX(), curr.getY(),
208 new Delta(Handle.BC, AffineTransform.getRotateInstance(Math.atan2((succ.getY() - curr.getY()), (succ.getX() - curr.getX())) + Math.toRadians(90))),
209 new Scheme(col));
210 }
211 if (space > 0)
212 gap = !gap;
213 curr = succ;
214 len = gap ? (psize * space) : (--scount == 0) ? ssize : psize;
215 if (scount == 0) {
216 symbol = secsymb;
217 scount = ratio;
218 } else {
219 symbol = prisymb;
220 }
221 }
222 }
223 }
224 }
225 }
226 }
227
228 public static void lineVector(Feature feature, LineStyle style) {
229 Path2D.Double p = new Path2D.Double();
230 p.setWindingRule(GeneralPath.WIND_EVEN_ODD);
231 Point2D point;
232 switch (feature.flag) {
233 case LINE:
234 EdgeIterator eit = map.new EdgeIterator(map.edges.get(feature.refs), true);
235 point = context.getPoint(eit.next());
236 p.moveTo(point.getX(), point.getY());
237 while (eit.hasNext()) {
238 point = context.getPoint(eit.next());
239 p.lineTo(point.getX(), point.getY());
240 }
241 break;
242 case AREA:
243 for (Bound bound : map.areas.get(feature.refs)) {
244 BoundIterator bit = map.new BoundIterator(bound);
245 point = context.getPoint(bit.next());
246 p.moveTo(point.getX(), point.getY());
247 while (bit.hasNext()) {
248 point = context.getPoint(bit.next());
249 p.lineTo(point.getX(), point.getY());
250 }
251 }
252 break;
253 }
254 if (style.line != null) {
255 if (style.dash != null) {
256 float[] dash = new float[style.dash.length];
257 System.arraycopy(style.dash, 0, dash, 0, style.dash.length);
258 for (int i = 0; i < style.dash.length; i++) {
259 dash[i] *= (float) sScale;
260 }
261 g2.setStroke(new BasicStroke((float) (style.width * sScale), BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1, dash, 0));
262 } else {
263 g2.setStroke(new BasicStroke((float) (style.width * sScale), BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
264 }
265 g2.setPaint(style.line);
266 g2.draw(p);
267 }
268 if (style.fill != null) {
269 g2.setPaint(style.fill);
270 g2.fill(p);
271 }
272 }
273
274 public static void lineCircle(Feature feature, LineStyle style, double radius, UniHLU units) {
275 switch (units) {
276 case HLU_FEET:
277 radius /= 6076;
278 break;
279 case HLU_KMTR:
280 radius /= 1.852;
281 break;
282 case HLU_HMTR:
283 radius /= 18.52;
284 break;
285 case HLU_SMIL:
286 radius /= 1.15078;
287 break;
288 case HLU_NMIL:
289 break;
290 default:
291 radius /= 1852;
292 break;
293 }
294 radius *= context.mile(feature);
295 Symbol circle = new Symbol();
296 if (style.fill != null) {
297 circle.add(new Instr(Prim.FILL, style.fill));
298 circle.add(new Instr(Prim.RSHP, new Ellipse2D.Double(-radius,-radius,radius*2,radius*2)));
299 }
300 circle.add(new Instr(Prim.FILL, style.line));
301 circle.add(new Instr(Prim.STRK, new BasicStroke(style.width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1, style.dash, 0)));
302 circle.add(new Instr(Prim.ELPS, new Ellipse2D.Double(-radius,-radius,radius*2,radius*2)));
303 Point2D point = context.getPoint(feature.centre);
304 Symbols.drawSymbol(g2, circle, 1, point.getX(), point.getY(), null, null);
305 }
306
307
308 public static void fillPattern(Feature feature, BufferedImage image) {
309 Path2D.Double p = new Path2D.Double();
310 p.setWindingRule(GeneralPath.WIND_EVEN_ODD);
311 Point2D point;
312 switch (feature.flag) {
313 case POINT:
314 point = context.getPoint(feature.centre);
315 g2.drawImage(image, new AffineTransformOp(AffineTransform.getScaleInstance(sScale, sScale), AffineTransformOp.TYPE_NEAREST_NEIGHBOR),
316 (int)(point.getX() - (50 * sScale)), (int)(point.getY() - (50 * sScale)));
317 break;
318 case AREA:
319 for (Bound bound : map.areas.get(feature.refs)) {
320 BoundIterator bit = map.new BoundIterator(bound);
321 point = context.getPoint(bit.next());
322 p.moveTo(point.getX(), point.getY());
323 while (bit.hasNext()) {
324 point = context.getPoint(bit.next());
325 p.lineTo(point.getX(), point.getY());
326 }
327 }
328 g2.setPaint(new TexturePaint(image, new Rectangle(0, 0, 1 + (int)(100 * sScale), 1 + (int)(100 * sScale))));
329 g2.fill(p);
330 break;
331 }
332 }
333
334 public static void labelText(Feature feature, String str, Font font, LabelStyle style, Color fg, Color bg, Delta delta) {
335 if (delta == null) delta = new Delta(Handle.CC, null);
336 if (bg == null) bg = new Color(0x00000000, true);
337 if (str == null) str = " ";
338 if (str.isEmpty()) str = " ";
339 FontRenderContext frc = g2.getFontRenderContext();
340 GlyphVector gv = font.deriveFont((float)(font.getSize())).createGlyphVector(frc, str.equals(" ") ? "M" : str);
341 Rectangle2D bounds = gv.getVisualBounds();
342 double width = bounds.getWidth();
343 double height = bounds.getHeight();
344 double dx = 0.25 * width;
345 double dy = 0.25 * height;
346 switch (delta.h) {
347 case CC:
348 dx += width / 2.0;
349 dy += height / 2.0;
350 break;
351 case TL:
352 dx += 0;
353 dy += 0;
354 break;
355 case TR:
356 dx += width;
357 dy += 0;
358 break;
359 case TC:
360 dx += width / 2.0;
361 dy += 0;
362 break;
363 case LC:
364 dx += 0;
365 dy += height / 2.0;
366 break;
367 case RC:
368 dx += width;
369 dy += height / 2.0;
370 break;
371 case BL:
372 dx += 0;
373 dy += height;
374 break;
375 case BR:
376 dx += width;
377 dy += height;
378 break;
379 case BC:
380 dx += width / 2.0;
381 dy += height;
382 break;
383 }
384 Symbol label = new Symbol();
385 switch (style) {
386 case RRCT:
387 if (width < height) width = height;
388 width *= 1.5;
389 height *= 1.5;
390 label.add(new Instr(Prim.FILL, bg));
391 label.add(new Instr(Prim.RSHP, new RoundRectangle2D.Double(-dx,-dy,width,height,height,height)));
392 label.add(new Instr(Prim.FILL, fg));
393 label.add(new Instr(Prim.STRK, new BasicStroke(1 + (int)(height/10), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
394 label.add(new Instr(Prim.RRCT, new RoundRectangle2D.Double(-dx,-dy,width,height,height,height)));
395 break;
396 }
397 label.add(new Instr(Prim.TEXT, new Caption(str, font, fg, delta)));
398 Point2D point = context.getPoint(feature.centre);
399 Symbols.drawSymbol(g2, label, sScale, point.getX(), point.getY(), null, null);
400 }
401
402 public static void lineText(Feature feature, String str, Font font, Color colour, double offset, double dy) {
403 Area area;
404 switch (feature.flag) {
405 case LINE:
406 Edge edge = map.edges.get(feature.refs);
407 area = map.new Area();
408 area.add(map.new Bound(map.new Side(edge, true), true));
409 break;
410 case AREA:
411 area = map.areas.get(feature.refs);
412 break;
413 default:
414 return;
415 }
416// Rectangle prect = symbolSize(prisymb);
417 if (!str.isEmpty()) {
418 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
419 FontRenderContext frc = g2.getFontRenderContext();
420 GlyphVector gv = font.deriveFont((float)(font.getSize()*sScale)).createGlyphVector(frc, str);
421// double psize = Math.abs(prect.getX());
422 Point2D prev = new Point2D.Double();
423 Point2D next = new Point2D.Double();
424 Point2D curr = new Point2D.Double();
425 Point2D succ = new Point2D.Double();
426 boolean gap = true;
427 boolean piv = false;
428 double len = 0;
429 double angle = 0;
430 for (Bound bound : area) {
431 BoundIterator bit = map.new BoundIterator(bound);
432 boolean first = true;
433 while (bit.hasNext()) {
434 prev = next;
435 next = context.getPoint(bit.next());
436 angle = Math.atan2(next.getY() - prev.getY(), next.getX() - prev.getX());
437 piv = true;
438 if (first) {
439 curr = succ = next;
440// gap = (space > 0);
441// len = gap ? psize * space * 0.5 : psize;
442 first = false;
443 } else {
444 while (curr.distance(next) >= len) {
445 if (piv) {
446 double rem = len;
447 double s = prev.distance(next);
448 double p = curr.distance(prev);
449 if ((s > 0) && (p > 0)) {
450 double n = curr.distance(next);
451 double theta = Math.acos((s * s + p * p - n * n) / 2 / s / p);
452 double phi = Math.asin(p / len * Math.sin(theta));
453 rem = len * Math.sin(Math.PI - theta - phi) / Math.sin(theta);
454 }
455 succ = new Point2D.Double(prev.getX() + (rem * Math.cos(angle)), prev.getY() + (rem * Math.sin(angle)));
456 piv = false;
457 } else {
458 succ = new Point2D.Double(curr.getX() + (len * Math.cos(angle)), curr.getY() + (len * Math.sin(angle)));
459 }
460 if (!gap) {
461// Symbols.drawSymbol(g2, symbol, sScale, curr.getX(), curr.getY(), new Delta(Handle.BC, AffineTransform.getRotateInstance(Math.atan2((succ.getY() - curr.getY()), (succ.getX() - curr.getX())) + Math.toRadians(90))), null);
462 }
463 gap = false;
464 curr = succ;
465// len = psize;
466 }
467 }
468 }
469 }
470 }
471 }
472}
Note: See TracBrowser for help on using the repository browser.