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

Last change on this file since 30028 was 30028, checked in by malcolmh, 11 years ago

save

File size: 15.4 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 enum LabelStyle { NONE, RRCT, RECT, ELPS, CIRC, VCLR, HCLR }
64
65 static MapContext context;
66 static SeaMap map;
67 static double sScale;
68 static Graphics2D g2;
69 static int zoom;
70
71 public static void reRender(Graphics2D g, int z, double factor, SeaMap m, MapContext c) {
72 g2 = g;
73 zoom = z;
74 context = c;
75 map = m;
76 sScale = symbolScale[zoom] * factor;
77 if (map != null) {
78 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
79 g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
80 g2.setStroke(new BasicStroke(0, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER));
81 Rules.rules(map, zoom);
82 }
83 }
84
85 public static AttMap getAtts(Feature feature, Obj obj, int idx) {
86 HashMap<Integer, AttMap> objs = feature.objs.get(obj);
87 if (objs == null)
88 return null;
89 else
90 return objs.get(idx);
91 }
92
93 public static Object getAttVal(Feature feature, Obj obj, int idx, Att att) {
94 AttMap atts = getAtts(feature, obj, idx);
95 if (atts == null)
96 return S57val.nullVal(att);
97 else {
98 AttItem item = atts.get(att);
99 if (item == null)
100 return S57val.nullVal(att);
101 return item.val;
102 }
103 }
104
105 public static void symbol(Feature feature, Symbol symbol, Obj obj, Delta delta, Scheme scheme) {
106 Point2D point = context.getPoint(feature.centre);
107 if (obj == null) {
108 Symbols.drawSymbol(g2, symbol, sScale, point.getX(), point.getY(), delta, scheme);
109 } else {
110 ArrayList<Color> colours = new ArrayList<Color>();
111 for (ColCOL col : (ArrayList<ColCOL>)getAttVal(feature, obj, 0, Att.COLOUR)) {
112 colours.add(bodyColours.get(col));
113 }
114 ArrayList<Patt> patterns = new ArrayList<Patt>();
115 for(ColPAT pat: (ArrayList<ColPAT>) getAttVal(feature, obj, 0, Att.COLPAT)) {
116 patterns.add(pattMap.get(pat));
117 }
118 Symbols.drawSymbol(g2, symbol, sScale, point.getX(), point.getY(), delta, new Scheme(patterns, colours));
119 }
120 }
121
122 private static Rectangle symbolSize(Symbol symbol) {
123 Symbol ssymb = symbol;
124 while (ssymb != null) {
125 for (Instr item : symbol) {
126 if (item.type == Prim.BBOX) {
127 return (Rectangle) item.params;
128 }
129 if (item.type == Prim.SYMB) {
130 ssymb = ((SubSymbol) item.params).instr;
131 break;
132 }
133 }
134 if (ssymb == symbol)
135 break;
136 }
137 return null;
138 }
139
140 public static void lineSymbols(Feature feature, Symbol prisymb, double space, Symbol secsymb, int ratio, Color col) {
141 Area area;
142 switch (feature.flag) {
143 case LINE:
144 Edge edge = map.edges.get(feature.refs);
145 area = map.new Area();
146 area.add(map.new Bound(map.new Side(edge, true), true));
147 break;
148 case AREA:
149 area = map.areas.get(feature.refs);
150 break;
151 default:
152 return;
153 }
154 Rectangle prect = symbolSize(prisymb);
155 Rectangle srect = symbolSize(secsymb);
156 if (srect == null)
157 ratio = 0;
158 if (prect != null) {
159 double psize = Math.abs(prect.getY()) * sScale;
160 double ssize = (srect != null) ? Math.abs(srect.getY()) * sScale : 0;
161 Point2D prev = new Point2D.Double();
162 Point2D next = new Point2D.Double();
163 Point2D curr = new Point2D.Double();
164 Point2D succ = new Point2D.Double();
165 boolean gap = true;
166 boolean piv = false;
167 double len = 0;
168 double angle = 0;
169 int scount = ratio;
170 Symbol symbol = prisymb;
171 for (Bound bound : area) {
172 BoundIterator bit = map.new BoundIterator(bound);
173 boolean first = true;
174 while (bit.hasNext()) {
175 prev = next;
176 next = context.getPoint(bit.next());
177 angle = Math.atan2(next.getY() - prev.getY(), next.getX() - prev.getX());
178 piv = true;
179 if (first) {
180 curr = succ = next;
181 gap = (space > 0);
182 scount = ratio;
183 symbol = prisymb;
184 len = gap ? psize * space * 0.5 : psize;
185 first = false;
186 } else {
187 while (curr.distance(next) >= len) {
188 if (piv) {
189 double rem = len;
190 double s = prev.distance(next);
191 double p = curr.distance(prev);
192 if ((s > 0) && (p > 0)) {
193 double n = curr.distance(next);
194 double theta = Math.acos((s * s + p * p - n * n) / 2 / s / p);
195 double phi = Math.asin(p / len * Math.sin(theta));
196 rem = len * Math.sin(Math.PI - theta - phi) / Math.sin(theta);
197 }
198 succ = new Point2D.Double(prev.getX() + (rem * Math.cos(angle)), prev.getY() + (rem * Math.sin(angle)));
199 piv = false;
200 } else {
201 succ = new Point2D.Double(curr.getX() + (len * Math.cos(angle)), curr.getY() + (len * Math.sin(angle)));
202 }
203 if (!gap) {
204 Symbols.drawSymbol(g2, symbol, sScale, curr.getX(), curr.getY(),
205 new Delta(Handle.BC, AffineTransform.getRotateInstance(Math.atan2((succ.getY() - curr.getY()), (succ.getX() - curr.getX())) + Math.toRadians(90))),
206 new Scheme(col));
207 }
208 if (space > 0)
209 gap = !gap;
210 curr = succ;
211 len = gap ? (psize * space) : (--scount == 0) ? ssize : psize;
212 if (scount == 0) {
213 symbol = secsymb;
214 scount = ratio;
215 } else {
216 symbol = prisymb;
217 }
218 }
219 }
220 }
221 }
222 }
223 }
224
225 public static void lineVector(Feature feature, LineStyle style) {
226 Path2D.Double p = new Path2D.Double();
227 p.setWindingRule(GeneralPath.WIND_EVEN_ODD);
228 Point2D point;
229 switch (feature.flag) {
230 case LINE:
231 EdgeIterator eit = map.new EdgeIterator(map.edges.get(feature.refs), true);
232 point = context.getPoint(eit.next());
233 p.moveTo(point.getX(), point.getY());
234 while (eit.hasNext()) {
235 point = context.getPoint(eit.next());
236 p.lineTo(point.getX(), point.getY());
237 }
238 break;
239 case AREA:
240 for (Bound bound : map.areas.get(feature.refs)) {
241 BoundIterator bit = map.new BoundIterator(bound);
242 point = context.getPoint(bit.next());
243 p.moveTo(point.getX(), point.getY());
244 while (bit.hasNext()) {
245 point = context.getPoint(bit.next());
246 p.lineTo(point.getX(), point.getY());
247 }
248 }
249 break;
250 }
251 if (style.line != null) {
252 if (style.dash != null) {
253 float[] dash = new float[style.dash.length];
254 System.arraycopy(style.dash, 0, dash, 0, style.dash.length);
255 for (int i = 0; i < style.dash.length; i++) {
256 dash[i] *= (float) sScale;
257 }
258 g2.setStroke(new BasicStroke((float) (style.width * sScale), BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1, dash, 0));
259 } else {
260 g2.setStroke(new BasicStroke((float) (style.width * sScale), BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
261 }
262 g2.setPaint(style.line);
263 g2.draw(p);
264 }
265 if (style.fill != null) {
266 g2.setPaint(style.fill);
267 g2.fill(p);
268 }
269 }
270
271 public static void lineCircle(Feature feature, LineStyle style, double radius, UniHLU units) {
272 switch (units) {
273 case HLU_FEET:
274 radius /= 6076;
275 break;
276 case HLU_KMTR:
277 radius /= 1.852;
278 break;
279 case HLU_HMTR:
280 radius /= 18.52;
281 break;
282 case HLU_SMIL:
283 radius /= 1.15078;
284 break;
285 case HLU_NMIL:
286 break;
287 default:
288 radius /= 1852;
289 break;
290 }
291 radius *= context.mile(feature);
292 Symbol circle = new Symbol();
293 if (style.fill != null) {
294 circle.add(new Instr(Prim.FILL, style.fill));
295 circle.add(new Instr(Prim.RSHP, new Ellipse2D.Double(-radius,-radius,radius*2,radius*2)));
296 }
297 circle.add(new Instr(Prim.FILL, style.line));
298 circle.add(new Instr(Prim.STRK, new BasicStroke(style.width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1, style.dash, 0)));
299 circle.add(new Instr(Prim.ELPS, new Ellipse2D.Double(-radius,-radius,radius*2,radius*2)));
300 Point2D point = context.getPoint(feature.centre);
301 Symbols.drawSymbol(g2, circle, 1, point.getX(), point.getY(), null, null);
302 }
303
304
305 public static void fillPattern(Feature feature, BufferedImage image) {
306 Path2D.Double p = new Path2D.Double();
307 p.setWindingRule(GeneralPath.WIND_EVEN_ODD);
308 Point2D point;
309 switch (feature.flag) {
310 case POINT:
311 point = context.getPoint(feature.centre);
312 g2.drawImage(image, new AffineTransformOp(AffineTransform.getScaleInstance(sScale, sScale), AffineTransformOp.TYPE_NEAREST_NEIGHBOR),
313 (int)(point.getX() - (50 * sScale)), (int)(point.getY() - (50 * sScale)));
314 break;
315 case AREA:
316 for (Bound bound : map.areas.get(feature.refs)) {
317 BoundIterator bit = map.new BoundIterator(bound);
318 point = context.getPoint(bit.next());
319 p.moveTo(point.getX(), point.getY());
320 while (bit.hasNext()) {
321 point = context.getPoint(bit.next());
322 p.lineTo(point.getX(), point.getY());
323 }
324 }
325 g2.setPaint(new TexturePaint(image, new Rectangle(0, 0, 1 + (int)(100 * sScale), 1 + (int)(100 * sScale))));
326 g2.fill(p);
327 break;
328 }
329 }
330
331 public static void labelText(Feature feature, String str, Font font, LabelStyle style, Color fg, Color bg, Delta delta) {
332 if (delta == null) delta = new Delta(Handle.CC, null);
333 if (bg == null) bg = new Color(0x00000000, true);
334 if (str == null) str = " ";
335 if (str.isEmpty()) str = " ";
336 FontRenderContext frc = g2.getFontRenderContext();
337 GlyphVector gv = font.deriveFont((float)(font.getSize())).createGlyphVector(frc, str.equals(" ") ? "M" : str);
338 Rectangle2D bounds = gv.getVisualBounds();
339 double width = bounds.getWidth();
340 double height = bounds.getHeight();
341 if (width < height) width = height;
342 double dx = 0;
343 double dy = 0;
344 switch (delta.h) {
345 case CC:
346 dx += width / 2.0;
347 dy += height / 2.0;
348 break;
349 case TR:
350 dx += width;
351 break;
352 case TC:
353 dx += width / 2.0;
354 break;
355 case LC:
356 dy += height / 2.0;
357 break;
358 case RC:
359 dx += width;
360 dy += height / 2.0;
361 break;
362 case BL:
363 dy += height;
364 break;
365 case BR:
366 dx += width;
367 dy += height;
368 break;
369 case BC:
370 dx += width / 2.0;
371 dy += height;
372 break;
373 }
374 width += (height * 0.8);
375 dx += (height * 0.4);
376 height *= 1.5;
377 dy += (height * 0.15);
378 Symbol label = new Symbol();
379 switch (style) {
380 case RRCT:
381 label.add(new Instr(Prim.FILL, bg));
382 label.add(new Instr(Prim.RSHP, new RoundRectangle2D.Double(-dx,-dy,width,height,height,height)));
383 label.add(new Instr(Prim.FILL, fg));
384 label.add(new Instr(Prim.STRK, new BasicStroke(1 + (int)(height/10), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
385 label.add(new Instr(Prim.RRCT, new RoundRectangle2D.Double(-dx,-dy,width,height,height,height)));
386 break;
387 case VCLR:
388 height += 20;
389 dy += 10;
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 int sw = 1 + (int)(height/10);
394 double po = dy - (sw / 2);
395 label.add(new Instr(Prim.STRK, new BasicStroke(sw, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
396 Path2D.Double p = new Path2D.Double(); p.moveTo(-(height*0.2),po); p.lineTo((height*0.2),po); p.moveTo(0,po); p.lineTo(0,po-10);
397 p.moveTo(-(height*0.2),-po); p.lineTo((height*0.2),-po); p.moveTo(0,-po); p.lineTo(0,-po+10);
398 label.add(new Instr(Prim.PLIN, p));
399 break;
400 }
401 label.add(new Instr(Prim.TEXT, new Caption(str, font, fg, delta)));
402 Point2D point = context.getPoint(feature.centre);
403 Symbols.drawSymbol(g2, label, sScale, point.getX(), point.getY(), null, null);
404 }
405
406 public static void lineText(Feature feature, String str, Font font, Color colour, double offset, double dy) {
407 Area area;
408 switch (feature.flag) {
409 case LINE:
410 Edge edge = map.edges.get(feature.refs);
411 area = map.new Area();
412 area.add(map.new Bound(map.new Side(edge, true), true));
413 break;
414 case AREA:
415 area = map.areas.get(feature.refs);
416 break;
417 default:
418 return;
419 }
420// Rectangle prect = symbolSize(prisymb);
421 if (!str.isEmpty()) {
422 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
423 FontRenderContext frc = g2.getFontRenderContext();
424 GlyphVector gv = font.deriveFont((float)(font.getSize()*sScale)).createGlyphVector(frc, str);
425// double psize = Math.abs(prect.getX());
426 Point2D prev = new Point2D.Double();
427 Point2D next = new Point2D.Double();
428 Point2D curr = new Point2D.Double();
429 Point2D succ = new Point2D.Double();
430 boolean gap = true;
431 boolean piv = false;
432 double len = 0;
433 double angle = 0;
434 for (Bound bound : area) {
435 BoundIterator bit = map.new BoundIterator(bound);
436 boolean first = true;
437 while (bit.hasNext()) {
438 prev = next;
439 next = context.getPoint(bit.next());
440 angle = Math.atan2(next.getY() - prev.getY(), next.getX() - prev.getX());
441 piv = true;
442 if (first) {
443 curr = succ = next;
444// gap = (space > 0);
445// len = gap ? psize * space * 0.5 : psize;
446 first = false;
447 } else {
448 while (curr.distance(next) >= len) {
449 if (piv) {
450 double rem = len;
451 double s = prev.distance(next);
452 double p = curr.distance(prev);
453 if ((s > 0) && (p > 0)) {
454 double n = curr.distance(next);
455 double theta = Math.acos((s * s + p * p - n * n) / 2 / s / p);
456 double phi = Math.asin(p / len * Math.sin(theta));
457 rem = len * Math.sin(Math.PI - theta - phi) / Math.sin(theta);
458 }
459 succ = new Point2D.Double(prev.getX() + (rem * Math.cos(angle)), prev.getY() + (rem * Math.sin(angle)));
460 piv = false;
461 } else {
462 succ = new Point2D.Double(curr.getX() + (len * Math.cos(angle)), curr.getY() + (len * Math.sin(angle)));
463 }
464 if (!gap) {
465// 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);
466 }
467 gap = false;
468 curr = succ;
469// len = psize;
470 }
471 }
472 }
473 }
474 }
475 }
476}
Note: See TracBrowser for help on using the repository browser.