Ignore:
Timestamp:
2016-06-24T03:48:12+02:00 (8 years ago)
Author:
donvip
Message:

checkstyle

Location:
applications/editors/josm/plugins/seachart/src/render
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/seachart/src/render/ChartContext.java

    r32082 r32394  
    1414
    1515import s57.S57map;
    16 import s57.S57map.*;
     16import s57.S57map.Feature;
     17import s57.S57map.Snode;
    1718
    1819public interface ChartContext {
    19         public enum RuleSet { ALL, BASE, SEAMARK }
     20    enum RuleSet { ALL, BASE, SEAMARK }
    2021
    21         Point2D getPoint(Snode coord);
    22         double mile(Feature feature);
    23         boolean clip();
    24         Color background(S57map map);
    25         RuleSet ruleset();
     22    Point2D getPoint(Snode coord);
     23
     24    double mile(Feature feature);
     25
     26    boolean clip();
     27
     28    Color background(S57map map);
     29
     30    RuleSet ruleset();
    2631}
  • applications/editors/josm/plugins/seachart/src/render/Renderer.java

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

    r32393 r32394  
    1818import java.util.HashMap;
    1919
     20import render.ChartContext.RuleSet;
     21import render.Renderer.LabelStyle;
     22import s57.S57att.Att;
     23import s57.S57map.AttMap;
     24import s57.S57map.Feature;
     25import s57.S57map.ObjTab;
     26import s57.S57map.Pflag;
     27import s57.S57map.Rflag;
     28import s57.S57obj.Obj;
    2029import s57.S57val;
    21 import s57.S57val.*;
    22 import s57.S57att.*;
    23 import s57.S57obj.*;
    24 import s57.S57map.*;
    25 import render.ChartContext.RuleSet;
    26 import render.Renderer.*;
    27 import symbols.*;
    28 import symbols.Symbols.*;
     30import s57.S57val.AddMRK;
     31import s57.S57val.AttVal;
     32import s57.S57val.BcnSHP;
     33import s57.S57val.BnkWTW;
     34import s57.S57val.BoySHP;
     35import s57.S57val.CatACH;
     36import s57.S57val.CatCBL;
     37import s57.S57val.CatCRN;
     38import s57.S57val.CatDIS;
     39import s57.S57val.CatHAF;
     40import s57.S57val.CatLAM;
     41import s57.S57val.CatLMK;
     42import s57.S57val.CatMOR;
     43import s57.S57val.CatNMK;
     44import s57.S57val.CatOBS;
     45import s57.S57val.CatOFP;
     46import s57.S57val.CatPIL;
     47import s57.S57val.CatPRA;
     48import s57.S57val.CatREA;
     49import s57.S57val.CatROD;
     50import s57.S57val.CatSCF;
     51import s57.S57val.CatSEA;
     52import s57.S57val.CatSIL;
     53import s57.S57val.CatSIT;
     54import s57.S57val.CatSIW;
     55import s57.S57val.CatSLC;
     56import s57.S57val.CatWED;
     57import s57.S57val.CatWRK;
     58import s57.S57val.ColCOL;
     59import s57.S57val.ColPAT;
     60import s57.S57val.FncFNC;
     61import s57.S57val.MarSYS;
     62import s57.S57val.StsSTS;
     63import s57.S57val.TopSHP;
     64import s57.S57val.TrfTRF;
     65import s57.S57val.UniHLU;
     66import s57.S57val.WatLEV;
     67import symbols.Areas;
     68import symbols.Beacons;
     69import symbols.Buoys;
     70import symbols.Facilities;
     71import symbols.Harbours;
     72import symbols.Landmarks;
     73import symbols.Notices;
     74import symbols.Symbols;
     75import symbols.Symbols.Delta;
     76import symbols.Symbols.Handle;
     77import symbols.Symbols.LineStyle;
     78import symbols.Symbols.Patt;
     79import symbols.Symbols.Scheme;
     80import symbols.Symbols.Symbol;
     81import symbols.Topmarks;
    2982
    3083public class Rules {
    31        
    32         static final DecimalFormat df = new DecimalFormat("#.#");
    33 
    34         static final EnumMap<ColCOL, Color> bodyColours = new EnumMap<>(ColCOL.class);
    35         static {
    36                 bodyColours.put(ColCOL.COL_UNK, new Color(0, true));
    37                 bodyColours.put(ColCOL.COL_WHT, new Color(0xffffff));
    38                 bodyColours.put(ColCOL.COL_BLK, new Color(0x000000));
    39                 bodyColours.put(ColCOL.COL_RED, new Color(0xd40000));
    40                 bodyColours.put(ColCOL.COL_GRN, new Color(0x00d400));
    41                 bodyColours.put(ColCOL.COL_BLU, Color.blue);
    42                 bodyColours.put(ColCOL.COL_YEL, new Color(0xffd400));
    43                 bodyColours.put(ColCOL.COL_GRY, Color.gray);
    44                 bodyColours.put(ColCOL.COL_BRN, new Color(0x8b4513));
    45                 bodyColours.put(ColCOL.COL_AMB, new Color(0xfbf00f));
    46                 bodyColours.put(ColCOL.COL_VIO, new Color(0xee82ee));
    47                 bodyColours.put(ColCOL.COL_ORG, Color.orange);
    48                 bodyColours.put(ColCOL.COL_MAG, new Color(0xf000f0));
    49                 bodyColours.put(ColCOL.COL_PNK, Color.pink);
    50         }
    51 
    52         static final EnumMap<ColPAT, Patt> pattMap = new EnumMap<>(ColPAT.class);
    53         static {
    54                 pattMap.put(ColPAT.PAT_UNKN, Patt.Z);
    55                 pattMap.put(ColPAT.PAT_HORI, Patt.H);
    56                 pattMap.put(ColPAT.PAT_VERT, Patt.V);
    57                 pattMap.put(ColPAT.PAT_DIAG, Patt.D);
    58                 pattMap.put(ColPAT.PAT_BRDR, Patt.B);
    59                 pattMap.put(ColPAT.PAT_SQUR, Patt.S);
    60                 pattMap.put(ColPAT.PAT_CROS, Patt.C);
    61                 pattMap.put(ColPAT.PAT_SALT, Patt.X);
    62                 pattMap.put(ColPAT.PAT_STRP, Patt.H);
    63         }
    64        
    65         static String getName() {
    66                 AttVal<?> name = feature.atts.get(Att.OBJNAM);
    67                 if (name == null) {
    68                         AttMap atts = feature.objs.get(feature.type).get(0);
    69                         if (atts != null) {
    70                                 name = atts.get(Att.OBJNAM);
    71                         }
    72                 }
    73                 return (name != null) ? (String)name.val: null;
    74         }
    75 
    76         public static void addName(int z, Font font) {
    77                 addName(z, font, Color.black, new Delta(Handle.CC, new AffineTransform()));
    78         }
    79         public static void addName(int z, Font font, Color colour) {
    80                 addName(z, font, colour, new Delta(Handle.CC, new AffineTransform()));
    81         }
    82         public static void addName(int z, Font font, Delta delta) {
    83                 addName(z, font, Color.black, delta);
    84         }
    85         public static void addName(int z, Font font, Color colour, Delta delta) {
    86                 if (Renderer.zoom >= z) {
    87                         String name = getName();
    88                         if (name != null) {
    89                                 Renderer.labelText(name, font,  colour, delta);
    90                         }
    91                 }
    92         }
    93 
    94         static AttMap getAtts(Obj obj, int idx) {
    95                 HashMap<Integer, AttMap> objs = feature.objs.get(obj);
    96                 if (objs == null)
    97                         return null;
    98                 else
    99                         return objs.get(idx);
    100         }
    101 
    102         public static Object getAttVal(Obj obj, Att att) {
    103                 AttMap atts;
    104                 HashMap<Integer, AttMap> objs;
    105                 AttVal<?> item;
    106                 if ((objs = feature.objs.get(obj)) != null)
    107                         atts = objs.get(0);
    108                 else
    109                         return null;
    110                 if ((item = atts.get(att)) == null)
    111                         return null;
    112                 else
    113                         return item.val;
    114         }
    115        
    116         public static String getAttStr(Obj obj, Att att) {
    117                 String str = (String)getAttVal(obj, att);
    118                 if (str != null) {
    119                         return str;
    120                 }
    121                 return "";
    122         }
    123 
    124         @SuppressWarnings("unchecked")
    125         public static Enum<?> getAttEnum(Obj obj, Att att) {
    126                 ArrayList<?> list = (ArrayList<?>)getAttVal(obj, att);
    127                 if (list != null) {
    128                         return ((ArrayList<Enum<?>>)list).get(0);
    129                 }
    130                 return S57val.unknAtt(att);
    131         }
    132        
    133         @SuppressWarnings("unchecked")
    134         public static ArrayList<?> getAttList(Obj obj, Att att) {
    135                 ArrayList<Enum<?>> list = (ArrayList<Enum<?>>)getAttVal(obj, att);
    136                 if (list != null) {
    137                         return list;
    138                 }
    139                 list = new ArrayList<>();
    140                 list.add(S57val.unknAtt(att));
    141                 return list;
    142         }
    143        
    144         @SuppressWarnings("unchecked")
    145         static Scheme getScheme(Obj obj) {
    146                 ArrayList<Color> colours = new ArrayList<>();
    147                 for (ColCOL col : (ArrayList<ColCOL>) getAttList(obj, Att.COLOUR)) {
    148                         colours.add(bodyColours.get(col));
    149                 }
    150                 ArrayList<Patt> patterns = new ArrayList<>();
    151                 for (ColPAT pat : (ArrayList<ColPAT>) getAttList(obj, Att.COLPAT)) {
    152                         patterns.add(pattMap.get(pat));
    153                 }
    154                 return new Scheme(patterns, colours);
    155         }
    156 
    157         static boolean hasAttribute(Obj obj, Att att) {
    158                 AttMap atts;
    159                 if ((atts = getAtts(obj, 0)) != null) {
    160                         AttVal<?> item = atts.get(att);
    161                         return item != null;
    162                 }
    163                 return false;
    164         }
    165        
    166         static boolean testAttribute(Obj obj, Att att, Object val) {
    167                 AttMap atts;
    168                 if ((atts = getAtts(obj, 0)) != null) {
    169                         AttVal<?> item = atts.get(att);
    170                         if (item != null) {
    171                                 switch (item.conv) {
    172                                 case S:
    173                                 case A:
    174                                         return ((String)item.val).equals(val);
    175                                 case E:
    176                                 case L:
    177                                         return ((ArrayList<?>)item.val).contains(val);
    178                                 case F:
    179                                 case I:
    180                                         return item.val == val;
    181                                 }
    182                         }
    183                 }
    184                 return false;
    185         }
    186        
    187         static boolean hasObject(Obj obj) {
    188                 return (feature.objs.containsKey(obj));
    189         }
    190        
    191         public static Feature feature;
    192         static ArrayList<Feature> objects;
    193        
    194         static boolean testObject(Obj obj) {
    195                 return ((objects = Renderer.map.features.get(obj)) != null);
    196         }
    197        
    198         static boolean testFeature(Feature f) {
    199                 return ((feature = f).reln == Rflag.MASTER);
    200         }
    201        
    202         public static void rules () {
    203                 if ((Renderer.context.ruleset() == RuleSet.ALL) || (Renderer.context.ruleset() == RuleSet.BASE)) {
    204                         if (testObject(Obj.LNDARE)) for (Feature f : objects) if (testFeature(f)) areas();
    205                         if (testObject(Obj.BUAARE)) for (Feature f : objects) if (testFeature(f)) areas();
    206                         if (testObject(Obj.HRBFAC)) for (Feature f : objects) if (testFeature(f)) areas();
    207                         if (testObject(Obj.HRBBSN)) for (Feature f : objects) if (testFeature(f)) areas();
    208                         if (testObject(Obj.LOKBSN)) for (Feature f : objects) if (testFeature(f)) areas();
    209                         if (testObject(Obj.LKBSPT)) for (Feature f : objects) if (testFeature(f)) areas();
    210                         if (testObject(Obj.LAKARE)) for (Feature f : objects) if (testFeature(f)) areas();
    211                         if (testObject(Obj.RIVERS)) for (Feature f : objects) if (testFeature(f)) waterways();
    212                         if (testObject(Obj.CANALS)) for (Feature f : objects) if (testFeature(f)) waterways();
    213                         if (testObject(Obj.DEPARE)) for (Feature f : objects) if (testFeature(f)) areas();
    214                         if (testObject(Obj.COALNE)) for (Feature f : objects) if (testFeature(f)) areas();
    215                         if (testObject(Obj.ROADWY)) for (Feature f : objects) if (testFeature(f)) highways();
    216                         if (testObject(Obj.RAILWY)) for (Feature f : objects) if (testFeature(f)) highways();
    217                 }
    218                 if (Renderer.context.ruleset() == RuleSet.ALL) {
    219                         if (testObject(Obj.SOUNDG)) for (Feature f : objects) if (testFeature(f)) depths();
    220                         if (testObject(Obj.DEPCNT)) for (Feature f : objects) if (testFeature(f)) depths();
    221                 }
    222                 if (testObject(Obj.SLCONS)) for (Feature f : objects) if (testFeature(f)) shoreline();
    223                 if ((Renderer.context.ruleset() == RuleSet.ALL) || (Renderer.context.ruleset() == RuleSet.SEAMARK)) {
    224                         if (testObject(Obj.PIPSOL)) for (Feature f : objects) if (testFeature(f)) pipelines();
    225                         if (testObject(Obj.CBLSUB)) for (Feature f : objects) if (testFeature(f)) cables();
    226                         if (testObject(Obj.PIPOHD)) for (Feature f : objects) if (testFeature(f)) pipelines();
    227                         if (testObject(Obj.CBLOHD)) for (Feature f : objects) if (testFeature(f)) cables();
    228                         if (testObject(Obj.TSEZNE)) for (Feature f : objects) if (testFeature(f)) separation();
    229                         if (testObject(Obj.TSSCRS)) for (Feature f : objects) if (testFeature(f)) separation();
    230                         if (testObject(Obj.TSSRON)) for (Feature f : objects) if (testFeature(f)) separation();
    231                         if (testObject(Obj.TSELNE)) for (Feature f : objects) if (testFeature(f)) separation();
    232                         if (testObject(Obj.TSSLPT)) for (Feature f : objects) if (testFeature(f)) separation();
    233                         if (testObject(Obj.TSSBND)) for (Feature f : objects) if (testFeature(f)) separation();
    234                         if (testObject(Obj.ISTZNE)) for (Feature f : objects) if (testFeature(f)) separation();
    235                         if (testObject(Obj.SNDWAV)) for (Feature f : objects) if (testFeature(f)) areas();
    236                         if (testObject(Obj.WEDKLP)) for (Feature f : objects) if (testFeature(f)) areas();
    237                         if (testObject(Obj.OSPARE)) for (Feature f : objects) if (testFeature(f)) areas();
    238                         if (testObject(Obj.FAIRWY)) for (Feature f : objects) if (testFeature(f)) areas();
    239                         if (testObject(Obj.DRGARE)) for (Feature f : objects) if (testFeature(f)) areas();
    240                         if (testObject(Obj.RESARE)) for (Feature f : objects) if (testFeature(f)) areas();
    241                         if (testObject(Obj.PRCARE)) for (Feature f : objects) if (testFeature(f)) areas();
    242                         if (testObject(Obj.SPLARE)) for (Feature f : objects) if (testFeature(f)) areas();
    243                         if (testObject(Obj.SEAARE)) for (Feature f : objects) if (testFeature(f)) areas();
    244                         if (testObject(Obj.OBSTRN)) for (Feature f : objects) if (testFeature(f)) obstructions();
    245                         if (testObject(Obj.UWTROC)) for (Feature f : objects) if (testFeature(f)) obstructions();
    246                         if (testObject(Obj.MARCUL)) for (Feature f : objects) if (testFeature(f)) areas();
    247                         if (testObject(Obj.RECTRC)) for (Feature f : objects) if (testFeature(f)) transits();
    248                         if (testObject(Obj.NAVLNE)) for (Feature f : objects) if (testFeature(f)) transits();
    249                         if (testObject(Obj.HRBFAC)) for (Feature f : objects) if (testFeature(f)) harbours();
    250                         if (testObject(Obj.ACHARE)) for (Feature f : objects) if (testFeature(f)) harbours();
    251                         if (testObject(Obj.ACHBRT)) for (Feature f : objects) if (testFeature(f)) harbours();
    252                         if (testObject(Obj.BERTHS)) for (Feature f : objects) if (testFeature(f)) harbours();
    253                         if (testObject(Obj.DISMAR)) for (Feature f : objects) if (testFeature(f)) distances();
    254                         if (testObject(Obj.HULKES)) for (Feature f : objects) if (testFeature(f)) ports();
    255                         if (testObject(Obj.CRANES)) for (Feature f : objects) if (testFeature(f)) ports();
    256                         if (testObject(Obj.LNDMRK)) for (Feature f : objects) if (testFeature(f)) landmarks();
    257                         if (testObject(Obj.SILTNK)) for (Feature f : objects) if (testFeature(f)) landmarks();
    258                         if (testObject(Obj.BUISGL)) for (Feature f : objects) if (testFeature(f)) harbours();
    259                         if (testObject(Obj.MORFAC)) for (Feature f : objects) if (testFeature(f)) moorings();
    260                         if (testObject(Obj.NOTMRK)) for (Feature f : objects) if (testFeature(f)) notices();
    261                         if (testObject(Obj.SMCFAC)) for (Feature f : objects) if (testFeature(f)) marinas();
    262                         if (testObject(Obj.BRIDGE)) for (Feature f : objects) if (testFeature(f)) bridges();
    263                         if (testObject(Obj.PILPNT)) for (Feature f : objects) if (testFeature(f)) points();
    264                         if (testObject(Obj.TOPMAR)) for (Feature f : objects) if (testFeature(f)) points();
    265                         if (testObject(Obj.DAYMAR)) for (Feature f : objects) if (testFeature(f)) points();
    266                         if (testObject(Obj.FOGSIG)) for (Feature f : objects) if (testFeature(f)) points();
    267                         if (testObject(Obj.RDOCAL)) for (Feature f : objects) if (testFeature(f)) callpoint();
    268                         if (testObject(Obj.LITMIN)) for (Feature f : objects) if (testFeature(f)) lights();
    269                         if (testObject(Obj.LITMAJ)) for (Feature f : objects) if (testFeature(f)) lights();
    270                         if (testObject(Obj.LIGHTS)) for (Feature f : objects) if (testFeature(f)) lights();
    271                         if (testObject(Obj.SISTAT)) for (Feature f : objects) if (testFeature(f)) stations();
    272                         if (testObject(Obj.SISTAW)) for (Feature f : objects) if (testFeature(f)) stations();
    273                         if (testObject(Obj.CGUSTA)) for (Feature f : objects) if (testFeature(f)) stations();
    274                         if (testObject(Obj.RDOSTA)) for (Feature f : objects) if (testFeature(f)) stations();
    275                         if (testObject(Obj.RADRFL)) for (Feature f : objects) if (testFeature(f)) stations();
    276                         if (testObject(Obj.RADSTA)) for (Feature f : objects) if (testFeature(f)) stations();
    277                         if (testObject(Obj.RTPBCN)) for (Feature f : objects) if (testFeature(f)) stations();
    278                         if (testObject(Obj.RSCSTA)) for (Feature f : objects) if (testFeature(f)) stations();
    279                         if (testObject(Obj.PILBOP)) for (Feature f : objects) if (testFeature(f)) stations();
    280                         if (testObject(Obj.WTWGAG)) for (Feature f : objects) if (testFeature(f)) gauges();
    281                         if (testObject(Obj.OFSPLF)) for (Feature f : objects) if (testFeature(f)) platforms();
    282                         if (testObject(Obj.WRECKS)) for (Feature f : objects) if (testFeature(f)) wrecks();
    283                         if (testObject(Obj.LITVES)) for (Feature f : objects) if (testFeature(f)) floats();
    284                         if (testObject(Obj.LITFLT)) for (Feature f : objects) if (testFeature(f)) floats();
    285                         if (testObject(Obj.BOYINB)) for (Feature f : objects) if (testFeature(f)) floats();
    286                         if (testObject(Obj.BOYLAT)) for (Feature f : objects) if (testFeature(f)) buoys();
    287                         if (testObject(Obj.BOYCAR)) for (Feature f : objects) if (testFeature(f)) buoys();
    288                         if (testObject(Obj.BOYISD)) for (Feature f : objects) if (testFeature(f)) buoys();
    289                         if (testObject(Obj.BOYSAW)) for (Feature f : objects) if (testFeature(f)) buoys();
    290                         if (testObject(Obj.BOYSPP)) for (Feature f : objects) if (testFeature(f)) buoys();
    291                         if (testObject(Obj.BCNLAT)) for (Feature f : objects) if (testFeature(f)) beacons();
    292                         if (testObject(Obj.BCNCAR)) for (Feature f : objects) if (testFeature(f)) beacons();
    293                         if (testObject(Obj.BCNISD)) for (Feature f : objects) if (testFeature(f)) beacons();
    294                         if (testObject(Obj.BCNSAW)) for (Feature f : objects) if (testFeature(f)) beacons();
    295                         if (testObject(Obj.BCNSPP)) for (Feature f : objects) if (testFeature(f)) beacons();
    296                 }
    297         }
    298        
    299         private static void areas() {
    300                 String name = getName();
    301                 switch (feature.type) {
    302                 case BUAARE:
    303                         Renderer.lineVector(new LineStyle(new Color(0x20000000, true)));
    304                         break;
    305                 case COALNE:
    306                         if (Renderer.zoom >= 12)
    307                                 Renderer.lineVector(new LineStyle(Color.black, 10));
    308                         break;
    309                 case DEPARE:
    310                         Double depmax = 0.0;
    311                         if (((depmax = (Double) getAttVal(Obj.DEPARE, Att.DRVAL2)) != null) && (depmax <= 0.0)) {
    312                                 Renderer.lineVector(new LineStyle(Symbols.Gdries));
    313                         }
    314                         break;
    315                 case LAKARE:
    316                         if ((Renderer.zoom >= 12) || (feature.geom.area > 10.0))
    317                                 Renderer.lineVector(new LineStyle(Symbols.Bwater));
    318                         break;
    319                 case DRGARE:
    320                         if (Renderer.zoom < 16)
    321                                 Renderer.lineVector(new LineStyle(Color.black, 8, new float[] { 25, 25 }, new Color(0x40ffffff, true)));
    322                         else
    323                                 Renderer.lineVector(new LineStyle(Color.black, 8, new float[] { 25, 25 }));
    324                         addName(12, new Font("Arial", Font.PLAIN, 100), new Delta(Handle.CC, new AffineTransform()));
    325                         break;
    326                 case FAIRWY:
    327                         if (feature.geom.area > 2.0) {
    328                                 if (Renderer.zoom < 16)
    329                                         Renderer.lineVector(new LineStyle(Symbols.Mline, 8, new float[] { 50, 50 }, new Color(0x40ffffff, true)));
    330                                 else
    331                                         Renderer.lineVector(new LineStyle(Symbols.Mline, 8, new float[] { 50, 50 }));
    332                         } else {
    333                                 if (Renderer.zoom >= 14)
    334                                         Renderer.lineVector(new LineStyle(new Color(0x40ffffff, true)));
    335                         }
    336                         break;
    337                 case LKBSPT:
    338                 case LOKBSN:
    339                 case HRBBSN:
    340                         if (Renderer.zoom >= 12) {
    341                                 Renderer.lineVector(new LineStyle(Color.black, 10, Symbols.Bwater));
    342                         } else {
    343                                 Renderer.lineVector(new LineStyle(Symbols.Bwater));
    344                         }
    345                         break;
    346                 case HRBFAC:
    347                         if (feature.objs.get(Obj.HRBBSN) != null) {
    348                                 if (Renderer.zoom >= 12) {
    349                                         Renderer.lineVector(new LineStyle(Color.black, 10, Symbols.Bwater));
    350                                 } else {
    351                                         Renderer.lineVector(new LineStyle(Symbols.Bwater));
    352                                 }
    353                         }
    354                         break;
    355                 case LNDARE:
    356                         Renderer.lineVector(new LineStyle(Symbols.Yland));
    357                         break;
    358                 case MARCUL:
    359                         if (Renderer.zoom >= 12) {
    360                                 if (Renderer.zoom >= 14) {
    361                                         Renderer.symbol(Areas.MarineFarm);
    362                                 }
    363                                 if ((feature.geom.area > 0.2) || ((feature.geom.area > 0.05) && (Renderer.zoom >= 14)) || ((feature.geom.area > 0.005) && (Renderer.zoom >= 16))) {
    364                                         Renderer.lineVector(new LineStyle(Color.black, 4, new float[] { 10, 10 }));
    365                                 }
    366                         }
    367                         break;
    368                 case OSPARE:
    369                         if (testAttribute(feature.type, Att.CATPRA, CatPRA.PRA_WFRM)) {
    370                                 Renderer.symbol(Areas.WindFarm);
    371                                 Renderer.lineVector(new LineStyle(Color.black, 20, new float[] { 40, 40 }));
    372                                 addName(15, new Font("Arial", Font.BOLD, 80), new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, 10)));
    373                         }
    374                         break;
    375                 case RESARE:
    376                 case MIPARE:
    377                         if (Renderer.zoom >= 12) {
    378                                 Renderer.lineSymbols(Areas.Restricted, 1.0, null, null, 0, Symbols.Mline);
    379                                 if (testAttribute(feature.type, Att.CATREA, CatREA.REA_NWAK)) {
    380                                         Renderer.symbol(Areas.NoWake);
    381                                 }
    382                         }
    383                         break;
    384                 case PRCARE:
    385                         if (Renderer.zoom >= 12) {
    386                                 Renderer.lineVector(new LineStyle(Symbols.Mline, 10, new float[] { 40, 40 }));
    387                         }
    388                         break;
    389                 case SEAARE:
    390                         switch ((CatSEA) getAttEnum(feature.type, Att.CATSEA)) {
    391                         case SEA_RECH:
    392                                 if ((Renderer.zoom >= 10) && (name != null))
    393                                         if (feature.geom.prim == Pflag.LINE) {
    394                                                 Renderer.lineText(name, new Font("Arial", Font.PLAIN, 150), Color.black, -40);
    395                                         } else {
    396                                                 Renderer.labelText(name, new Font("Arial", Font.PLAIN, 150), Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -40)));
    397                                         }
    398                                 break;
    399                         case SEA_BAY:
    400                                 if ((Renderer.zoom >= 12) && (name != null))
    401                                         if (feature.geom.prim == Pflag.LINE) {
    402                                                 Renderer.lineText(name, new Font("Arial", Font.PLAIN, 150), Color.black, -40);
    403                                         } else {
    404                                                 Renderer.labelText(name, new Font("Arial", Font.PLAIN, 150), Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -40)));
    405                                         }
    406                                 break;
    407                         case SEA_SHOL:
    408                                 if (Renderer.zoom >= 14) {
    409                                         if (feature.geom.prim == Pflag.AREA) {
    410                                                 Renderer.lineVector(new LineStyle(new Color(0xc480ff), 4, new float[] { 25, 25 }));
    411                                                 if (name != null) {
    412                                                         Renderer.labelText(name, new Font("Arial", Font.ITALIC, 75), Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -40)));
    413                                                         Renderer.labelText("(Shoal)", new Font("Arial", Font.PLAIN, 60), Color.black, new Delta(Handle.BC));
    414                                                 }
    415                                         } else if (feature.geom.prim == Pflag.LINE) {
    416                                                 if (name != null) {
    417                                                         Renderer.lineText(name, new Font("Arial", Font.ITALIC, 75), Color.black, -40);
    418                                                         Renderer.lineText("(Shoal)", new Font("Arial", Font.PLAIN, 60), Color.black, 0);
    419                                                 }
    420                                         } else {
    421                                                 if (name != null) {
    422                                                         Renderer.labelText(name, new Font("Arial", Font.ITALIC, 75), Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -40)));
    423                                                         Renderer.labelText("(Shoal)", new Font("Arial", Font.PLAIN, 60), Color.black, new Delta(Handle.BC));
    424                                                 }
    425                                         }
    426                                 }
    427                                 break;
    428                         case SEA_GAT:
    429                         case SEA_NRRW:
    430                                 addName(12, new Font("Arial", Font.PLAIN, 100));
    431                                 break;
    432                         default:
    433                                 break;
    434                         }
    435                         break;
    436                 case SNDWAV:
    437                         if (Renderer.zoom >= 12) Renderer.fillPattern(Areas.Sandwaves);
    438                         break;
    439                 case WEDKLP:
    440                         if (Renderer.zoom >= 12) {
    441                                 switch ((CatWED) getAttEnum(feature.type, Att.CATWED)) {
    442                                 case WED_KELP:
    443                                         if (feature.geom.prim == Pflag.AREA) {
    444                                                 Renderer.fillPattern(Areas.KelpA);
    445                                         } else {
    446                                                 Renderer.symbol(Areas.KelpS);
    447                                         }
    448                                         break;
    449                                 default:
    450                                         break;
    451                                 }
    452                         }
    453                         break;
    454                 case SPLARE:
    455                         if (Renderer.zoom >= 12) {
    456                                 Renderer.symbol(Areas.Plane, new Scheme(Symbols.Msymb));
    457                                 Renderer.lineSymbols(Areas.Restricted, 0.5, Areas.LinePlane, null, 10, Symbols.Mline);
    458                         }
    459                         addName(15, new Font("Arial", Font.BOLD, 80), new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -90)));
    460                         break;
    461                 default:
    462                         break;
    463                 }
    464         }
    465        
    466         @SuppressWarnings("unchecked")
    467         private static void beacons() {
    468                 if ((Renderer.zoom >= 14) || ((Renderer.zoom >= 12) && ((feature.type == Obj.BCNLAT) || (feature.type == Obj.BCNCAR)))
    469                                 || ((Renderer.zoom >= 11) && ((feature.type == Obj.BCNSAW) || hasObject(Obj.RTPBCN)))) {
    470                         BcnSHP shape = (BcnSHP)getAttEnum(feature.type, Att.BCNSHP);
    471                         if (shape == BcnSHP.BCN_UNKN)
    472                                 shape = BcnSHP.BCN_PILE;
    473                         if ((shape == BcnSHP.BCN_WTHY) && (feature.type == Obj.BCNLAT)) {
    474                                 switch ((CatLAM) getAttEnum(feature.type, Att.CATLAM)) {
    475                                 case LAM_PORT:
    476                                         Renderer.symbol(Beacons.WithyPort);
    477                                         break;
    478                                 case LAM_STBD:
    479                                         Renderer.symbol(Beacons.WithyStarboard);
    480                                         break;
    481                                 default:
    482                                         Renderer.symbol(Beacons.Stake, getScheme(feature.type));
    483                                 }
    484                         } else if ((shape == BcnSHP.BCN_PRCH) && (feature.type == Obj.BCNLAT) && !(feature.objs.containsKey(Obj.TOPMAR))) {
    485                                 switch ((CatLAM) getAttEnum(feature.type, Att.CATLAM)) {
    486                                 case LAM_PORT:
    487                                         Renderer.symbol(Beacons.PerchPort);
    488                                         break;
    489                                 case LAM_STBD:
    490                                         Renderer.symbol(Beacons.PerchStarboard);
    491                                         break;
    492                                 default:
    493                                         Renderer.symbol(Beacons.Stake, getScheme(feature.type));
    494                                 }
    495                         } else {
    496                                 Renderer.symbol(Beacons.Shapes.get(shape), getScheme(feature.type));
    497                                 if (feature.objs.containsKey(Obj.TOPMAR)) {
    498                                         AttMap topmap = feature.objs.get(Obj.TOPMAR).get(0);
    499                                         if (topmap.containsKey(Att.TOPSHP)) {
    500                                                 Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>)(topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.TOPMAR), Topmarks.BeaconDelta);
    501                                         }
    502                                 } else if (feature.objs.containsKey(Obj.DAYMAR)) {
    503                                         AttMap topmap = feature.objs.get(Obj.DAYMAR).get(0);
    504                                         if (topmap.containsKey(Att.TOPSHP)) {
    505                                                 Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>)(topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.DAYMAR), Topmarks.BeaconDelta);
    506                                         }
    507                                 }
    508                         }
    509                         if (hasObject(Obj.NOTMRK))
    510                                 notices();
    511                         addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(60, -50)));
    512                         Signals.addSignals();
    513                 }
    514         }
    515        
    516         @SuppressWarnings("unchecked")
    517         private static void buoys() {
    518                 if ((Renderer.zoom >= 14) || ((Renderer.zoom >= 12) && ((feature.type == Obj.BOYLAT) || (feature.type == Obj.BOYCAR)))
    519                                 || ((Renderer.zoom >= 11) && ((feature.type == Obj.BOYSAW) || hasObject(Obj.RTPBCN)))) {
    520                         BoySHP shape = (BoySHP) getAttEnum(feature.type, Att.BOYSHP);
    521                         if (shape == BoySHP.BOY_UNKN) shape = BoySHP.BOY_PILR;
    522                         Renderer.symbol(Buoys.Shapes.get(shape), getScheme(feature.type));
    523                         if (feature.objs.containsKey(Obj.TOPMAR)) {
    524                                 AttMap topmap = feature.objs.get(Obj.TOPMAR).get(0);
    525                                 if (topmap.containsKey(Att.TOPSHP)) {
    526                                         Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>)(topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.TOPMAR), Topmarks.BuoyDeltas.get(shape));
    527                                 }
    528                         } else if (feature.objs.containsKey(Obj.DAYMAR)) {
    529                                 AttMap topmap = feature.objs.get(Obj.DAYMAR).get(0);
    530                                 if (topmap.containsKey(Att.TOPSHP)) {
    531                                         Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>)(topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.DAYMAR), Topmarks.BuoyDeltas.get(shape));
    532                                 }
    533                         }
    534                         addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(60, -50)));
    535                         Signals.addSignals();
    536                 }
    537         }
    538        
    539         private static void bridges() {
    540                 if (Renderer.zoom >= 16) {
    541                         double verclr, verccl, vercop, horclr;
    542                         AttMap atts = feature.objs.get(Obj.BRIDGE).get(0);
    543                         String vstr = "";
    544                         String hstr = "";
    545                         if (atts != null) {
    546                                 if (atts.containsKey(Att.HORCLR)) {
    547                                         horclr = (Double) atts.get(Att.HORCLR).val;
    548                                         hstr = String.valueOf(horclr);
    549                                 }
    550                                         if (atts.containsKey(Att.VERCLR)) {
    551                                                 verclr = (Double) atts.get(Att.VERCLR).val;
    552                                 } else {
    553                                         verclr = atts.containsKey(Att.VERCSA) ? (Double) atts.get(Att.VERCSA).val : 0;
    554                                 }
    555                                 verccl = atts.containsKey(Att.VERCCL) ? (Double) atts.get(Att.VERCCL).val : 0;
    556                                 vercop = atts.containsKey(Att.VERCOP) ? (Double) atts.get(Att.VERCOP).val : 0;
    557                                 if (verclr > 0) {
    558                                         vstr += String.valueOf(verclr);
    559                                 } else if (verccl > 0) {
    560                                         if (vercop == 0) {
    561                                                 vstr += String.valueOf(verccl) + "/-";
    562                                         } else {
    563                                                 vstr += String.valueOf(verccl) + "/" + String.valueOf(vercop);
    564                                         }
    565                                 }
    566                                 if (hstr.isEmpty() && !vstr.isEmpty()) {
    567                                         Renderer.labelText(vstr, new Font("Arial", Font.PLAIN, 30), Color.black, LabelStyle.VCLR, Color.black, Color.white, new Delta(Handle.CC));
    568                                 } else if (!hstr.isEmpty() && !vstr.isEmpty()) {
    569                                         Renderer.labelText(vstr, new Font("Arial", Font.PLAIN, 30), Color.black, LabelStyle.VCLR, Color.black, Color.white, new Delta(Handle.BC));
    570                                         Renderer.labelText(hstr, new Font("Arial", Font.PLAIN, 30), Color.black, LabelStyle.HCLR, Color.black, Color.white, new Delta(Handle.TC));
    571                                 } else if (!hstr.isEmpty() && vstr.isEmpty()) {
    572                                         Renderer.labelText(hstr, new Font("Arial", Font.PLAIN, 30), Color.black, LabelStyle.HCLR, Color.black, Color.white, new Delta(Handle.CC));
    573                                 }
    574                         }
    575                 }
    576         }
    577        
    578         private static void cables() {
    579                 if ((Renderer.zoom >= 16) && (feature.geom.length < 2)) {
    580                         if (feature.type == Obj.CBLSUB) {
    581                                 Renderer.lineSymbols(Areas.Cable, 0.0, null, null, 0, Symbols.Mline);
    582                         } else if (feature.type == Obj.CBLOHD) {
    583                                 AttMap atts = feature.objs.get(Obj.CBLOHD).get(0);
    584                                 if ((atts != null) && (atts.containsKey(Att.CATCBL)) && (atts.get(Att.CATCBL).val == CatCBL.CBL_POWR)) {
    585                                         Renderer.lineSymbols(Areas.CableDash, 0, Areas.CableDot, Areas.CableFlash, 2, Color.black);
    586                                 } else {
    587                                         Renderer.lineSymbols(Areas.CableDash, 0, Areas.CableDot, null, 2, Color.black);
    588                                 }
    589                                 if (atts != null) {
    590                                         if (atts.containsKey(Att.VERCLR)) {
    591                                                 Renderer.labelText(String.valueOf((Double) atts.get(Att.VERCLR).val), new Font("Arial", Font.PLAIN, 50), Color.black, LabelStyle.VCLR, Color.black, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0,25)));
    592                                         } else if (atts.containsKey(Att.VERCSA)) {
    593                                                 Renderer.labelText(String.valueOf((Double) atts.get(Att.VERCSA).val), new Font("Arial", Font.PLAIN, 50), Color.black, LabelStyle.PCLR, Color.black, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0,25)));
    594                                         }
    595                                 }
    596                         }
    597                 }
    598         }
    599        
    600         private static void callpoint() {
    601                 if (Renderer.zoom >= 14) {
    602                         Symbol symb = Harbours.CallPoint2;
    603                         TrfTRF trf = (TrfTRF) getAttEnum(feature.type, Att.TRAFIC);
    604                         if (trf != TrfTRF.TRF_TWOW) {
    605                                 symb = Harbours.CallPoint1;
    606                         }
    607                         Double orient = 0.0;
    608                         if ((orient = (Double) getAttVal(feature.type, Att.ORIENT)) == null) {
    609                                 orient = 0.0;
    610                         }
    611                         Renderer.symbol(symb, new Delta(Handle.CC, AffineTransform.getRotateInstance(Math.toRadians(orient))));
    612                         String chn;
    613                         if (!(chn = getAttStr(feature.type, Att.COMCHA)).isEmpty()) {
    614                                 Renderer.labelText(("Ch." + chn), new Font("Arial", Font.PLAIN, 50), Color.black, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0,50)));
    615                         }
    616                 }
    617         }
    618        
    619         private static void depths() {
    620                 switch (feature.type) {
    621                 case SOUNDG:
    622                         if ((Renderer.zoom >= 14) && hasAttribute(Obj.SOUNDG, Att.VALSOU)) {
    623                                 double depth = (double)getAttVal(Obj.SOUNDG, Att.VALSOU);
    624                                 String dstr = df.format(depth);
    625                                 String tok[] = dstr.split("[-.]");
    626                                 String ul = "";
    627                                 String id = tok[0];
    628                                 String dd = "";
    629                                 if (tok[0].equals("")) {
    630                                         for (int i = 0; i <  tok[1].length(); i++)
    631                                                 ul += "_";
    632                                         id = tok[1];
    633                                         dd = (tok.length == 3) ? tok[2] : "";
    634                                 } else {
    635                                         dd = (tok.length == 2) ? tok[1] : "";
    636                                 }
    637                                 Renderer.labelText(ul, new Font("Arial", Font.PLAIN, 30), Color.black, new Delta(Handle.RC, AffineTransform.getTranslateInstance(10,15)));
    638                                 Renderer.labelText(id, new Font("Arial", Font.PLAIN, 30), Color.black, new Delta(Handle.RC, AffineTransform.getTranslateInstance(10,0)));
    639                                 Renderer.labelText(dd, new Font("Arial", Font.PLAIN, 20), Color.black, new Delta(Handle.LC, AffineTransform.getTranslateInstance(15,10)));
    640                         }
    641                         break;
    642                 case DEPCNT:
    643                         break;
    644                 default:
    645                         break;
    646                 }
    647         }
    648        
    649         private static void distances() {
    650                 if (Renderer.zoom >= 14) {
    651                         if (!testAttribute(Obj.DISMAR, Att.CATDIS, CatDIS.DIS_NONI)) {
    652                                 Renderer.symbol(Harbours.DistanceI);
    653                         } else {
    654                                 Renderer.symbol(Harbours.DistanceU);
    655                         }
    656                         if (Renderer.zoom >= 15) {
    657                                 AttMap atts = getAtts(Obj.DISMAR, 0);
    658                                 if ((atts != null) && (atts.containsKey(Att.WTWDIS))) {
    659                                         Double dist = (Double) atts.get(Att.WTWDIS).val;
    660                                         String str = "";
    661                                         if (atts.containsKey(Att.HUNITS)) {
    662                                                 switch ((UniHLU) getAttEnum(Obj.DISMAR, Att.HUNITS)) {
    663                                                 case HLU_METR:
    664                                                         str += "m ";
    665                                                         break;
    666                                                 case HLU_FEET:
    667                                                         str += "ft ";
    668                                                         break;
    669                                                 case HLU_HMTR:
    670                                                         str += "hm ";
    671                                                         break;
    672                                                 case HLU_KMTR:
    673                                                         str += "km ";
    674                                                         break;
    675                                                 case HLU_SMIL:
    676                                                         str += "M ";
    677                                                         break;
    678                                                 case HLU_NMIL:
    679                                                         str += "NM ";
    680                                                         break;
    681                                                 default:
    682                                                         break;
    683                                                 }
    684                                         }
    685                                         str += String.format("%1.0f", dist);
    686                                         Renderer.labelText(str, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.CC, AffineTransform.getTranslateInstance(0, 45)));
    687                                 }
    688                         }
    689                 }
    690         }
    691        
    692         @SuppressWarnings("unchecked")
    693         private static void floats() {
    694                 if ((Renderer.zoom >= 12) || ((Renderer.zoom >= 11) && ((feature.type == Obj.LITVES) || (feature.type == Obj.BOYINB) || hasObject(Obj.RTPBCN)))) {
    695                         switch (feature.type) {
    696                         case LITVES:
    697                                 Renderer.symbol(Buoys.Super, getScheme(feature.type));
    698                                 break;
    699                         case LITFLT:
    700                                 Renderer.symbol(Buoys.Float, getScheme(feature.type));
    701                                 break;
    702                         case BOYINB:
    703                                 Renderer.symbol(Buoys.Super, getScheme(feature.type));
    704                                 break;
    705                         default:
    706                                 break;
    707                         }
    708                         if (feature.objs.containsKey(Obj.TOPMAR)) {
    709                                 AttMap topmap = feature.objs.get(Obj.TOPMAR).get(0);
    710                                 if (topmap.containsKey(Att.TOPSHP)) {
    711                                         Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>)(topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.TOPMAR), Topmarks.FloatDelta);
    712                                 }
    713                         } else if (feature.objs.containsKey(Obj.DAYMAR)) {
    714                                 AttMap topmap = feature.objs.get(Obj.DAYMAR).get(0);
    715                                 if (topmap.containsKey(Att.TOPSHP)) {
    716                                         Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>)(topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.DAYMAR), Topmarks.FloatDelta);
    717                                 }
    718                         }
    719                         addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(20, -50)));
    720                         Signals.addSignals();
    721                 }
    722         }
    723        
    724         private static void gauges() {
    725                 if (Renderer.zoom >= 14) {
    726                         Renderer.symbol(Harbours.TideGauge);
    727                         addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(20, -50)));
    728                         Signals.addSignals();
    729                 }
    730         }
    731        
    732         @SuppressWarnings("unchecked")
    733         private static void harbours() {
    734                 String name = getName();
    735                 switch (feature.type) {
    736                 case ACHBRT:
    737                         if (Renderer.zoom >= 14) {
    738                                 Renderer.symbol(Harbours.Anchor, new Scheme(Symbols.Msymb));
    739                                 if (Renderer.zoom >= 15) {
    740                                         Renderer.labelText(name == null ? "" : name, new Font("Arial", Font.PLAIN, 30), Symbols.Msymb, LabelStyle.RRCT, Symbols.Msymb, Color.white, new Delta(Handle.BC));
    741                                 }
    742                         }
    743                         if (getAttVal(Obj.ACHBRT, Att.RADIUS) != null) {
    744                                 double radius;
    745                                 if ((radius = (Double) getAttVal(Obj.ACHBRT, Att.RADIUS)) != 0) {
    746                                         UniHLU units = (UniHLU) getAttEnum(Obj.ACHBRT, Att.HUNITS);
    747                                         if (units == UniHLU.HLU_UNKN) {
    748                                                 units = UniHLU.HLU_METR;
    749                                         }
    750                                         Renderer.lineCircle(new LineStyle(Symbols.Mline, 4, new float[] { 10, 10 }, null), radius, units);
    751                                 }
    752                         }
    753                         break;
    754                 case ACHARE:
    755                         if (Renderer.zoom >= 12) {
    756                                 if (feature.geom.prim != Pflag.AREA) {
    757                                         Renderer.symbol(Harbours.Anchorage, new Scheme(Color.black));
    758                                 } else {
    759                                         Renderer.symbol(Harbours.Anchorage, new Scheme(Symbols.Mline));
    760                                         Renderer.lineSymbols(Areas.Restricted, 1.0, Areas.LineAnchor, null, 10, Symbols.Mline);
    761                                 }
    762                                 addName(15, new Font("Arial", Font.BOLD, 60), Symbols.Mline, new Delta(Handle.LC, AffineTransform.getTranslateInstance(70, 0)));
    763                                 ArrayList<StsSTS> sts = (ArrayList<StsSTS>) getAttList(Obj.ACHARE, Att.STATUS);
    764                                 if ((Renderer.zoom >= 15) && (sts.contains(StsSTS.STS_RESV))) {
    765                                         Renderer.labelText("Reserved", new Font("Arial", Font.PLAIN, 50), Symbols.Mline, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, 60)));
    766                                 }
    767                                 ArrayList<CatACH> cats = (ArrayList<CatACH>) getAttList(Obj.ACHARE, Att.CATACH);
    768                                 int dy = (cats.size() - 1) * -30;
    769                                 for (CatACH cat : cats) {
    770                                         switch (cat) {
    771                                         case ACH_DEEP:
    772                                                 Renderer.labelText("DW", new Font("Arial", Font.BOLD, 50), Symbols.Msymb, new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
    773                                                 dy += 60;
    774                                                 break;
    775                                         case ACH_TANK:
    776                                                 Renderer.labelText("Tanker", new Font("Arial", Font.BOLD, 50), Symbols.Msymb, new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
    777                                                 dy += 60;
    778                                                 break;
    779                                         case ACH_H24P:
    780                                                 Renderer.labelText("24h", new Font("Arial", Font.BOLD, 50), Symbols.Msymb, new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
    781                                                 dy += 60;
    782                                                 break;
    783                                         case ACH_EXPL:
    784                                                 Renderer.symbol(Harbours.Explosives, new Scheme(Symbols.Msymb), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
    785                                                 dy += 60;
    786                                                 break;
    787                                         case ACH_QUAR:
    788                                                 Renderer.symbol(Harbours.Hospital, new Scheme(Symbols.Msymb), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
    789                                                 dy += 60;
    790                                                 break;
    791                                         case ACH_SEAP:
    792                                                 Renderer.symbol(Areas.Seaplane, new Scheme(Symbols.Msymb), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
    793                                                 dy += 60;
    794                                                 break;
    795                                         default:
    796                                         }
    797                                 }
    798                         }
    799                         break;
    800                 case BERTHS:
    801                         if (Renderer.zoom >= 14) {
    802                                 Renderer.lineVector(new LineStyle(Symbols.Mline, 6, new float[] { 20, 20 }));
    803                                 Renderer.labelText(name == null ? " " : name, new Font("Arial", Font.PLAIN, 40), Symbols.Msymb, LabelStyle.RRCT, Symbols.Mline, Color.white);
    804                         }
    805                         break;
    806                 case BUISGL:
    807                         if (Renderer.zoom >= 16) {
    808                                 ArrayList<Symbol> symbols = new ArrayList<>();
    809                                 ArrayList<FncFNC> fncs = (ArrayList<FncFNC>) getAttList(Obj.BUISGL, Att.FUNCTN);
    810                                 for (FncFNC fnc : fncs) {
    811                                         symbols.add(Landmarks.Funcs.get(fnc));
    812                                 }
    813                                 if (feature.objs.containsKey(Obj.SMCFAC)) {
    814                                         ArrayList<CatSCF> scfs = (ArrayList<CatSCF>) getAttList(Obj.SMCFAC, Att.CATSCF);
    815                                         for (CatSCF scf : scfs) {
    816                                                 symbols.add(Facilities.Cats.get(scf));
    817                                         }
    818                                 }
    819                                 Renderer.cluster(symbols);
    820                         }
    821                         break;
    822                 case HRBFAC:
    823                         if (Renderer.zoom >= 12) {
    824                                 ArrayList<CatHAF> cathaf = (ArrayList<CatHAF>) getAttList(Obj.HRBFAC, Att.CATHAF);
    825                                 if (cathaf.size() == 1) {
    826                                         switch (cathaf.get(0)) {
    827                                         case HAF_MRNA:
    828                                                 Renderer.symbol(Harbours.Marina);
    829                                                 break;
    830                                         case HAF_MANF:
    831                                                 Renderer.symbol(Harbours.MarinaNF);
    832                                                 break;
    833                                         case HAF_FISH:
    834                                                 Renderer.symbol(Harbours.Fishing);
    835                                                 break;
    836                                         default:
    837                                                 Renderer.symbol(Harbours.Harbour);
    838                                                 break;
    839                                         }
    840                                 } else {
    841                                         Renderer.symbol(Harbours.Harbour);
    842                                 }
    843                         }
    844                         break;
    845                 default:
    846                         break;
    847                 }
    848         }
    849        
    850         @SuppressWarnings("unchecked")
    851         private static void highways() {
    852                 switch (feature.type) {
    853                 case ROADWY:
    854                         ArrayList<CatROD> cat = (ArrayList<CatROD>) (getAttList(Obj.ROADWY, Att.CATROD));
    855                         if (cat.size() > 0) {
    856                                 switch (cat.get(0)) {
    857                                 case ROD_MWAY:
    858                                         Renderer.lineVector(new LineStyle(Color.black, 20));
    859                                         break;
    860                                 case ROD_MAJR:
    861                                         Renderer.lineVector(new LineStyle(Color.black, 15));
    862                                         break;
    863                                 case ROD_MINR:
    864                                         Renderer.lineVector(new LineStyle(Color.black, 10));
    865                                         break;
    866                                 default:
    867                                         Renderer.lineVector(new LineStyle(Color.black, 5));
    868                                 }
    869                         } else {
    870                                 Renderer.lineVector(new LineStyle(Color.black, 5));
    871                         }
    872                         break;
    873                 case RAILWY:
    874                         Renderer.lineVector(new LineStyle(Color.gray, 10));
    875                         Renderer.lineVector(new LineStyle(Color.black, 10, new float[] { 30, 30 }));
    876                         break;
    877                 default:
    878                 }
    879         }
    880        
    881         @SuppressWarnings("unchecked")
    882         private static void landmarks() {
    883                 if (!hasAttribute(Obj.LNDMRK, Att.CATLMK)
    884                                 && (!hasAttribute(Obj.LNDMRK, Att.FUNCTN) || testAttribute(Obj.LNDMRK, Att.FUNCTN, FncFNC.FNC_LGHT))
    885                                 && hasObject(Obj.LIGHTS))
    886                         lights();
    887                 else if (Renderer.zoom >= 12) {
    888                         switch (feature.type) {
    889                         case LNDMRK:
    890                         ArrayList<CatLMK> cats = (ArrayList<CatLMK>) getAttList(feature.type, Att.CATLMK);
    891                         Symbol catSym = Landmarks.Shapes.get(cats.get(0));
    892                         ArrayList<FncFNC> fncs = (ArrayList<FncFNC>) getAttList(feature.type, Att.FUNCTN);
    893                         Symbol fncSym = Landmarks.Funcs.get(fncs.get(0));
    894                         if ((fncs.get(0) == FncFNC.FNC_CHCH) && (cats.get(0) == CatLMK.LMK_TOWR))
    895                                 catSym = Landmarks.ChurchTower;
    896                         if (cats.get(0) == CatLMK.LMK_RADR)
    897                                 fncSym = Landmarks.RadioTV;
    898                         Renderer.symbol(catSym);
    899                         Renderer.symbol(fncSym);
    900                         break;
    901                         case SILTNK:
    902                                 if (testAttribute(feature.type, Att.CATSIL, CatSIL.SIL_WTRT))
    903                                         Renderer.symbol(Landmarks.WaterTower);
    904                                 break;
    905                         default:
    906                                 break;
    907                         }
    908                         if (Renderer.zoom >= 15)
    909                                 addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(60, -50)));
    910                         Signals.addSignals();
    911                 }
    912         }
    913        
    914         @SuppressWarnings("unchecked")
    915         private static void points() {
    916                 boolean ok = false;
    917                 switch (feature.type) {
    918                 case FOGSIG:
    919                         if (Renderer.zoom >= 12) {
    920                                 if (feature.objs.containsKey(Obj.LIGHTS))
    921                                         lights();
    922                                 else
    923                                         Renderer.symbol(Harbours.Post);
    924                                 ok = true;
    925                         }
    926                         break;
    927                 default:
    928                         if (Renderer.zoom >= 14) {
    929                                 if (feature.objs.containsKey(Obj.LIGHTS))
    930                                         lights();
    931                                 else
    932                                         Renderer.symbol(Harbours.Post);
    933                                 ok = true;
    934                         }
    935                         break;
    936                 }
    937                 if (ok) {
    938                         if (feature.objs.containsKey(Obj.TOPMAR)) {
    939                                 AttMap topmap = feature.objs.get(Obj.TOPMAR).get(0);
    940                                 if (topmap.containsKey(Att.TOPSHP)) {
    941                                         Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.TOPMAR), null);
    942                                 }
    943                         } else if (feature.objs.containsKey(Obj.DAYMAR)) {
    944                                 AttMap topmap = feature.objs.get(Obj.DAYMAR).get(0);
    945                                 if (topmap.containsKey(Att.TOPSHP)) {
    946                                         Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.DAYMAR), null);
    947                                 }
    948                         }
    949                         Signals.addSignals();
    950                 }
    951         }
    952        
    953         @SuppressWarnings("unchecked")
    954         private static void lights() {
    955                 boolean ok = false;
    956                 switch (feature.type) {
    957                 case LITMAJ:
    958                 case LNDMRK:
    959                         if (Renderer.zoom >= 12) {
    960                                 Renderer.symbol(Beacons.LightMajor);
    961                                 ok = true;
    962                         }
    963                         break;
    964                 case LITMIN:
    965                 case LIGHTS:
    966                 case PILPNT:
    967                         if (Renderer.zoom >= 14) {
    968                                 Renderer.symbol(Beacons.LightMinor);
    969                                 ok = true;
    970                         }
    971                         break;
    972                 default:
    973                         break;
    974                 }
    975                 if (ok) {
    976                         if (feature.objs.containsKey(Obj.TOPMAR)) {
    977                                 AttMap topmap = feature.objs.get(Obj.TOPMAR).get(0);
    978                                 if (topmap.containsKey(Att.TOPSHP)) {
    979                                         Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.TOPMAR), Topmarks.LightDelta);
    980                                 }
    981                         } else if (feature.objs.containsKey(Obj.DAYMAR)) {
    982                                 AttMap topmap = feature.objs.get(Obj.DAYMAR).get(0);
    983                                 if (topmap.containsKey(Att.TOPSHP)) {
    984                                         Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.DAYMAR), Topmarks.LightDelta);
    985                                 }
    986                         }
    987                         Signals.addSignals();
    988                 }
    989         }
    990 
    991         @SuppressWarnings("unchecked")
    992         private static void marinas() {
    993                 if (Renderer.zoom >= 16) {
    994                         ArrayList<Symbol> symbols = new ArrayList<>();
    995                         ArrayList<CatSCF> scfs = (ArrayList<CatSCF>) getAttList(Obj.SMCFAC, Att.CATSCF);
    996                         for (CatSCF scf : scfs) {
    997                                 symbols.add(Facilities.Cats.get(scf));
    998                         }
    999                         Renderer.cluster(symbols);
    1000                 }
    1001         }
    1002        
    1003         private static void moorings() {
    1004                 if (Renderer.zoom >= 14) {
    1005                         switch ((CatMOR) getAttEnum(feature.type, Att.CATMOR)) {
    1006                         case MOR_DLPN:
    1007                                 Renderer.symbol(Harbours.Dolphin);
    1008                                 break;
    1009                         case MOR_DDPN:
    1010                                 Renderer.symbol(Harbours.DeviationDolphin);
    1011                                 break;
    1012                         case MOR_BLRD:
    1013                         case MOR_POST:
    1014                                 Renderer.symbol(Harbours.Bollard);
    1015                                 break;
    1016                         case MOR_BUOY:
    1017                                 BoySHP shape = (BoySHP) getAttEnum(feature.type, Att.BOYSHP);
    1018                                 if (shape == BoySHP.BOY_UNKN) {
    1019                                         shape = BoySHP.BOY_SPHR;
    1020                                 }
    1021                                 Renderer.symbol(Buoys.Shapes.get(shape), getScheme(feature.type));
    1022                                 Renderer.symbol(Topmarks.TopMooring, Topmarks.BuoyDeltas.get(shape));
    1023                                 break;
    1024                         default:
    1025                                 break;
    1026                         }
    1027                         Signals.addSignals();
    1028                 }
    1029         }
    1030 
    1031         @SuppressWarnings("unchecked")
    1032         private static void notices() {
    1033                 if (Renderer.zoom >= 14) {
    1034                         double dx = 0.0, dy = 0.0;
    1035                         switch (feature.type) {
    1036                         case BCNCAR:
    1037                         case BCNISD:
    1038                         case BCNLAT:
    1039                         case BCNSAW:
    1040                         case BCNSPP:
    1041                                 if (testAttribute(Obj.TOPMAR, Att.TOPSHP, TopSHP.TOP_BORD) || testAttribute(Obj.DAYMAR, Att.TOPSHP, TopSHP.TOP_BORD)) {
    1042                                         dy = -100.0;
    1043                                 } else {
    1044                                         dy = -45.0;
    1045                                 }
    1046                                 break;
    1047                         case NOTMRK:
    1048                                 dy = 0.0;
    1049                                 break;
    1050                         default:
    1051                                 return;
    1052                         }
    1053                         MarSYS sys = MarSYS.SYS_CEVN;
    1054                         BnkWTW bnk = BnkWTW.BWW_UNKN;
    1055                         AttVal<?> att = feature.atts.get(Att.MARSYS);
    1056                         if (att != null) sys = (MarSYS)att.val;
    1057                         att = feature.atts.get(Att.BNKWTW);
    1058                         if (att != null) bnk = (BnkWTW)att.val;
    1059                         ObjTab objs = feature.objs.get(Obj.NOTMRK);
    1060                         int n = objs.size();
    1061                         if (n > 5) {
    1062                                 Renderer.symbol(Notices.Notice, new Delta(Handle.CC, AffineTransform.getTranslateInstance(dx, dy)));
    1063                         } else {
    1064                                 int i = 0;
    1065                                 for (AttMap atts : objs.values()) {
    1066                                         if (atts.get(Att.MARSYS) != null) sys = ((ArrayList<MarSYS>)(atts.get(Att.MARSYS).val)).get(0);
    1067                                         if (atts.get(Att.BNKWTW) != null) bnk = ((ArrayList<BnkWTW>)(atts.get(Att.BNKWTW).val)).get(0);
    1068                                         CatNMK cat = CatNMK.NMK_UNKN;
    1069                                         if (atts.get(Att.CATNMK) != null) cat = ((ArrayList<CatNMK>)(atts.get(Att.CATNMK).val)).get(0);
    1070                                         Symbol sym = Notices.getNotice(cat, sys, bnk);
    1071                                         Scheme sch = Notices.getScheme(sys, bnk);
    1072                                         ArrayList<AddMRK> add = new ArrayList<>();
    1073                                         if (atts.get(Att.ADDMRK) != null) add = (ArrayList<AddMRK>)(atts.get(Att.ADDMRK).val);
    1074                                         Handle h = Handle.CC;
    1075                                         double ax = 0.0;
    1076                                         double ay = 0.0;
    1077                                         switch (i) {
    1078                                         case 0:
    1079                                                 if (n != 1) h = null;
    1080                                                 break;
    1081                                         case 1:
    1082                                                 if (n <= 3) {
    1083                                                         h = Handle.RC;
    1084                                                         ax = -30;
    1085                                                         ay = dy;
    1086                                                 }
    1087                                                 else {
    1088                                                         h = Handle.BR;
    1089                                                 }
    1090                                                 break;
    1091                                         case 2:
    1092                                                 if (n <= 3)
    1093                                                         h = Handle.LC;
    1094                                                 else
    1095                                                         h = Handle.BL;
    1096                                                 break;
    1097                                         case 3:
    1098                                                 if (n == 4)
    1099                                                         h = Handle.TC;
    1100                                                 else
    1101                                                         h = Handle.TR;
    1102                                                 break;
    1103                                         case 4:
    1104                                                 h = Handle.TL;
    1105                                                 break;
    1106                                         }
    1107                                         if (h != null) {
    1108                                                 Renderer.symbol(sym, sch, new Delta(h, AffineTransform.getTranslateInstance(dx, dy)));
    1109                                                 if (!add.isEmpty()) Renderer.symbol(Notices.NoticeBoard, new Delta(Handle.BC, AffineTransform.getTranslateInstance(ax, ay - 30)));
    1110                                         }
    1111                                         i++;
    1112                                 }
    1113                         }
    1114                 }
    1115         }
    1116 
    1117         private static void obstructions() {
    1118                 if ((Renderer.zoom >= 12) && (feature.type == Obj.OBSTRN)) {
    1119                         switch ((CatOBS) getAttEnum(feature.type, Att.CATOBS)) {
    1120                         case OBS_BOOM:
    1121                                 Renderer.lineVector(new LineStyle(Color.black, 5, new float[] { 20, 20 }, null));
    1122                                 if (Renderer.zoom >= 15) {
    1123                                         Renderer.lineText("Boom", new Font("Arial", Font.PLAIN, 80), Color.black, -20);
    1124                                 }
    1125                         default:
    1126                                 break;
    1127                         }
    1128                 }
    1129                 if ((Renderer.zoom >= 14) && (feature.type == Obj.UWTROC)) {
    1130                         switch ((WatLEV) getAttEnum(feature.type, Att.WATLEV)) {
    1131                         case LEV_CVRS:
    1132                                 Renderer.symbol(Areas.RockC);
    1133                                 break;
    1134                         case LEV_AWSH:
    1135                                 Renderer.symbol(Areas.RockA);
    1136                                 break;
    1137                         default:
    1138                                 Renderer.symbol(Areas.Rock);
    1139                         }
    1140                 } else {
    1141                         Renderer.symbol(Areas.Rock);
    1142                 }
    1143         }
    1144 
    1145         private static void pipelines() {
    1146                 if ((Renderer.zoom >= 16) && (feature.geom.length < 2)) {
    1147                         if (feature.type == Obj.PIPSOL) {
    1148                                 Renderer.lineSymbols(Areas.Pipeline, 1.0, null, null, 0, Symbols.Mline);
    1149                         } else if (feature.type == Obj.PIPOHD) {
    1150                                 Renderer.lineVector(new LineStyle(Color.black, 8));
    1151                                 AttMap atts = feature.atts;
    1152                                 double verclr = 0;
    1153                                 if (atts != null) {
    1154                                         if (atts.containsKey(Att.VERCLR)) {
    1155                                                 verclr = (Double) atts.get(Att.VERCLR).val;
    1156                                         } else {
    1157                                                 verclr = atts.containsKey(Att.VERCSA) ? (Double) atts.get(Att.VERCSA).val : 0;
    1158                                         }
    1159                                         if (verclr > 0) {
    1160                                                 Renderer.labelText(String.valueOf(verclr), new Font("Arial", Font.PLAIN, 50), Color.black, LabelStyle.VCLR, Color.black, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0,25)));
    1161                                         }
    1162                                 }
    1163                         }
    1164                 }
    1165         }
    1166 
    1167         @SuppressWarnings("unchecked")
    1168         private static void platforms() {
    1169                 ArrayList<CatOFP> cats = (ArrayList<CatOFP>) getAttList(Obj.OFSPLF, Att.CATOFP);
    1170                 if ((CatOFP) cats.get(0) == CatOFP.OFP_FPSO)
    1171                         Renderer.symbol(Buoys.Storage);
    1172                 else
    1173                         Renderer.symbol(Landmarks.Platform);
    1174                 addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(20, -50)));
    1175                 Signals.addSignals();
    1176         }
    1177 
    1178         private static void ports() {
    1179                 if (Renderer.zoom >= 14) {
    1180                         if (feature.type == Obj.CRANES) {
    1181                                 if ((CatCRN) getAttEnum(feature.type, Att.CATCRN) == CatCRN.CRN_CONT)
    1182                                         Renderer.symbol(Harbours.ContainerCrane);
    1183                                 else
    1184                                         Renderer.symbol(Harbours.PortCrane);
    1185                         } else if (feature.type == Obj.HULKES) {
    1186                                 Renderer.lineVector(new LineStyle(Color.black, 4, null, new Color(0xffe000)));
    1187                                 addName(15, new Font("Arial", Font.BOLD, 40));
    1188                         }
    1189                 }
    1190         }
    1191 
    1192         private static void separation() {
    1193                 switch (feature.type) {
    1194                 case TSEZNE:
    1195                 case TSSCRS:
    1196                 case TSSRON:
    1197                         if (Renderer.zoom <= 15)
    1198                                 Renderer.lineVector(new LineStyle(Symbols.Mtss));
    1199                         else
    1200                                 Renderer.lineVector(new LineStyle(Symbols.Mtss, 20, null, null));
    1201                         addName(10, new Font("Arial", Font.BOLD, 150), Symbols.Mline);
    1202                         break;
    1203                 case TSELNE:
    1204                         Renderer.lineVector(new LineStyle(Symbols.Mtss, 20, null, null));
    1205                         break;
    1206                 case TSSLPT:
    1207                         Renderer.lineSymbols(Areas.LaneArrow, 0.5, null, null, 0, Symbols.Mtss);
    1208                         break;
    1209                 case TSSBND:
    1210                         Renderer.lineVector(new LineStyle(Symbols.Mtss, 20, new float[] { 40, 40 }, null));
    1211                         break;
    1212                 case ISTZNE:
    1213                         Renderer.lineSymbols(Areas.Restricted, 1.0, null, null, 0, Symbols.Mtss);
    1214                         break;
    1215                 default:
    1216                         break;
    1217                 }
    1218         }
    1219 
    1220         @SuppressWarnings("unchecked")
    1221         private static void shoreline() {
    1222                 CatSLC cat = (CatSLC) getAttEnum(feature.type, Att.CATSLC);
    1223                 if ((Renderer.context.ruleset() == RuleSet.ALL) || (Renderer.context.ruleset() == RuleSet.BASE)) {
    1224                         if ((cat != CatSLC.SLC_SWAY) && (cat != CatSLC.SLC_TWAL)) {
    1225                                 if (Renderer.zoom >= 12) {
    1226                                         Renderer.lineVector(new LineStyle(Color.black, 10, Symbols.Yland));
    1227                                 } else {
    1228                                         Renderer.lineVector(new LineStyle(Symbols.Yland));
    1229                                 }
    1230                         }
    1231                 }
    1232                 if ((Renderer.context.ruleset() == RuleSet.ALL) || (Renderer.context.ruleset() == RuleSet.SEAMARK)) {
    1233                         if (Renderer.zoom >= 12) {
    1234                                 switch (cat) {
    1235                                 case SLC_TWAL:
    1236                                         WatLEV lev = (WatLEV) getAttEnum(feature.type, Att.WATLEV);
    1237                                         if (lev == WatLEV.LEV_CVRS) {
    1238                                                 Renderer.lineVector(new LineStyle(Color.black, 10, new float[] { 40, 40 }, null));
    1239                                                 if (Renderer.zoom >= 15)
    1240                                                         Renderer.lineText("(covers)", new Font("Arial", Font.PLAIN, 60), Color.black, 80);
    1241                                         } else {
    1242                                                 Renderer.lineVector(new LineStyle(Color.black, 10, null, null));
    1243                                         }
    1244                                         if (Renderer.zoom >= 15)
    1245                                                 Renderer.lineText("Training Wall", new Font("Arial", Font.PLAIN, 60), Color.black, -30);
    1246                                         break;
    1247                                 case SLC_SWAY:
    1248                                         Renderer.lineVector(new LineStyle(Color.black, 2, null, new Color(0xffe000)));
    1249                                         if ((Renderer.zoom >= 16) && feature.objs.containsKey(Obj.SMCFAC)) {
    1250                                                 ArrayList<Symbol> symbols = new ArrayList<>();
    1251                                                 ArrayList<CatSCF> scfs = (ArrayList<CatSCF>) getAttList(Obj.SMCFAC, Att.CATSCF);
    1252                                                 for (CatSCF scf : scfs) {
    1253                                                         symbols.add(Facilities.Cats.get(scf));
    1254                                                 }
    1255                                                 Renderer.cluster(symbols);
    1256                                         }
    1257                                         break;
    1258                                 default:
    1259                                         break;
    1260                                 }
    1261                         }
    1262                 }
    1263         }
    1264 
    1265         @SuppressWarnings("unchecked")
    1266         private static void stations() {
    1267                 if (Renderer.zoom >= 14) {
    1268                         String str = "";
    1269                         switch (feature.type) {
    1270                         case SISTAT:
    1271                                 Renderer.symbol(Harbours.SignalStation);
    1272                                 str = "SS";
    1273                                 ArrayList<CatSIT> tcats = (ArrayList<CatSIT>) getAttList(Obj.SISTAT, Att.CATSIT);
    1274                                 switch (tcats.get(0)) {
    1275                                 case SIT_IPT:
    1276                                         str += "(INT)";
    1277                                         break;
    1278                                 case SIT_PRTE:
    1279                                         str += "(Traffic)";
    1280                                         break;
    1281                                 case SIT_PRTC:
    1282                                         str += "(Port Control)";
    1283                                         break;
    1284                                 case SIT_LOCK:
    1285                                         str += "(Lock)";
    1286                                         break;
    1287                                 case SIT_BRDG:
    1288                                         str += "(Bridge)";
    1289                                         break;
    1290                                 default:
    1291                                         break;
    1292                                 }
    1293                                 break;
    1294                         case SISTAW:
    1295                                 Renderer.symbol(Harbours.SignalStation);
    1296                                 str = "SS";
    1297                                 str = "SS";
    1298                                 ArrayList<CatSIW> wcats = (ArrayList<CatSIW>) getAttList(Obj.SISTAW, Att.CATSIW);
    1299                                 switch (wcats.get(0)) {
    1300                                 case SIW_STRM:
    1301                                         str += "(Storm)";
    1302                                         break;
    1303                                 case SIW_WTHR:
    1304                                         str += "(Weather)";
    1305                                         break;
    1306                                 case SIW_ICE:
    1307                                         str += "(Ice)";
    1308                                         break;
    1309                                 case SIW_TIDG:
    1310                                         str = "Tide gauge";
    1311                                         break;
    1312                                 case SIW_TIDS:
    1313                                         str = "Tide scale";
    1314                                         break;
    1315                                 case SIW_TIDE:
    1316                                         str += "(Tide)";
    1317                                         break;
    1318                                 case SIW_TSTR:
    1319                                         str += "(Stream)";
    1320                                         break;
    1321                                 case SIW_DNGR:
    1322                                         str += "(Danger)";
    1323                                         break;
    1324                                 case SIW_MILY:
    1325                                         str += "(Firing)";
    1326                                         break;
    1327                                 case SIW_TIME:
    1328                                         str += "(Time)";
    1329                                         break;
    1330                                 default:
    1331                                         break;
    1332                                 }
    1333                                 break;
    1334                         case RDOSTA:
    1335                         case RTPBCN:
    1336                                 Renderer.symbol(Harbours.SignalStation);
    1337                                 Renderer.symbol(Beacons.RadarStation);
    1338                                 break;
    1339                         case RADRFL:
    1340                                 Renderer.symbol(Topmarks.RadarReflector);
    1341                                 break;
    1342                         case RADSTA:
    1343                                 Renderer.symbol(Harbours.SignalStation);
    1344                                 Renderer.symbol(Beacons.RadarStation);
    1345                                 Renderer.labelText("Ra", new Font("Arial", Font.PLAIN, 40), Symbols.Msymb, new Delta(Handle.TR, AffineTransform.getTranslateInstance(-30, -70)));
    1346                                 break;
    1347                         case PILBOP:
    1348                                 Renderer.symbol(Harbours.Pilot);
    1349                                 addName(15, new Font("Arial", Font.BOLD, 40), Symbols.Msymb , new Delta(Handle.LC, AffineTransform.getTranslateInstance(70, -40)));
    1350                                 CatPIL cat = (CatPIL) getAttEnum(feature.type, Att.CATPIL);
    1351                                 if (cat == CatPIL.PIL_HELI) {
    1352                                         Renderer.labelText("H", new Font("Arial", Font.PLAIN, 40), Symbols.Msymb, new Delta(Handle.LC, AffineTransform.getTranslateInstance(70, 0)));
    1353                                 }
    1354                                 break;
    1355                         case CGUSTA:
    1356                                 Renderer.symbol(Harbours.SignalStation);
    1357                                 str = "CG";
    1358                           if (feature.objs.containsKey(Obj.RSCSTA)) Renderer.symbol(Harbours.Rescue, new Delta(Handle.CC, AffineTransform.getTranslateInstance(130, 0)));
    1359                                 break;
    1360                         case RSCSTA:
    1361                                 Renderer.symbol(Harbours.Rescue);
    1362                                 break;
    1363                         default:
    1364                                 break;
    1365                         }
    1366                         if ((Renderer.zoom >= 15) && !str.isEmpty()) {
    1367                                 Renderer.labelText(str, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.LC, AffineTransform.getTranslateInstance(40, 0)));
    1368                         }
    1369                         Signals.addSignals();
    1370                 }
    1371         }
    1372 
    1373         private static void transits() {
    1374           if (Renderer.zoom >= 14) {
    1375                 if (feature.type == Obj.RECTRC) Renderer.lineVector (new LineStyle(Color.black, 10, null, null));
    1376                 else if (feature.type == Obj.NAVLNE) Renderer.lineVector (new LineStyle(Color.black, 10, new float[] { 25, 25 }, null));
    1377           }
    1378                 if (Renderer.zoom >= 15) {
    1379                         String str = "";
    1380                         String name = getName();
    1381                         if (name != null)
    1382                                 str += name + " ";
    1383                         Double ort;
    1384                         if ((ort = (Double) getAttVal(feature.type, Att.ORIENT)) != null) {
    1385                                 str += df.format(ort) + "º";
    1386                                 if (!str.isEmpty())
    1387                                         Renderer.lineText(str, new Font("Arial", Font.PLAIN, 80), Color.black, -20);
    1388                         }
    1389                 }
    1390         }
    1391 
    1392         private static void waterways() {
    1393                 Renderer.lineVector(new LineStyle(Symbols.Bwater, 20, (feature.geom.prim == Pflag.AREA) ? Symbols.Bwater : null));
    1394         }
    1395 
    1396         private static void wrecks() {
    1397                 if (Renderer.zoom >= 14) {
    1398                         switch ((CatWRK) getAttEnum(feature.type, Att.CATWRK)) {
    1399                         case WRK_DNGR:
    1400                         case WRK_MSTS:
    1401                                 Renderer.symbol(Areas.WreckD);
    1402                                 break;
    1403                         case WRK_HULS:
    1404                                 Renderer.symbol(Areas.WreckS);
    1405                                 break;
    1406                         default:
    1407                                 Renderer.symbol(Areas.WreckND);
    1408                         }
    1409                 }
    1410         }
     84
     85    static final DecimalFormat df = new DecimalFormat("#.#");
     86
     87    static final EnumMap<ColCOL, Color> bodyColours = new EnumMap<>(ColCOL.class);
     88    static {
     89        bodyColours.put(ColCOL.COL_UNK, new Color(0, true));
     90        bodyColours.put(ColCOL.COL_WHT, new Color(0xffffff));
     91        bodyColours.put(ColCOL.COL_BLK, new Color(0x000000));
     92        bodyColours.put(ColCOL.COL_RED, new Color(0xd40000));
     93        bodyColours.put(ColCOL.COL_GRN, new Color(0x00d400));
     94        bodyColours.put(ColCOL.COL_BLU, Color.blue);
     95        bodyColours.put(ColCOL.COL_YEL, new Color(0xffd400));
     96        bodyColours.put(ColCOL.COL_GRY, Color.gray);
     97        bodyColours.put(ColCOL.COL_BRN, new Color(0x8b4513));
     98        bodyColours.put(ColCOL.COL_AMB, new Color(0xfbf00f));
     99        bodyColours.put(ColCOL.COL_VIO, new Color(0xee82ee));
     100        bodyColours.put(ColCOL.COL_ORG, Color.orange);
     101        bodyColours.put(ColCOL.COL_MAG, new Color(0xf000f0));
     102        bodyColours.put(ColCOL.COL_PNK, Color.pink);
     103    }
     104
     105    static final EnumMap<ColPAT, Patt> pattMap = new EnumMap<>(ColPAT.class);
     106    static {
     107        pattMap.put(ColPAT.PAT_UNKN, Patt.Z);
     108        pattMap.put(ColPAT.PAT_HORI, Patt.H);
     109        pattMap.put(ColPAT.PAT_VERT, Patt.V);
     110        pattMap.put(ColPAT.PAT_DIAG, Patt.D);
     111        pattMap.put(ColPAT.PAT_BRDR, Patt.B);
     112        pattMap.put(ColPAT.PAT_SQUR, Patt.S);
     113        pattMap.put(ColPAT.PAT_CROS, Patt.C);
     114        pattMap.put(ColPAT.PAT_SALT, Patt.X);
     115        pattMap.put(ColPAT.PAT_STRP, Patt.H);
     116    }
     117
     118    static String getName() {
     119        AttVal<?> name = feature.atts.get(Att.OBJNAM);
     120        if (name == null) {
     121            AttMap atts = feature.objs.get(feature.type).get(0);
     122            if (atts != null) {
     123                name = atts.get(Att.OBJNAM);
     124            }
     125        }
     126        return (name != null) ? (String) name.val : null;
     127    }
     128
     129    public static void addName(int z, Font font) {
     130        addName(z, font, Color.black, new Delta(Handle.CC, new AffineTransform()));
     131    }
     132
     133    public static void addName(int z, Font font, Color colour) {
     134        addName(z, font, colour, new Delta(Handle.CC, new AffineTransform()));
     135    }
     136
     137    public static void addName(int z, Font font, Delta delta) {
     138        addName(z, font, Color.black, delta);
     139    }
     140
     141    public static void addName(int z, Font font, Color colour, Delta delta) {
     142        if (Renderer.zoom >= z) {
     143            String name = getName();
     144            if (name != null) {
     145                Renderer.labelText(name, font, colour, delta);
     146            }
     147        }
     148    }
     149
     150    static AttMap getAtts(Obj obj, int idx) {
     151        HashMap<Integer, AttMap> objs = feature.objs.get(obj);
     152        if (objs == null)
     153            return null;
     154        else
     155            return objs.get(idx);
     156    }
     157
     158    public static Object getAttVal(Obj obj, Att att) {
     159        AttMap atts;
     160        HashMap<Integer, AttMap> objs;
     161        AttVal<?> item;
     162        if ((objs = feature.objs.get(obj)) != null)
     163            atts = objs.get(0);
     164        else
     165            return null;
     166        if ((item = atts.get(att)) == null)
     167            return null;
     168        else
     169            return item.val;
     170    }
     171
     172    public static String getAttStr(Obj obj, Att att) {
     173        String str = (String) getAttVal(obj, att);
     174        if (str != null) {
     175            return str;
     176        }
     177        return "";
     178    }
     179
     180    @SuppressWarnings("unchecked")
     181    public static Enum<?> getAttEnum(Obj obj, Att att) {
     182        ArrayList<?> list = (ArrayList<?>) getAttVal(obj, att);
     183        if (list != null) {
     184            return ((ArrayList<Enum<?>>) list).get(0);
     185        }
     186        return S57val.unknAtt(att);
     187    }
     188
     189    @SuppressWarnings("unchecked")
     190    public static ArrayList<?> getAttList(Obj obj, Att att) {
     191        ArrayList<Enum<?>> list = (ArrayList<Enum<?>>) getAttVal(obj, att);
     192        if (list != null) {
     193            return list;
     194        }
     195        list = new ArrayList<>();
     196        list.add(S57val.unknAtt(att));
     197        return list;
     198    }
     199
     200    @SuppressWarnings("unchecked")
     201    static Scheme getScheme(Obj obj) {
     202        ArrayList<Color> colours = new ArrayList<>();
     203        for (ColCOL col : (ArrayList<ColCOL>) getAttList(obj, Att.COLOUR)) {
     204            colours.add(bodyColours.get(col));
     205        }
     206        ArrayList<Patt> patterns = new ArrayList<>();
     207        for (ColPAT pat : (ArrayList<ColPAT>) getAttList(obj, Att.COLPAT)) {
     208            patterns.add(pattMap.get(pat));
     209        }
     210        return new Scheme(patterns, colours);
     211    }
     212
     213    static boolean hasAttribute(Obj obj, Att att) {
     214        AttMap atts;
     215        if ((atts = getAtts(obj, 0)) != null) {
     216            AttVal<?> item = atts.get(att);
     217            return item != null;
     218        }
     219        return false;
     220    }
     221
     222    static boolean testAttribute(Obj obj, Att att, Object val) {
     223        AttMap atts;
     224        if ((atts = getAtts(obj, 0)) != null) {
     225            AttVal<?> item = atts.get(att);
     226            if (item != null) {
     227                switch (item.conv) {
     228                case S:
     229                case A:
     230                    return ((String) item.val).equals(val);
     231                case E:
     232                case L:
     233                    return ((ArrayList<?>) item.val).contains(val);
     234                case F:
     235                case I:
     236                    return item.val == val;
     237                }
     238            }
     239        }
     240        return false;
     241    }
     242
     243    static boolean hasObject(Obj obj) {
     244        return (feature.objs.containsKey(obj));
     245    }
     246
     247    public static Feature feature;
     248    static ArrayList<Feature> objects;
     249
     250    static boolean testObject(Obj obj) {
     251        return ((objects = Renderer.map.features.get(obj)) != null);
     252    }
     253
     254    static boolean testFeature(Feature f) {
     255        return ((feature = f).reln == Rflag.MASTER);
     256    }
     257
     258    public static void rules() {
     259        if ((Renderer.context.ruleset() == RuleSet.ALL) || (Renderer.context.ruleset() == RuleSet.BASE)) {
     260            if (testObject(Obj.LNDARE)) for (Feature f : objects) if (testFeature(f)) areas();
     261            if (testObject(Obj.BUAARE)) for (Feature f : objects) if (testFeature(f)) areas();
     262            if (testObject(Obj.HRBFAC)) for (Feature f : objects) if (testFeature(f)) areas();
     263            if (testObject(Obj.HRBBSN)) for (Feature f : objects) if (testFeature(f)) areas();
     264            if (testObject(Obj.LOKBSN)) for (Feature f : objects) if (testFeature(f)) areas();
     265            if (testObject(Obj.LKBSPT)) for (Feature f : objects) if (testFeature(f)) areas();
     266            if (testObject(Obj.LAKARE)) for (Feature f : objects) if (testFeature(f)) areas();
     267            if (testObject(Obj.RIVERS)) for (Feature f : objects) if (testFeature(f)) waterways();
     268            if (testObject(Obj.CANALS)) for (Feature f : objects) if (testFeature(f)) waterways();
     269            if (testObject(Obj.DEPARE)) for (Feature f : objects) if (testFeature(f)) areas();
     270            if (testObject(Obj.COALNE)) for (Feature f : objects) if (testFeature(f)) areas();
     271            if (testObject(Obj.ROADWY)) for (Feature f : objects) if (testFeature(f)) highways();
     272            if (testObject(Obj.RAILWY)) for (Feature f : objects) if (testFeature(f)) highways();
     273        }
     274        if (Renderer.context.ruleset() == RuleSet.ALL) {
     275            if (testObject(Obj.SOUNDG)) for (Feature f : objects) if (testFeature(f)) depths();
     276            if (testObject(Obj.DEPCNT)) for (Feature f : objects) if (testFeature(f)) depths();
     277        }
     278        if (testObject(Obj.SLCONS)) for (Feature f : objects) if (testFeature(f)) shoreline();
     279        if ((Renderer.context.ruleset() == RuleSet.ALL) || (Renderer.context.ruleset() == RuleSet.SEAMARK)) {
     280            if (testObject(Obj.PIPSOL)) for (Feature f : objects) if (testFeature(f)) pipelines();
     281            if (testObject(Obj.CBLSUB)) for (Feature f : objects) if (testFeature(f)) cables();
     282            if (testObject(Obj.PIPOHD)) for (Feature f : objects) if (testFeature(f)) pipelines();
     283            if (testObject(Obj.CBLOHD)) for (Feature f : objects) if (testFeature(f)) cables();
     284            if (testObject(Obj.TSEZNE)) for (Feature f : objects) if (testFeature(f)) separation();
     285            if (testObject(Obj.TSSCRS)) for (Feature f : objects) if (testFeature(f)) separation();
     286            if (testObject(Obj.TSSRON)) for (Feature f : objects) if (testFeature(f)) separation();
     287            if (testObject(Obj.TSELNE)) for (Feature f : objects) if (testFeature(f)) separation();
     288            if (testObject(Obj.TSSLPT)) for (Feature f : objects) if (testFeature(f)) separation();
     289            if (testObject(Obj.TSSBND)) for (Feature f : objects) if (testFeature(f)) separation();
     290            if (testObject(Obj.ISTZNE)) for (Feature f : objects) if (testFeature(f)) separation();
     291            if (testObject(Obj.SNDWAV)) for (Feature f : objects) if (testFeature(f)) areas();
     292            if (testObject(Obj.WEDKLP)) for (Feature f : objects) if (testFeature(f)) areas();
     293            if (testObject(Obj.OSPARE)) for (Feature f : objects) if (testFeature(f)) areas();
     294            if (testObject(Obj.FAIRWY)) for (Feature f : objects) if (testFeature(f)) areas();
     295            if (testObject(Obj.DRGARE)) for (Feature f : objects) if (testFeature(f)) areas();
     296            if (testObject(Obj.RESARE)) for (Feature f : objects) if (testFeature(f)) areas();
     297            if (testObject(Obj.PRCARE)) for (Feature f : objects) if (testFeature(f)) areas();
     298            if (testObject(Obj.SPLARE)) for (Feature f : objects) if (testFeature(f)) areas();
     299            if (testObject(Obj.SEAARE)) for (Feature f : objects) if (testFeature(f)) areas();
     300            if (testObject(Obj.OBSTRN)) for (Feature f : objects) if (testFeature(f)) obstructions();
     301            if (testObject(Obj.UWTROC)) for (Feature f : objects) if (testFeature(f)) obstructions();
     302            if (testObject(Obj.MARCUL)) for (Feature f : objects) if (testFeature(f)) areas();
     303            if (testObject(Obj.RECTRC)) for (Feature f : objects) if (testFeature(f)) transits();
     304            if (testObject(Obj.NAVLNE)) for (Feature f : objects) if (testFeature(f)) transits();
     305            if (testObject(Obj.HRBFAC)) for (Feature f : objects) if (testFeature(f)) harbours();
     306            if (testObject(Obj.ACHARE)) for (Feature f : objects) if (testFeature(f)) harbours();
     307            if (testObject(Obj.ACHBRT)) for (Feature f : objects) if (testFeature(f)) harbours();
     308            if (testObject(Obj.BERTHS)) for (Feature f : objects) if (testFeature(f)) harbours();
     309            if (testObject(Obj.DISMAR)) for (Feature f : objects) if (testFeature(f)) distances();
     310            if (testObject(Obj.HULKES)) for (Feature f : objects) if (testFeature(f)) ports();
     311            if (testObject(Obj.CRANES)) for (Feature f : objects) if (testFeature(f)) ports();
     312            if (testObject(Obj.LNDMRK)) for (Feature f : objects) if (testFeature(f)) landmarks();
     313            if (testObject(Obj.SILTNK)) for (Feature f : objects) if (testFeature(f)) landmarks();
     314            if (testObject(Obj.BUISGL)) for (Feature f : objects) if (testFeature(f)) harbours();
     315            if (testObject(Obj.MORFAC)) for (Feature f : objects) if (testFeature(f)) moorings();
     316            if (testObject(Obj.NOTMRK)) for (Feature f : objects) if (testFeature(f)) notices();
     317            if (testObject(Obj.SMCFAC)) for (Feature f : objects) if (testFeature(f)) marinas();
     318            if (testObject(Obj.BRIDGE)) for (Feature f : objects) if (testFeature(f)) bridges();
     319            if (testObject(Obj.PILPNT)) for (Feature f : objects) if (testFeature(f)) points();
     320            if (testObject(Obj.TOPMAR)) for (Feature f : objects) if (testFeature(f)) points();
     321            if (testObject(Obj.DAYMAR)) for (Feature f : objects) if (testFeature(f)) points();
     322            if (testObject(Obj.FOGSIG)) for (Feature f : objects) if (testFeature(f)) points();
     323            if (testObject(Obj.RDOCAL)) for (Feature f : objects) if (testFeature(f)) callpoint();
     324            if (testObject(Obj.LITMIN)) for (Feature f : objects) if (testFeature(f)) lights();
     325            if (testObject(Obj.LITMAJ)) for (Feature f : objects) if (testFeature(f)) lights();
     326            if (testObject(Obj.LIGHTS)) for (Feature f : objects) if (testFeature(f)) lights();
     327            if (testObject(Obj.SISTAT)) for (Feature f : objects) if (testFeature(f)) stations();
     328            if (testObject(Obj.SISTAW)) for (Feature f : objects) if (testFeature(f)) stations();
     329            if (testObject(Obj.CGUSTA)) for (Feature f : objects) if (testFeature(f)) stations();
     330            if (testObject(Obj.RDOSTA)) for (Feature f : objects) if (testFeature(f)) stations();
     331            if (testObject(Obj.RADRFL)) for (Feature f : objects) if (testFeature(f)) stations();
     332            if (testObject(Obj.RADSTA)) for (Feature f : objects) if (testFeature(f)) stations();
     333            if (testObject(Obj.RTPBCN)) for (Feature f : objects) if (testFeature(f)) stations();
     334            if (testObject(Obj.RSCSTA)) for (Feature f : objects) if (testFeature(f)) stations();
     335            if (testObject(Obj.PILBOP)) for (Feature f : objects) if (testFeature(f)) stations();
     336            if (testObject(Obj.WTWGAG)) for (Feature f : objects) if (testFeature(f)) gauges();
     337            if (testObject(Obj.OFSPLF)) for (Feature f : objects) if (testFeature(f)) platforms();
     338            if (testObject(Obj.WRECKS)) for (Feature f : objects) if (testFeature(f)) wrecks();
     339            if (testObject(Obj.LITVES)) for (Feature f : objects) if (testFeature(f)) floats();
     340            if (testObject(Obj.LITFLT)) for (Feature f : objects) if (testFeature(f)) floats();
     341            if (testObject(Obj.BOYINB)) for (Feature f : objects) if (testFeature(f)) floats();
     342            if (testObject(Obj.BOYLAT)) for (Feature f : objects) if (testFeature(f)) buoys();
     343            if (testObject(Obj.BOYCAR)) for (Feature f : objects) if (testFeature(f)) buoys();
     344            if (testObject(Obj.BOYISD)) for (Feature f : objects) if (testFeature(f)) buoys();
     345            if (testObject(Obj.BOYSAW)) for (Feature f : objects) if (testFeature(f)) buoys();
     346            if (testObject(Obj.BOYSPP)) for (Feature f : objects) if (testFeature(f)) buoys();
     347            if (testObject(Obj.BCNLAT)) for (Feature f : objects) if (testFeature(f)) beacons();
     348            if (testObject(Obj.BCNCAR)) for (Feature f : objects) if (testFeature(f)) beacons();
     349            if (testObject(Obj.BCNISD)) for (Feature f : objects) if (testFeature(f)) beacons();
     350            if (testObject(Obj.BCNSAW)) for (Feature f : objects) if (testFeature(f)) beacons();
     351            if (testObject(Obj.BCNSPP)) for (Feature f : objects) if (testFeature(f)) beacons();
     352        }
     353    }
     354
     355    private static void areas() {
     356        String name = getName();
     357        switch (feature.type) {
     358        case BUAARE:
     359            Renderer.lineVector(new LineStyle(new Color(0x20000000, true)));
     360            break;
     361        case COALNE:
     362            if (Renderer.zoom >= 12)
     363                Renderer.lineVector(new LineStyle(Color.black, 10));
     364            break;
     365        case DEPARE:
     366            Double depmax = 0.0;
     367            if (((depmax = (Double) getAttVal(Obj.DEPARE, Att.DRVAL2)) != null) && (depmax <= 0.0)) {
     368                Renderer.lineVector(new LineStyle(Symbols.Gdries));
     369            }
     370            break;
     371        case LAKARE:
     372            if ((Renderer.zoom >= 12) || (feature.geom.area > 10.0))
     373                Renderer.lineVector(new LineStyle(Symbols.Bwater));
     374            break;
     375        case DRGARE:
     376            if (Renderer.zoom < 16)
     377                Renderer.lineVector(new LineStyle(Color.black, 8, new float[] {25, 25 }, new Color(0x40ffffff, true)));
     378            else
     379                Renderer.lineVector(new LineStyle(Color.black, 8, new float[] {25, 25 }));
     380            addName(12, new Font("Arial", Font.PLAIN, 100), new Delta(Handle.CC, new AffineTransform()));
     381            break;
     382        case FAIRWY:
     383            if (feature.geom.area > 2.0) {
     384                if (Renderer.zoom < 16)
     385                    Renderer.lineVector(new LineStyle(Symbols.Mline, 8, new float[] {50, 50 }, new Color(0x40ffffff, true)));
     386                else
     387                    Renderer.lineVector(new LineStyle(Symbols.Mline, 8, new float[] {50, 50 }));
     388            } else {
     389                if (Renderer.zoom >= 14)
     390                    Renderer.lineVector(new LineStyle(new Color(0x40ffffff, true)));
     391            }
     392            break;
     393        case LKBSPT:
     394        case LOKBSN:
     395        case HRBBSN:
     396            if (Renderer.zoom >= 12) {
     397                Renderer.lineVector(new LineStyle(Color.black, 10, Symbols.Bwater));
     398            } else {
     399                Renderer.lineVector(new LineStyle(Symbols.Bwater));
     400            }
     401            break;
     402        case HRBFAC:
     403            if (feature.objs.get(Obj.HRBBSN) != null) {
     404                if (Renderer.zoom >= 12) {
     405                    Renderer.lineVector(new LineStyle(Color.black, 10, Symbols.Bwater));
     406                } else {
     407                    Renderer.lineVector(new LineStyle(Symbols.Bwater));
     408                }
     409            }
     410            break;
     411        case LNDARE:
     412            Renderer.lineVector(new LineStyle(Symbols.Yland));
     413            break;
     414        case MARCUL:
     415            if (Renderer.zoom >= 12) {
     416                if (Renderer.zoom >= 14) {
     417                    Renderer.symbol(Areas.MarineFarm);
     418                }
     419                if ((feature.geom.area > 0.2) || ((feature.geom.area > 0.05) && (Renderer.zoom >= 14)) || ((feature.geom.area > 0.005) && (Renderer.zoom >= 16))) {
     420                    Renderer.lineVector(new LineStyle(Color.black, 4, new float[] {10, 10}));
     421                }
     422            }
     423            break;
     424        case OSPARE:
     425            if (testAttribute(feature.type, Att.CATPRA, CatPRA.PRA_WFRM)) {
     426                Renderer.symbol(Areas.WindFarm);
     427                Renderer.lineVector(new LineStyle(Color.black, 20, new float[] {40, 40}));
     428                addName(15, new Font("Arial", Font.BOLD, 80), new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, 10)));
     429            }
     430            break;
     431        case RESARE:
     432        case MIPARE:
     433            if (Renderer.zoom >= 12) {
     434                Renderer.lineSymbols(Areas.Restricted, 1.0, null, null, 0, Symbols.Mline);
     435                if (testAttribute(feature.type, Att.CATREA, CatREA.REA_NWAK)) {
     436                    Renderer.symbol(Areas.NoWake);
     437                }
     438            }
     439            break;
     440        case PRCARE:
     441            if (Renderer.zoom >= 12) {
     442                Renderer.lineVector(new LineStyle(Symbols.Mline, 10, new float[] {40, 40}));
     443            }
     444            break;
     445        case SEAARE:
     446            switch ((CatSEA) getAttEnum(feature.type, Att.CATSEA)) {
     447            case SEA_RECH:
     448                if ((Renderer.zoom >= 10) && (name != null))
     449                    if (feature.geom.prim == Pflag.LINE) {
     450                        Renderer.lineText(name, new Font("Arial", Font.PLAIN, 150), Color.black, -40);
     451                    } else {
     452                        Renderer.labelText(name, new Font("Arial", Font.PLAIN, 150), Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -40)));
     453                    }
     454                break;
     455            case SEA_BAY:
     456                if ((Renderer.zoom >= 12) && (name != null))
     457                    if (feature.geom.prim == Pflag.LINE) {
     458                        Renderer.lineText(name, new Font("Arial", Font.PLAIN, 150), Color.black, -40);
     459                    } else {
     460                        Renderer.labelText(name, new Font("Arial", Font.PLAIN, 150), Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -40)));
     461                    }
     462                break;
     463            case SEA_SHOL:
     464                if (Renderer.zoom >= 14) {
     465                    if (feature.geom.prim == Pflag.AREA) {
     466                        Renderer.lineVector(new LineStyle(new Color(0xc480ff), 4, new float[] {25, 25}));
     467                        if (name != null) {
     468                            Renderer.labelText(name, new Font("Arial", Font.ITALIC, 75), Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -40)));
     469                            Renderer.labelText("(Shoal)", new Font("Arial", Font.PLAIN, 60), Color.black, new Delta(Handle.BC));
     470                        }
     471                    } else if (feature.geom.prim == Pflag.LINE) {
     472                        if (name != null) {
     473                            Renderer.lineText(name, new Font("Arial", Font.ITALIC, 75), Color.black, -40);
     474                            Renderer.lineText("(Shoal)", new Font("Arial", Font.PLAIN, 60), Color.black, 0);
     475                        }
     476                    } else {
     477                        if (name != null) {
     478                            Renderer.labelText(name, new Font("Arial", Font.ITALIC, 75), Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -40)));
     479                            Renderer.labelText("(Shoal)", new Font("Arial", Font.PLAIN, 60), Color.black, new Delta(Handle.BC));
     480                        }
     481                    }
     482                }
     483                break;
     484            case SEA_GAT:
     485            case SEA_NRRW:
     486                addName(12, new Font("Arial", Font.PLAIN, 100));
     487                break;
     488            default:
     489                break;
     490            }
     491            break;
     492        case SNDWAV:
     493            if (Renderer.zoom >= 12) Renderer.fillPattern(Areas.Sandwaves);
     494            break;
     495        case WEDKLP:
     496            if (Renderer.zoom >= 12) {
     497                switch ((CatWED) getAttEnum(feature.type, Att.CATWED)) {
     498                case WED_KELP:
     499                    if (feature.geom.prim == Pflag.AREA) {
     500                        Renderer.fillPattern(Areas.KelpA);
     501                    } else {
     502                        Renderer.symbol(Areas.KelpS);
     503                    }
     504                    break;
     505                default:
     506                    break;
     507                }
     508            }
     509            break;
     510        case SPLARE:
     511            if (Renderer.zoom >= 12) {
     512                Renderer.symbol(Areas.Plane, new Scheme(Symbols.Msymb));
     513                Renderer.lineSymbols(Areas.Restricted, 0.5, Areas.LinePlane, null, 10, Symbols.Mline);
     514            }
     515            addName(15, new Font("Arial", Font.BOLD, 80), new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -90)));
     516            break;
     517        default:
     518            break;
     519        }
     520    }
     521
     522    @SuppressWarnings("unchecked")
     523    private static void beacons() {
     524        if ((Renderer.zoom >= 14) || ((Renderer.zoom >= 12) && ((feature.type == Obj.BCNLAT) || (feature.type == Obj.BCNCAR)))
     525                || ((Renderer.zoom >= 11) && ((feature.type == Obj.BCNSAW) || hasObject(Obj.RTPBCN)))) {
     526            BcnSHP shape = (BcnSHP) getAttEnum(feature.type, Att.BCNSHP);
     527            if (shape == BcnSHP.BCN_UNKN)
     528                shape = BcnSHP.BCN_PILE;
     529            if ((shape == BcnSHP.BCN_WTHY) && (feature.type == Obj.BCNLAT)) {
     530                switch ((CatLAM) getAttEnum(feature.type, Att.CATLAM)) {
     531                case LAM_PORT:
     532                    Renderer.symbol(Beacons.WithyPort);
     533                    break;
     534                case LAM_STBD:
     535                    Renderer.symbol(Beacons.WithyStarboard);
     536                    break;
     537                default:
     538                    Renderer.symbol(Beacons.Stake, getScheme(feature.type));
     539                }
     540            } else if ((shape == BcnSHP.BCN_PRCH) && (feature.type == Obj.BCNLAT) && !(feature.objs.containsKey(Obj.TOPMAR))) {
     541                switch ((CatLAM) getAttEnum(feature.type, Att.CATLAM)) {
     542                case LAM_PORT:
     543                    Renderer.symbol(Beacons.PerchPort);
     544                    break;
     545                case LAM_STBD:
     546                    Renderer.symbol(Beacons.PerchStarboard);
     547                    break;
     548                default:
     549                    Renderer.symbol(Beacons.Stake, getScheme(feature.type));
     550                }
     551            } else {
     552                Renderer.symbol(Beacons.Shapes.get(shape), getScheme(feature.type));
     553                if (feature.objs.containsKey(Obj.TOPMAR)) {
     554                    AttMap topmap = feature.objs.get(Obj.TOPMAR).get(0);
     555                    if (topmap.containsKey(Att.TOPSHP)) {
     556                        Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.TOPMAR), Topmarks.BeaconDelta);
     557                    }
     558                } else if (feature.objs.containsKey(Obj.DAYMAR)) {
     559                    AttMap topmap = feature.objs.get(Obj.DAYMAR).get(0);
     560                    if (topmap.containsKey(Att.TOPSHP)) {
     561                        Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.DAYMAR), Topmarks.BeaconDelta);
     562                    }
     563                }
     564            }
     565            if (hasObject(Obj.NOTMRK))
     566                notices();
     567            addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(60, -50)));
     568            Signals.addSignals();
     569        }
     570    }
     571
     572    @SuppressWarnings("unchecked")
     573    private static void buoys() {
     574        if ((Renderer.zoom >= 14) || ((Renderer.zoom >= 12) && ((feature.type == Obj.BOYLAT) || (feature.type == Obj.BOYCAR)))
     575                || ((Renderer.zoom >= 11) && ((feature.type == Obj.BOYSAW) || hasObject(Obj.RTPBCN)))) {
     576            BoySHP shape = (BoySHP) getAttEnum(feature.type, Att.BOYSHP);
     577            if (shape == BoySHP.BOY_UNKN) shape = BoySHP.BOY_PILR;
     578            Renderer.symbol(Buoys.Shapes.get(shape), getScheme(feature.type));
     579            if (feature.objs.containsKey(Obj.TOPMAR)) {
     580                AttMap topmap = feature.objs.get(Obj.TOPMAR).get(0);
     581                if (topmap.containsKey(Att.TOPSHP)) {
     582                    Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.TOPMAR), Topmarks.BuoyDeltas.get(shape));
     583                }
     584            } else if (feature.objs.containsKey(Obj.DAYMAR)) {
     585                AttMap topmap = feature.objs.get(Obj.DAYMAR).get(0);
     586                if (topmap.containsKey(Att.TOPSHP)) {
     587                    Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.DAYMAR), Topmarks.BuoyDeltas.get(shape));
     588                }
     589            }
     590            addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(60, -50)));
     591            Signals.addSignals();
     592        }
     593    }
     594
     595    private static void bridges() {
     596        if (Renderer.zoom >= 16) {
     597            double verclr, verccl, vercop, horclr;
     598            AttMap atts = feature.objs.get(Obj.BRIDGE).get(0);
     599            String vstr = "";
     600            String hstr = "";
     601            if (atts != null) {
     602                if (atts.containsKey(Att.HORCLR)) {
     603                    horclr = (Double) atts.get(Att.HORCLR).val;
     604                    hstr = String.valueOf(horclr);
     605                }
     606                if (atts.containsKey(Att.VERCLR)) {
     607                    verclr = (Double) atts.get(Att.VERCLR).val;
     608                } else {
     609                    verclr = atts.containsKey(Att.VERCSA) ? (Double) atts.get(Att.VERCSA).val : 0;
     610                }
     611                verccl = atts.containsKey(Att.VERCCL) ? (Double) atts.get(Att.VERCCL).val : 0;
     612                vercop = atts.containsKey(Att.VERCOP) ? (Double) atts.get(Att.VERCOP).val : 0;
     613                if (verclr > 0) {
     614                    vstr += String.valueOf(verclr);
     615                } else if (verccl > 0) {
     616                    if (vercop == 0) {
     617                        vstr += String.valueOf(verccl) + "/-";
     618                    } else {
     619                        vstr += String.valueOf(verccl) + "/" + String.valueOf(vercop);
     620                    }
     621                }
     622                if (hstr.isEmpty() && !vstr.isEmpty()) {
     623                    Renderer.labelText(vstr, new Font("Arial", Font.PLAIN, 30), Color.black, LabelStyle.VCLR, Color.black, Color.white, new Delta(Handle.CC));
     624                } else if (!hstr.isEmpty() && !vstr.isEmpty()) {
     625                    Renderer.labelText(vstr, new Font("Arial", Font.PLAIN, 30), Color.black, LabelStyle.VCLR, Color.black, Color.white, new Delta(Handle.BC));
     626                    Renderer.labelText(hstr, new Font("Arial", Font.PLAIN, 30), Color.black, LabelStyle.HCLR, Color.black, Color.white, new Delta(Handle.TC));
     627                } else if (!hstr.isEmpty() && vstr.isEmpty()) {
     628                    Renderer.labelText(hstr, new Font("Arial", Font.PLAIN, 30), Color.black, LabelStyle.HCLR, Color.black, Color.white, new Delta(Handle.CC));
     629                }
     630            }
     631        }
     632    }
     633
     634    private static void cables() {
     635        if ((Renderer.zoom >= 16) && (feature.geom.length < 2)) {
     636            if (feature.type == Obj.CBLSUB) {
     637                Renderer.lineSymbols(Areas.Cable, 0.0, null, null, 0, Symbols.Mline);
     638            } else if (feature.type == Obj.CBLOHD) {
     639                AttMap atts = feature.objs.get(Obj.CBLOHD).get(0);
     640                if ((atts != null) && (atts.containsKey(Att.CATCBL)) && (atts.get(Att.CATCBL).val == CatCBL.CBL_POWR)) {
     641                    Renderer.lineSymbols(Areas.CableDash, 0, Areas.CableDot, Areas.CableFlash, 2, Color.black);
     642                } else {
     643                    Renderer.lineSymbols(Areas.CableDash, 0, Areas.CableDot, null, 2, Color.black);
     644                }
     645                if (atts != null) {
     646                    if (atts.containsKey(Att.VERCLR)) {
     647                        Renderer.labelText(String.valueOf(atts.get(Att.VERCLR).val), new Font("Arial", Font.PLAIN, 50), Color.black, LabelStyle.VCLR, Color.black, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, 25)));
     648                    } else if (atts.containsKey(Att.VERCSA)) {
     649                        Renderer.labelText(String.valueOf(atts.get(Att.VERCSA).val), new Font("Arial", Font.PLAIN, 50), Color.black, LabelStyle.PCLR, Color.black, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, 25)));
     650                    }
     651                }
     652            }
     653        }
     654    }
     655
     656    private static void callpoint() {
     657        if (Renderer.zoom >= 14) {
     658            Symbol symb = Harbours.CallPoint2;
     659            TrfTRF trf = (TrfTRF) getAttEnum(feature.type, Att.TRAFIC);
     660            if (trf != TrfTRF.TRF_TWOW) {
     661                symb = Harbours.CallPoint1;
     662            }
     663            Double orient = 0.0;
     664            if ((orient = (Double) getAttVal(feature.type, Att.ORIENT)) == null) {
     665                orient = 0.0;
     666            }
     667            Renderer.symbol(symb, new Delta(Handle.CC, AffineTransform.getRotateInstance(Math.toRadians(orient))));
     668            String chn;
     669            if (!(chn = getAttStr(feature.type, Att.COMCHA)).isEmpty()) {
     670                Renderer.labelText(("Ch." + chn), new Font("Arial", Font.PLAIN, 50), Color.black, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, 50)));
     671            }
     672        }
     673    }
     674
     675    private static void depths() {
     676        switch (feature.type) {
     677        case SOUNDG:
     678            if ((Renderer.zoom >= 14) && hasAttribute(Obj.SOUNDG, Att.VALSOU)) {
     679                double depth = (double) getAttVal(Obj.SOUNDG, Att.VALSOU);
     680                String dstr = df.format(depth);
     681                String[] tok = dstr.split("[-.]");
     682                String ul = "";
     683                String id = tok[0];
     684                String dd = "";
     685                if (tok[0].equals("")) {
     686                    for (int i = 0; i < tok[1].length(); i++) {
     687                        ul += "_";
     688                    }
     689                    id = tok[1];
     690                    dd = (tok.length == 3) ? tok[2] : "";
     691                } else {
     692                    dd = (tok.length == 2) ? tok[1] : "";
     693                }
     694                Renderer.labelText(ul, new Font("Arial", Font.PLAIN, 30), Color.black, new Delta(Handle.RC, AffineTransform.getTranslateInstance(10, 15)));
     695                Renderer.labelText(id, new Font("Arial", Font.PLAIN, 30), Color.black, new Delta(Handle.RC, AffineTransform.getTranslateInstance(10, 0)));
     696                Renderer.labelText(dd, new Font("Arial", Font.PLAIN, 20), Color.black, new Delta(Handle.LC, AffineTransform.getTranslateInstance(15, 10)));
     697            }
     698            break;
     699        case DEPCNT:
     700            break;
     701        default:
     702            break;
     703        }
     704    }
     705
     706    private static void distances() {
     707        if (Renderer.zoom >= 14) {
     708            if (!testAttribute(Obj.DISMAR, Att.CATDIS, CatDIS.DIS_NONI)) {
     709                Renderer.symbol(Harbours.DistanceI);
     710            } else {
     711                Renderer.symbol(Harbours.DistanceU);
     712            }
     713            if (Renderer.zoom >= 15) {
     714                AttMap atts = getAtts(Obj.DISMAR, 0);
     715                if ((atts != null) && (atts.containsKey(Att.WTWDIS))) {
     716                    Double dist = (Double) atts.get(Att.WTWDIS).val;
     717                    String str = "";
     718                    if (atts.containsKey(Att.HUNITS)) {
     719                        switch ((UniHLU) getAttEnum(Obj.DISMAR, Att.HUNITS)) {
     720                        case HLU_METR:
     721                            str += "m ";
     722                            break;
     723                        case HLU_FEET:
     724                            str += "ft ";
     725                            break;
     726                        case HLU_HMTR:
     727                            str += "hm ";
     728                            break;
     729                        case HLU_KMTR:
     730                            str += "km ";
     731                            break;
     732                        case HLU_SMIL:
     733                            str += "M ";
     734                            break;
     735                        case HLU_NMIL:
     736                            str += "NM ";
     737                            break;
     738                        default:
     739                            break;
     740                        }
     741                    }
     742                    str += String.format("%1.0f", dist);
     743                    Renderer.labelText(str, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.CC, AffineTransform.getTranslateInstance(0, 45)));
     744                }
     745            }
     746        }
     747    }
     748
     749    @SuppressWarnings("unchecked")
     750    private static void floats() {
     751        if ((Renderer.zoom >= 12) || ((Renderer.zoom >= 11) && ((feature.type == Obj.LITVES) || (feature.type == Obj.BOYINB) || hasObject(Obj.RTPBCN)))) {
     752            switch (feature.type) {
     753            case LITVES:
     754                Renderer.symbol(Buoys.Super, getScheme(feature.type));
     755                break;
     756            case LITFLT:
     757                Renderer.symbol(Buoys.Float, getScheme(feature.type));
     758                break;
     759            case BOYINB:
     760                Renderer.symbol(Buoys.Super, getScheme(feature.type));
     761                break;
     762            default:
     763                break;
     764            }
     765            if (feature.objs.containsKey(Obj.TOPMAR)) {
     766                AttMap topmap = feature.objs.get(Obj.TOPMAR).get(0);
     767                if (topmap.containsKey(Att.TOPSHP)) {
     768                    Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.TOPMAR), Topmarks.FloatDelta);
     769                }
     770            } else if (feature.objs.containsKey(Obj.DAYMAR)) {
     771                AttMap topmap = feature.objs.get(Obj.DAYMAR).get(0);
     772                if (topmap.containsKey(Att.TOPSHP)) {
     773                    Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.DAYMAR), Topmarks.FloatDelta);
     774                }
     775            }
     776            addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(20, -50)));
     777            Signals.addSignals();
     778        }
     779    }
     780
     781    private static void gauges() {
     782        if (Renderer.zoom >= 14) {
     783            Renderer.symbol(Harbours.TideGauge);
     784            addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(20, -50)));
     785            Signals.addSignals();
     786        }
     787    }
     788
     789    @SuppressWarnings("unchecked")
     790    private static void harbours() {
     791        String name = getName();
     792        switch (feature.type) {
     793        case ACHBRT:
     794            if (Renderer.zoom >= 14) {
     795                Renderer.symbol(Harbours.Anchor, new Scheme(Symbols.Msymb));
     796                if (Renderer.zoom >= 15) {
     797                    Renderer.labelText(name == null ? "" : name, new Font("Arial", Font.PLAIN, 30), Symbols.Msymb, LabelStyle.RRCT, Symbols.Msymb, Color.white, new Delta(Handle.BC));
     798                }
     799            }
     800            if (getAttVal(Obj.ACHBRT, Att.RADIUS) != null) {
     801                double radius;
     802                if ((radius = (Double) getAttVal(Obj.ACHBRT, Att.RADIUS)) != 0) {
     803                    UniHLU units = (UniHLU) getAttEnum(Obj.ACHBRT, Att.HUNITS);
     804                    if (units == UniHLU.HLU_UNKN) {
     805                        units = UniHLU.HLU_METR;
     806                    }
     807                    Renderer.lineCircle(new LineStyle(Symbols.Mline, 4, new float[] {10, 10}, null), radius, units);
     808                }
     809            }
     810            break;
     811        case ACHARE:
     812            if (Renderer.zoom >= 12) {
     813                if (feature.geom.prim != Pflag.AREA) {
     814                    Renderer.symbol(Harbours.Anchorage, new Scheme(Color.black));
     815                } else {
     816                    Renderer.symbol(Harbours.Anchorage, new Scheme(Symbols.Mline));
     817                    Renderer.lineSymbols(Areas.Restricted, 1.0, Areas.LineAnchor, null, 10, Symbols.Mline);
     818                }
     819                addName(15, new Font("Arial", Font.BOLD, 60), Symbols.Mline, new Delta(Handle.LC, AffineTransform.getTranslateInstance(70, 0)));
     820                ArrayList<StsSTS> sts = (ArrayList<StsSTS>) getAttList(Obj.ACHARE, Att.STATUS);
     821                if ((Renderer.zoom >= 15) && (sts.contains(StsSTS.STS_RESV))) {
     822                    Renderer.labelText("Reserved", new Font("Arial", Font.PLAIN, 50), Symbols.Mline, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, 60)));
     823                }
     824                ArrayList<CatACH> cats = (ArrayList<CatACH>) getAttList(Obj.ACHARE, Att.CATACH);
     825                int dy = (cats.size() - 1) * -30;
     826                for (CatACH cat : cats) {
     827                    switch (cat) {
     828                    case ACH_DEEP:
     829                        Renderer.labelText("DW", new Font("Arial", Font.BOLD, 50), Symbols.Msymb, new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
     830                        dy += 60;
     831                        break;
     832                    case ACH_TANK:
     833                        Renderer.labelText("Tanker", new Font("Arial", Font.BOLD, 50), Symbols.Msymb, new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
     834                        dy += 60;
     835                        break;
     836                    case ACH_H24P:
     837                        Renderer.labelText("24h", new Font("Arial", Font.BOLD, 50), Symbols.Msymb, new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
     838                        dy += 60;
     839                        break;
     840                    case ACH_EXPL:
     841                        Renderer.symbol(Harbours.Explosives, new Scheme(Symbols.Msymb), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
     842                        dy += 60;
     843                        break;
     844                    case ACH_QUAR:
     845                        Renderer.symbol(Harbours.Hospital, new Scheme(Symbols.Msymb), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
     846                        dy += 60;
     847                        break;
     848                    case ACH_SEAP:
     849                        Renderer.symbol(Areas.Seaplane, new Scheme(Symbols.Msymb), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
     850                        dy += 60;
     851                        break;
     852                    default:
     853                    }
     854                }
     855            }
     856            break;
     857        case BERTHS:
     858            if (Renderer.zoom >= 14) {
     859                Renderer.lineVector(new LineStyle(Symbols.Mline, 6, new float[] {20, 20}));
     860                Renderer.labelText(name == null ? " " : name, new Font("Arial", Font.PLAIN, 40), Symbols.Msymb, LabelStyle.RRCT, Symbols.Mline, Color.white);
     861            }
     862            break;
     863        case BUISGL:
     864            if (Renderer.zoom >= 16) {
     865                ArrayList<Symbol> symbols = new ArrayList<>();
     866                ArrayList<FncFNC> fncs = (ArrayList<FncFNC>) getAttList(Obj.BUISGL, Att.FUNCTN);
     867                for (FncFNC fnc : fncs) {
     868                    symbols.add(Landmarks.Funcs.get(fnc));
     869                }
     870                if (feature.objs.containsKey(Obj.SMCFAC)) {
     871                    ArrayList<CatSCF> scfs = (ArrayList<CatSCF>) getAttList(Obj.SMCFAC, Att.CATSCF);
     872                    for (CatSCF scf : scfs) {
     873                        symbols.add(Facilities.Cats.get(scf));
     874                    }
     875                }
     876                Renderer.cluster(symbols);
     877            }
     878            break;
     879        case HRBFAC:
     880            if (Renderer.zoom >= 12) {
     881                ArrayList<CatHAF> cathaf = (ArrayList<CatHAF>) getAttList(Obj.HRBFAC, Att.CATHAF);
     882                if (cathaf.size() == 1) {
     883                    switch (cathaf.get(0)) {
     884                    case HAF_MRNA:
     885                        Renderer.symbol(Harbours.Marina);
     886                        break;
     887                    case HAF_MANF:
     888                        Renderer.symbol(Harbours.MarinaNF);
     889                        break;
     890                    case HAF_FISH:
     891                        Renderer.symbol(Harbours.Fishing);
     892                        break;
     893                    default:
     894                        Renderer.symbol(Harbours.Harbour);
     895                        break;
     896                    }
     897                } else {
     898                    Renderer.symbol(Harbours.Harbour);
     899                }
     900            }
     901            break;
     902        default:
     903            break;
     904        }
     905    }
     906
     907    @SuppressWarnings("unchecked")
     908    private static void highways() {
     909        switch (feature.type) {
     910        case ROADWY:
     911            ArrayList<CatROD> cat = (ArrayList<CatROD>) (getAttList(Obj.ROADWY, Att.CATROD));
     912            if (cat.size() > 0) {
     913                switch (cat.get(0)) {
     914                case ROD_MWAY:
     915                    Renderer.lineVector(new LineStyle(Color.black, 20));
     916                    break;
     917                case ROD_MAJR:
     918                    Renderer.lineVector(new LineStyle(Color.black, 15));
     919                    break;
     920                case ROD_MINR:
     921                    Renderer.lineVector(new LineStyle(Color.black, 10));
     922                    break;
     923                default:
     924                    Renderer.lineVector(new LineStyle(Color.black, 5));
     925                }
     926            } else {
     927                Renderer.lineVector(new LineStyle(Color.black, 5));
     928            }
     929            break;
     930        case RAILWY:
     931            Renderer.lineVector(new LineStyle(Color.gray, 10));
     932            Renderer.lineVector(new LineStyle(Color.black, 10, new float[] {30, 30}));
     933            break;
     934        default:
     935        }
     936    }
     937
     938    @SuppressWarnings("unchecked")
     939    private static void landmarks() {
     940        if (!hasAttribute(Obj.LNDMRK, Att.CATLMK)
     941                && (!hasAttribute(Obj.LNDMRK, Att.FUNCTN) || testAttribute(Obj.LNDMRK, Att.FUNCTN, FncFNC.FNC_LGHT))
     942                && hasObject(Obj.LIGHTS))
     943            lights();
     944        else if (Renderer.zoom >= 12) {
     945            switch (feature.type) {
     946            case LNDMRK:
     947                ArrayList<CatLMK> cats = (ArrayList<CatLMK>) getAttList(feature.type, Att.CATLMK);
     948                Symbol catSym = Landmarks.Shapes.get(cats.get(0));
     949                ArrayList<FncFNC> fncs = (ArrayList<FncFNC>) getAttList(feature.type, Att.FUNCTN);
     950                Symbol fncSym = Landmarks.Funcs.get(fncs.get(0));
     951                if ((fncs.get(0) == FncFNC.FNC_CHCH) && (cats.get(0) == CatLMK.LMK_TOWR))
     952                    catSym = Landmarks.ChurchTower;
     953                if (cats.get(0) == CatLMK.LMK_RADR)
     954                    fncSym = Landmarks.RadioTV;
     955                Renderer.symbol(catSym);
     956                Renderer.symbol(fncSym);
     957                break;
     958            case SILTNK:
     959                if (testAttribute(feature.type, Att.CATSIL, CatSIL.SIL_WTRT))
     960                    Renderer.symbol(Landmarks.WaterTower);
     961                break;
     962            default:
     963                break;
     964            }
     965            if (Renderer.zoom >= 15)
     966                addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(60, -50)));
     967            Signals.addSignals();
     968        }
     969    }
     970
     971    @SuppressWarnings("unchecked")
     972    private static void points() {
     973        boolean ok = false;
     974        switch (feature.type) {
     975        case FOGSIG:
     976            if (Renderer.zoom >= 12) {
     977                if (feature.objs.containsKey(Obj.LIGHTS))
     978                    lights();
     979                else
     980                    Renderer.symbol(Harbours.Post);
     981                ok = true;
     982            }
     983            break;
     984        default:
     985            if (Renderer.zoom >= 14) {
     986                if (feature.objs.containsKey(Obj.LIGHTS))
     987                    lights();
     988                else
     989                    Renderer.symbol(Harbours.Post);
     990                ok = true;
     991            }
     992            break;
     993        }
     994        if (ok) {
     995            if (feature.objs.containsKey(Obj.TOPMAR)) {
     996                AttMap topmap = feature.objs.get(Obj.TOPMAR).get(0);
     997                if (topmap.containsKey(Att.TOPSHP)) {
     998                    Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.TOPMAR), null);
     999                }
     1000            } else if (feature.objs.containsKey(Obj.DAYMAR)) {
     1001                AttMap topmap = feature.objs.get(Obj.DAYMAR).get(0);
     1002                if (topmap.containsKey(Att.TOPSHP)) {
     1003                    Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.DAYMAR), null);
     1004                }
     1005            }
     1006            Signals.addSignals();
     1007        }
     1008    }
     1009
     1010    @SuppressWarnings("unchecked")
     1011    private static void lights() {
     1012        boolean ok = false;
     1013        switch (feature.type) {
     1014        case LITMAJ:
     1015        case LNDMRK:
     1016            if (Renderer.zoom >= 12) {
     1017                Renderer.symbol(Beacons.LightMajor);
     1018                ok = true;
     1019            }
     1020            break;
     1021        case LITMIN:
     1022        case LIGHTS:
     1023        case PILPNT:
     1024            if (Renderer.zoom >= 14) {
     1025                Renderer.symbol(Beacons.LightMinor);
     1026                ok = true;
     1027            }
     1028            break;
     1029        default:
     1030            break;
     1031        }
     1032        if (ok) {
     1033            if (feature.objs.containsKey(Obj.TOPMAR)) {
     1034                AttMap topmap = feature.objs.get(Obj.TOPMAR).get(0);
     1035                if (topmap.containsKey(Att.TOPSHP)) {
     1036                    Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.TOPMAR), Topmarks.LightDelta);
     1037                }
     1038            } else if (feature.objs.containsKey(Obj.DAYMAR)) {
     1039                AttMap topmap = feature.objs.get(Obj.DAYMAR).get(0);
     1040                if (topmap.containsKey(Att.TOPSHP)) {
     1041                    Renderer.symbol(Topmarks.Shapes.get(((ArrayList<TopSHP>) (topmap.get(Att.TOPSHP).val)).get(0)), getScheme(Obj.DAYMAR), Topmarks.LightDelta);
     1042                }
     1043            }
     1044            Signals.addSignals();
     1045        }
     1046    }
     1047
     1048    @SuppressWarnings("unchecked")
     1049    private static void marinas() {
     1050        if (Renderer.zoom >= 16) {
     1051            ArrayList<Symbol> symbols = new ArrayList<>();
     1052            ArrayList<CatSCF> scfs = (ArrayList<CatSCF>) getAttList(Obj.SMCFAC, Att.CATSCF);
     1053            for (CatSCF scf : scfs) {
     1054                symbols.add(Facilities.Cats.get(scf));
     1055            }
     1056            Renderer.cluster(symbols);
     1057        }
     1058    }
     1059
     1060    private static void moorings() {
     1061        if (Renderer.zoom >= 14) {
     1062            switch ((CatMOR) getAttEnum(feature.type, Att.CATMOR)) {
     1063            case MOR_DLPN:
     1064                Renderer.symbol(Harbours.Dolphin);
     1065                break;
     1066            case MOR_DDPN:
     1067                Renderer.symbol(Harbours.DeviationDolphin);
     1068                break;
     1069            case MOR_BLRD:
     1070            case MOR_POST:
     1071                Renderer.symbol(Harbours.Bollard);
     1072                break;
     1073            case MOR_BUOY:
     1074                BoySHP shape = (BoySHP) getAttEnum(feature.type, Att.BOYSHP);
     1075                if (shape == BoySHP.BOY_UNKN) {
     1076                    shape = BoySHP.BOY_SPHR;
     1077                }
     1078                Renderer.symbol(Buoys.Shapes.get(shape), getScheme(feature.type));
     1079                Renderer.symbol(Topmarks.TopMooring, Topmarks.BuoyDeltas.get(shape));
     1080                break;
     1081            default:
     1082                break;
     1083            }
     1084            Signals.addSignals();
     1085        }
     1086    }
     1087
     1088    @SuppressWarnings("unchecked")
     1089    private static void notices() {
     1090        if (Renderer.zoom >= 14) {
     1091            double dx = 0.0, dy = 0.0;
     1092            switch (feature.type) {
     1093            case BCNCAR:
     1094            case BCNISD:
     1095            case BCNLAT:
     1096            case BCNSAW:
     1097            case BCNSPP:
     1098                if (testAttribute(Obj.TOPMAR, Att.TOPSHP, TopSHP.TOP_BORD) || testAttribute(Obj.DAYMAR, Att.TOPSHP, TopSHP.TOP_BORD)) {
     1099                    dy = -100.0;
     1100                } else {
     1101                    dy = -45.0;
     1102                }
     1103                break;
     1104            case NOTMRK:
     1105                dy = 0.0;
     1106                break;
     1107            default:
     1108                return;
     1109            }
     1110            MarSYS sys = MarSYS.SYS_CEVN;
     1111            BnkWTW bnk = BnkWTW.BWW_UNKN;
     1112            AttVal<?> att = feature.atts.get(Att.MARSYS);
     1113            if (att != null) sys = (MarSYS) att.val;
     1114            att = feature.atts.get(Att.BNKWTW);
     1115            if (att != null) bnk = (BnkWTW) att.val;
     1116            ObjTab objs = feature.objs.get(Obj.NOTMRK);
     1117            int n = objs.size();
     1118            if (n > 5) {
     1119                Renderer.symbol(Notices.Notice, new Delta(Handle.CC, AffineTransform.getTranslateInstance(dx, dy)));
     1120            } else {
     1121                int i = 0;
     1122                for (AttMap atts : objs.values()) {
     1123                    if (atts.get(Att.MARSYS) != null) sys = ((ArrayList<MarSYS>) (atts.get(Att.MARSYS).val)).get(0);
     1124                    if (atts.get(Att.BNKWTW) != null) bnk = ((ArrayList<BnkWTW>) (atts.get(Att.BNKWTW).val)).get(0);
     1125                    CatNMK cat = CatNMK.NMK_UNKN;
     1126                    if (atts.get(Att.CATNMK) != null) cat = ((ArrayList<CatNMK>) (atts.get(Att.CATNMK).val)).get(0);
     1127                    Symbol sym = Notices.getNotice(cat, sys, bnk);
     1128                    Scheme sch = Notices.getScheme(sys, bnk);
     1129                    ArrayList<AddMRK> add = new ArrayList<>();
     1130                    if (atts.get(Att.ADDMRK) != null) add = (ArrayList<AddMRK>) (atts.get(Att.ADDMRK).val);
     1131                    Handle h = Handle.CC;
     1132                    double ax = 0.0;
     1133                    double ay = 0.0;
     1134                    switch (i) {
     1135                    case 0:
     1136                        if (n != 1) h = null;
     1137                        break;
     1138                    case 1:
     1139                        if (n <= 3) {
     1140                            h = Handle.RC;
     1141                            ax = -30;
     1142                            ay = dy;
     1143                        } else {
     1144                            h = Handle.BR;
     1145                        }
     1146                        break;
     1147                    case 2:
     1148                        if (n <= 3)
     1149                            h = Handle.LC;
     1150                        else
     1151                            h = Handle.BL;
     1152                        break;
     1153                    case 3:
     1154                        if (n == 4)
     1155                            h = Handle.TC;
     1156                        else
     1157                            h = Handle.TR;
     1158                        break;
     1159                    case 4:
     1160                        h = Handle.TL;
     1161                        break;
     1162                    }
     1163                    if (h != null) {
     1164                        Renderer.symbol(sym, sch, new Delta(h, AffineTransform.getTranslateInstance(dx, dy)));
     1165                        if (!add.isEmpty())
     1166                            Renderer.symbol(Notices.NoticeBoard, new Delta(Handle.BC, AffineTransform.getTranslateInstance(ax, ay - 30)));
     1167                    }
     1168                    i++;
     1169                }
     1170            }
     1171        }
     1172    }
     1173
     1174    private static void obstructions() {
     1175        if ((Renderer.zoom >= 12) && (feature.type == Obj.OBSTRN)) {
     1176            switch ((CatOBS) getAttEnum(feature.type, Att.CATOBS)) {
     1177            case OBS_BOOM:
     1178                Renderer.lineVector(new LineStyle(Color.black, 5, new float[] {20, 20}, null));
     1179                if (Renderer.zoom >= 15) {
     1180                    Renderer.lineText("Boom", new Font("Arial", Font.PLAIN, 80), Color.black, -20);
     1181                }
     1182            default:
     1183                break;
     1184            }
     1185        }
     1186        if ((Renderer.zoom >= 14) && (feature.type == Obj.UWTROC)) {
     1187            switch ((WatLEV) getAttEnum(feature.type, Att.WATLEV)) {
     1188            case LEV_CVRS:
     1189                Renderer.symbol(Areas.RockC);
     1190                break;
     1191            case LEV_AWSH:
     1192                Renderer.symbol(Areas.RockA);
     1193                break;
     1194            default:
     1195                Renderer.symbol(Areas.Rock);
     1196            }
     1197        } else {
     1198            Renderer.symbol(Areas.Rock);
     1199        }
     1200    }
     1201
     1202    private static void pipelines() {
     1203        if ((Renderer.zoom >= 16) && (feature.geom.length < 2)) {
     1204            if (feature.type == Obj.PIPSOL) {
     1205                Renderer.lineSymbols(Areas.Pipeline, 1.0, null, null, 0, Symbols.Mline);
     1206            } else if (feature.type == Obj.PIPOHD) {
     1207                Renderer.lineVector(new LineStyle(Color.black, 8));
     1208                AttMap atts = feature.atts;
     1209                double verclr = 0;
     1210                if (atts != null) {
     1211                    if (atts.containsKey(Att.VERCLR)) {
     1212                        verclr = (Double) atts.get(Att.VERCLR).val;
     1213                    } else {
     1214                        verclr = atts.containsKey(Att.VERCSA) ? (Double) atts.get(Att.VERCSA).val : 0;
     1215                    }
     1216                    if (verclr > 0) {
     1217                        Renderer.labelText(String.valueOf(verclr), new Font("Arial", Font.PLAIN, 50), Color.black, LabelStyle.VCLR, Color.black, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, 25)));
     1218                    }
     1219                }
     1220            }
     1221        }
     1222    }
     1223
     1224    @SuppressWarnings("unchecked")
     1225    private static void platforms() {
     1226        ArrayList<CatOFP> cats = (ArrayList<CatOFP>) getAttList(Obj.OFSPLF, Att.CATOFP);
     1227        if (cats.get(0) == CatOFP.OFP_FPSO)
     1228            Renderer.symbol(Buoys.Storage);
     1229        else
     1230            Renderer.symbol(Landmarks.Platform);
     1231        addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(20, -50)));
     1232        Signals.addSignals();
     1233    }
     1234
     1235    private static void ports() {
     1236        if (Renderer.zoom >= 14) {
     1237            if (feature.type == Obj.CRANES) {
     1238                if ((CatCRN) getAttEnum(feature.type, Att.CATCRN) == CatCRN.CRN_CONT)
     1239                    Renderer.symbol(Harbours.ContainerCrane);
     1240                else
     1241                    Renderer.symbol(Harbours.PortCrane);
     1242            } else if (feature.type == Obj.HULKES) {
     1243                Renderer.lineVector(new LineStyle(Color.black, 4, null, new Color(0xffe000)));
     1244                addName(15, new Font("Arial", Font.BOLD, 40));
     1245            }
     1246        }
     1247    }
     1248
     1249    private static void separation() {
     1250        switch (feature.type) {
     1251        case TSEZNE:
     1252        case TSSCRS:
     1253        case TSSRON:
     1254            if (Renderer.zoom <= 15)
     1255                Renderer.lineVector(new LineStyle(Symbols.Mtss));
     1256            else
     1257                Renderer.lineVector(new LineStyle(Symbols.Mtss, 20, null, null));
     1258            addName(10, new Font("Arial", Font.BOLD, 150), Symbols.Mline);
     1259            break;
     1260        case TSELNE:
     1261            Renderer.lineVector(new LineStyle(Symbols.Mtss, 20, null, null));
     1262            break;
     1263        case TSSLPT:
     1264            Renderer.lineSymbols(Areas.LaneArrow, 0.5, null, null, 0, Symbols.Mtss);
     1265            break;
     1266        case TSSBND:
     1267            Renderer.lineVector(new LineStyle(Symbols.Mtss, 20, new float[] {40, 40}, null));
     1268            break;
     1269        case ISTZNE:
     1270            Renderer.lineSymbols(Areas.Restricted, 1.0, null, null, 0, Symbols.Mtss);
     1271            break;
     1272        default:
     1273            break;
     1274        }
     1275    }
     1276
     1277    @SuppressWarnings("unchecked")
     1278    private static void shoreline() {
     1279        CatSLC cat = (CatSLC) getAttEnum(feature.type, Att.CATSLC);
     1280        if ((Renderer.context.ruleset() == RuleSet.ALL) || (Renderer.context.ruleset() == RuleSet.BASE)) {
     1281            if ((cat != CatSLC.SLC_SWAY) && (cat != CatSLC.SLC_TWAL)) {
     1282                if (Renderer.zoom >= 12) {
     1283                    Renderer.lineVector(new LineStyle(Color.black, 10, Symbols.Yland));
     1284                } else {
     1285                    Renderer.lineVector(new LineStyle(Symbols.Yland));
     1286                }
     1287            }
     1288        }
     1289        if ((Renderer.context.ruleset() == RuleSet.ALL) || (Renderer.context.ruleset() == RuleSet.SEAMARK)) {
     1290            if (Renderer.zoom >= 12) {
     1291                switch (cat) {
     1292                case SLC_TWAL:
     1293                    WatLEV lev = (WatLEV) getAttEnum(feature.type, Att.WATLEV);
     1294                    if (lev == WatLEV.LEV_CVRS) {
     1295                        Renderer.lineVector(new LineStyle(Color.black, 10, new float[] {40, 40}, null));
     1296                        if (Renderer.zoom >= 15)
     1297                            Renderer.lineText("(covers)", new Font("Arial", Font.PLAIN, 60), Color.black, 80);
     1298                    } else {
     1299                        Renderer.lineVector(new LineStyle(Color.black, 10, null, null));
     1300                    }
     1301                    if (Renderer.zoom >= 15)
     1302                        Renderer.lineText("Training Wall", new Font("Arial", Font.PLAIN, 60), Color.black, -30);
     1303                    break;
     1304                case SLC_SWAY:
     1305                    Renderer.lineVector(new LineStyle(Color.black, 2, null, new Color(0xffe000)));
     1306                    if ((Renderer.zoom >= 16) && feature.objs.containsKey(Obj.SMCFAC)) {
     1307                        ArrayList<Symbol> symbols = new ArrayList<>();
     1308                        ArrayList<CatSCF> scfs = (ArrayList<CatSCF>) getAttList(Obj.SMCFAC, Att.CATSCF);
     1309                        for (CatSCF scf : scfs) {
     1310                            symbols.add(Facilities.Cats.get(scf));
     1311                        }
     1312                        Renderer.cluster(symbols);
     1313                    }
     1314                    break;
     1315                default:
     1316                    break;
     1317                }
     1318            }
     1319        }
     1320    }
     1321
     1322    @SuppressWarnings("unchecked")
     1323    private static void stations() {
     1324        if (Renderer.zoom >= 14) {
     1325            String str = "";
     1326            switch (feature.type) {
     1327            case SISTAT:
     1328                Renderer.symbol(Harbours.SignalStation);
     1329                str = "SS";
     1330                ArrayList<CatSIT> tcats = (ArrayList<CatSIT>) getAttList(Obj.SISTAT, Att.CATSIT);
     1331                switch (tcats.get(0)) {
     1332                case SIT_IPT:
     1333                    str += "(INT)";
     1334                    break;
     1335                case SIT_PRTE:
     1336                    str += "(Traffic)";
     1337                    break;
     1338                case SIT_PRTC:
     1339                    str += "(Port Control)";
     1340                    break;
     1341                case SIT_LOCK:
     1342                    str += "(Lock)";
     1343                    break;
     1344                case SIT_BRDG:
     1345                    str += "(Bridge)";
     1346                    break;
     1347                default:
     1348                    break;
     1349                }
     1350                break;
     1351            case SISTAW:
     1352                Renderer.symbol(Harbours.SignalStation);
     1353                str = "SS";
     1354                str = "SS";
     1355                ArrayList<CatSIW> wcats = (ArrayList<CatSIW>) getAttList(Obj.SISTAW, Att.CATSIW);
     1356                switch (wcats.get(0)) {
     1357                case SIW_STRM:
     1358                    str += "(Storm)";
     1359                    break;
     1360                case SIW_WTHR:
     1361                    str += "(Weather)";
     1362                    break;
     1363                case SIW_ICE:
     1364                    str += "(Ice)";
     1365                    break;
     1366                case SIW_TIDG:
     1367                    str = "Tide gauge";
     1368                    break;
     1369                case SIW_TIDS:
     1370                    str = "Tide scale";
     1371                    break;
     1372                case SIW_TIDE:
     1373                    str += "(Tide)";
     1374                    break;
     1375                case SIW_TSTR:
     1376                    str += "(Stream)";
     1377                    break;
     1378                case SIW_DNGR:
     1379                    str += "(Danger)";
     1380                    break;
     1381                case SIW_MILY:
     1382                    str += "(Firing)";
     1383                    break;
     1384                case SIW_TIME:
     1385                    str += "(Time)";
     1386                    break;
     1387                default:
     1388                    break;
     1389                }
     1390                break;
     1391            case RDOSTA:
     1392            case RTPBCN:
     1393                Renderer.symbol(Harbours.SignalStation);
     1394                Renderer.symbol(Beacons.RadarStation);
     1395                break;
     1396            case RADRFL:
     1397                Renderer.symbol(Topmarks.RadarReflector);
     1398                break;
     1399            case RADSTA:
     1400                Renderer.symbol(Harbours.SignalStation);
     1401                Renderer.symbol(Beacons.RadarStation);
     1402                Renderer.labelText("Ra", new Font("Arial", Font.PLAIN, 40), Symbols.Msymb, new Delta(Handle.TR, AffineTransform.getTranslateInstance(-30, -70)));
     1403                break;
     1404            case PILBOP:
     1405                Renderer.symbol(Harbours.Pilot);
     1406                addName(15, new Font("Arial", Font.BOLD, 40), Symbols.Msymb, new Delta(Handle.LC, AffineTransform.getTranslateInstance(70, -40)));
     1407                CatPIL cat = (CatPIL) getAttEnum(feature.type, Att.CATPIL);
     1408                if (cat == CatPIL.PIL_HELI) {
     1409                    Renderer.labelText("H", new Font("Arial", Font.PLAIN, 40), Symbols.Msymb, new Delta(Handle.LC, AffineTransform.getTranslateInstance(70, 0)));
     1410                }
     1411                break;
     1412            case CGUSTA:
     1413                Renderer.symbol(Harbours.SignalStation);
     1414                str = "CG";
     1415                if (feature.objs.containsKey(Obj.RSCSTA)) Renderer.symbol(Harbours.Rescue, new Delta(Handle.CC, AffineTransform.getTranslateInstance(130, 0)));
     1416                break;
     1417            case RSCSTA:
     1418                Renderer.symbol(Harbours.Rescue);
     1419                break;
     1420            default:
     1421                break;
     1422            }
     1423            if ((Renderer.zoom >= 15) && !str.isEmpty()) {
     1424                Renderer.labelText(str, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.LC, AffineTransform.getTranslateInstance(40, 0)));
     1425            }
     1426            Signals.addSignals();
     1427        }
     1428    }
     1429
     1430    private static void transits() {
     1431        if (Renderer.zoom >= 14) {
     1432            if (feature.type == Obj.RECTRC) Renderer.lineVector(new LineStyle(Color.black, 10, null, null));
     1433            else if (feature.type == Obj.NAVLNE) Renderer.lineVector(new LineStyle(Color.black, 10, new float[] {25, 25}, null));
     1434        }
     1435        if (Renderer.zoom >= 15) {
     1436            String str = "";
     1437            String name = getName();
     1438            if (name != null)
     1439                str += name + " ";
     1440            Double ort;
     1441            if ((ort = (Double) getAttVal(feature.type, Att.ORIENT)) != null) {
     1442                str += df.format(ort) + "º";
     1443                if (!str.isEmpty())
     1444                    Renderer.lineText(str, new Font("Arial", Font.PLAIN, 80), Color.black, -20);
     1445            }
     1446        }
     1447    }
     1448
     1449    private static void waterways() {
     1450        Renderer.lineVector(new LineStyle(Symbols.Bwater, 20, (feature.geom.prim == Pflag.AREA) ? Symbols.Bwater : null));
     1451    }
     1452
     1453    private static void wrecks() {
     1454        if (Renderer.zoom >= 14) {
     1455            switch ((CatWRK) getAttEnum(feature.type, Att.CATWRK)) {
     1456            case WRK_DNGR:
     1457            case WRK_MSTS:
     1458                Renderer.symbol(Areas.WreckD);
     1459                break;
     1460            case WRK_HULS:
     1461                Renderer.symbol(Areas.WreckS);
     1462                break;
     1463            default:
     1464                Renderer.symbol(Areas.WreckND);
     1465            }
     1466        }
     1467    }
    14111468}
  • applications/editors/josm/plugins/seachart/src/render/Signals.java

    r32393 r32394  
    1212import java.awt.Color;
    1313import java.awt.Font;
    14 import java.awt.geom.*;
     14import java.awt.geom.AffineTransform;
    1515import java.text.DecimalFormat;
    1616import java.util.ArrayList;
    1717import java.util.EnumMap;
    1818
     19import s57.S57att.Att;
     20import s57.S57map.AttMap;
     21import s57.S57map.ObjTab;
     22import s57.S57obj.Obj;
     23import s57.S57val.BoySHP;
     24import s57.S57val.CatFOG;
    1925import s57.S57val.CatLIT;
     26import s57.S57val.CatROS;
     27import s57.S57val.CatRTB;
    2028import s57.S57val.ColCOL;
    21 import s57.S57att.*;
    22 import s57.S57obj.*;
    23 import s57.S57val.*;
    24 import s57.S57map.*;
     29import s57.S57val.LitCHR;
    2530import symbols.Beacons;
    2631import symbols.Symbols;
     32import symbols.Symbols.Delta;
     33import symbols.Symbols.Handle;
     34import symbols.Symbols.Scheme;
    2735import symbols.Topmarks;
    28 import symbols.Symbols.*;
    29 
    30 public class Signals extends Rules{
    31 
    32         static final EnumMap<ColCOL, Color> LightColours = new EnumMap<>(ColCOL.class);
    33         static {
    34                 LightColours.put(ColCOL.COL_WHT, new Color(0xffff00));
    35                 LightColours.put(ColCOL.COL_RED, new Color(0xff0000));
    36                 LightColours.put(ColCOL.COL_GRN, new Color(0x00ff00));
    37                 LightColours.put(ColCOL.COL_BLU, new Color(0x0000ff));
    38                 LightColours.put(ColCOL.COL_YEL, new Color(0xffff00));
    39                 LightColours.put(ColCOL.COL_AMB, new Color(0xffc200));
    40                 LightColours.put(ColCOL.COL_VIO, new Color(0xee82ee));
    41                 LightColours.put(ColCOL.COL_ORG, Color.orange);
    42                 LightColours.put(ColCOL.COL_MAG, Color.magenta);
    43         }
    44 
    45         static final EnumMap<ColCOL, String> LightLetters = new EnumMap<>(ColCOL.class);
    46         static {
    47                 LightLetters.put(ColCOL.COL_WHT, "W");
    48                 LightLetters.put(ColCOL.COL_RED, "R");
    49                 LightLetters.put(ColCOL.COL_GRN, "G");
    50                 LightLetters.put(ColCOL.COL_BLU, "Bu");
    51                 LightLetters.put(ColCOL.COL_YEL, "Y");
    52                 LightLetters.put(ColCOL.COL_AMB, "Am");
    53                 LightLetters.put(ColCOL.COL_VIO, "Vi");
    54                 LightLetters.put(ColCOL.COL_ORG, "Or");
    55         }
    56 
    57         static final EnumMap<LitCHR, String> LightCharacters = new EnumMap<>(LitCHR.class);
    58         static {
    59                 LightCharacters.put(LitCHR.CHR_F, "F");
    60                 LightCharacters.put(LitCHR.CHR_FL, "Fl");
    61                 LightCharacters.put(LitCHR.CHR_LFL, "LFl");
    62                 LightCharacters.put(LitCHR.CHR_Q, "Q");
    63                 LightCharacters.put(LitCHR.CHR_VQ, "VQ");
    64                 LightCharacters.put(LitCHR.CHR_UQ, "UQ");
    65                 LightCharacters.put(LitCHR.CHR_ISO, "Iso");
    66                 LightCharacters.put(LitCHR.CHR_OC, "Oc");
    67                 LightCharacters.put(LitCHR.CHR_IQ, "IQ");
    68                 LightCharacters.put(LitCHR.CHR_IVQ, "IVQ");
    69                 LightCharacters.put(LitCHR.CHR_IUQ, "IUQ");
    70                 LightCharacters.put(LitCHR.CHR_MO, "Mo");
    71                 LightCharacters.put(LitCHR.CHR_FFL, "FFl");
    72                 LightCharacters.put(LitCHR.CHR_FLLFL, "FlLFl");
    73                 LightCharacters.put(LitCHR.CHR_OCFL, "OcFl");
    74                 LightCharacters.put(LitCHR.CHR_FLFL, "FLFl");
    75                 LightCharacters.put(LitCHR.CHR_ALOC, "Al.Oc");
    76                 LightCharacters.put(LitCHR.CHR_ALLFL, "Al.LFl");
    77                 LightCharacters.put(LitCHR.CHR_ALFL, "Al.Fl");
    78                 LightCharacters.put(LitCHR.CHR_ALGR, "Al.Gr");
    79                 LightCharacters.put(LitCHR.CHR_QLFL, "Q+LFl");
    80                 LightCharacters.put(LitCHR.CHR_VQLFL, "VQ+LFl");
    81                 LightCharacters.put(LitCHR.CHR_UQLFL, "UQ+LFl");
    82                 LightCharacters.put(LitCHR.CHR_AL, "Al");
    83                 LightCharacters.put(LitCHR.CHR_ALFFL, "Al.FFl");
    84         }
    85        
    86         static final EnumMap<CatFOG, String> fogSignals = new EnumMap<>(CatFOG.class);
    87         static {
    88                 fogSignals.put(CatFOG.FOG_EXPL, "Explos");
    89                 fogSignals.put(CatFOG.FOG_DIA, "Dia");
    90                 fogSignals.put(CatFOG.FOG_SIRN, "Siren");
    91                 fogSignals.put(CatFOG.FOG_NAUT, "Horn");
    92                 fogSignals.put(CatFOG.FOG_REED, "Horn");
    93                 fogSignals.put(CatFOG.FOG_TYPH, "Horn");
    94                 fogSignals.put(CatFOG.FOG_BELL, "Bell");
    95                 fogSignals.put(CatFOG.FOG_WHIS, "Whis");
    96                 fogSignals.put(CatFOG.FOG_GONG, "Gong");
    97                 fogSignals.put(CatFOG.FOG_HORN, "Horn");
    98         }
    99 
    100         static final DecimalFormat df = new DecimalFormat("#.#");
    101        
    102         public static void addSignals() {
    103           if (feature.objs.containsKey(Obj.RADRFL)) reflectors();
    104           if (feature.objs.containsKey(Obj.FOGSIG)) fogSignals();
    105           if (feature.objs.containsKey(Obj.RTPBCN)) radarStations();
    106           if (feature.objs.containsKey(Obj.RADSTA)) radarStations();
    107           if (feature.objs.containsKey(Obj.RDOSTA)) radioStations();
    108           if (feature.objs.containsKey(Obj.LIGHTS)) lights();
    109         }
    110 
    111         public static void reflectors() {
    112                 if (Renderer.zoom >= 14) {
    113                         switch (feature.type) {
    114                         case BCNLAT:
    115                         case BCNCAR:
    116                         case BCNISD:
    117                         case BCNSAW:
    118                         case BCNSPP:
    119                                 if ((feature.objs.containsKey(Obj.TOPMAR)) || (feature.objs.containsKey(Obj.DAYMAR))) {
    120                                         Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -140)));
    121                                 } else {
    122                                         Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -80)));
    123                                 }
    124                                 break;
    125                         case LITFLT:
    126                         case LITVES:
    127                         case BOYINB:
    128                                 if ((feature.objs.containsKey(Obj.TOPMAR)) || (feature.objs.containsKey(Obj.DAYMAR))) {
    129                                         Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -110)));
    130                                 } else {
    131                                         Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -60)));
    132                                 }
    133                                 break;
    134                         case LITMAJ:
    135                         case LITMIN:
    136                                 if ((feature.objs.containsKey(Obj.TOPMAR)) || (feature.objs.containsKey(Obj.DAYMAR))) {
    137                                         Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -90)));
    138                                 } else {
    139                                         Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -30)));
    140                                 }
    141                                 break;
    142                         case BOYLAT:
    143                         case BOYCAR:
    144                         case BOYISD:
    145                         case BOYSAW:
    146                         case BOYSPP:
    147                                 if ((feature.objs.containsKey(Obj.TOPMAR)) || (feature.objs.containsKey(Obj.DAYMAR))) {
    148                                         if (testAttribute(feature.type, Att.BOYSHP, BoySHP.BOY_PILR) || testAttribute(feature.type, Att.BOYSHP, BoySHP.BOY_SPAR)) {
    149                                                 Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(50, -160)));
    150                                         } else {
    151                                                 Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(25, -80)));
    152                                         }
    153                                 } else {
    154                                         if (testAttribute(feature.type, Att.BOYSHP, BoySHP.BOY_PILR) || testAttribute(feature.type, Att.BOYSHP, BoySHP.BOY_SPAR)) {
    155                                                 Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(30, -100)));
    156                                         } else {
    157                                                 Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(10, -50)));
    158                                         }
    159                                 }
    160                                 break;
    161                         default:
    162                                 break;
    163                         }
    164                 }
    165         }
    166        
    167         public static void fogSignals() {
    168                 if (Renderer.zoom >= 11)
    169                         Renderer.symbol(Beacons.FogSignal);
    170                 if (Renderer.zoom >= 15) {
    171                         AttMap atts = feature.objs.get(Obj.FOGSIG).get(0);
    172                         if (atts != null) {
    173                                 String str = "";
    174                                 if (atts.containsKey(Att.CATFOG)) {
    175                                         str += fogSignals.get(((ArrayList<?>) (atts.get(Att.CATFOG).val)).get(0));
    176                                 }
    177                                 if (atts.containsKey(Att.SIGGRP)) {
    178                                         str += "(" + atts.get(Att.SIGGRP).val + ")";
    179                                 } else {
    180                                         str += " ";
    181                                 }
    182                                 if (atts.containsKey(Att.SIGPER)) {
    183                                         str += df.format(atts.get(Att.SIGPER).val) + "s";
    184                                 }
    185                                 if (atts.containsKey(Att.VALMXR)) {
    186                                         str += df.format(atts.get(Att.VALMXR).val) + "M";
    187                                 }
    188                                 if (!str.isEmpty()) {
    189                                         Renderer.labelText(str, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.TR, AffineTransform.getTranslateInstance(-60, -30)));
    190                                 }
    191                         }
    192                 }
    193         }
    194 
    195         public static void radarStations() {
    196                 if (Renderer.zoom >= 11)
    197                         Renderer.symbol(Beacons.RadarStation);
    198                 if (Renderer.zoom >= 15) {
    199                         String bstr = "";
    200                         CatRTB cat = (CatRTB) getAttEnum(Obj.RTPBCN, Att.CATRTB);
    201                         String wal = getAttStr(Obj.RTPBCN, Att.RADWAL);
    202                         switch (cat) {
    203                         case RTB_RAMK:
    204                                 bstr += " Ramark";
    205                                 break;
    206                         case RTB_RACN:
    207                                 bstr += " Racon";
    208                                 String astr = getAttStr(Obj.RTPBCN, Att.SIGGRP);
    209                                 if (!astr.isEmpty()) {
    210                                         bstr += "(" + astr + ")";
    211                                 }
    212                                 Double per = (Double) getAttVal(Obj.RTPBCN, Att.SIGPER);
    213                                 Double mxr = (Double) getAttVal(Obj.RTPBCN, Att.VALMXR);
    214                                 if ((per != null) || (mxr != null)) {
    215                                         bstr += (astr.isEmpty() ? " " : "");
    216                                         if (per != null)
    217                                                 bstr += (per != 0) ? per.toString() + "s" : "";
    218                                         if (mxr != null)
    219                                                 bstr += (mxr != 0) ? mxr.toString() + "M" : "";
    220                                 }
    221                                 break;
    222                         default:
    223                                 break;
    224                         }
    225                         if (!wal.isEmpty()) {
    226                                 switch (wal) {
    227                                 case "0.03-X":
    228                                         bstr += "(3cm)";
    229                                         break;
    230                                 case "0.10-S":
    231                                         bstr += "(10cm)";
    232                                         break;
    233                                 }
    234                         }
    235                         if (!bstr.isEmpty()) {
    236                                 Renderer.labelText(bstr, new Font("Arial", Font.PLAIN, 40), Symbols.Msymb, new Delta(Handle.TR, AffineTransform.getTranslateInstance(-30, -70)));
    237                         }
    238                 }
    239         }
    240 
    241         @SuppressWarnings("unchecked")
    242         public static void radioStations() {
    243                 boolean vais = false;
    244                 String bstr = "";
    245                 if (Renderer.zoom >= 11) {
    246                         ArrayList<CatROS> cats = (ArrayList<CatROS>) getAttList(Obj.RDOSTA, Att.CATROS);
    247                         for (CatROS ros : cats) {
    248                                 switch (ros) {
    249                                 case ROS_OMNI:
    250                                         bstr += " RC";
    251                                         break;
    252                                 case ROS_DIRL:
    253                                         bstr += " RD";
    254                                         break;
    255                                 case ROS_ROTP:
    256                                         bstr += " RW";
    257                                         break;
    258                                 case ROS_CNSL:
    259                                         bstr += " Consol";
    260                                         break;
    261                                 case ROS_RDF:
    262                                         bstr += " RG";
    263                                         break;
    264                                 case ROS_QTA:
    265                                         bstr += " R";
    266                                         break;
    267                                 case ROS_AERO:
    268                                         bstr += " AeroRC";
    269                                         break;
    270                                 case ROS_DECA:
    271                                         bstr += " Decca";
    272                                         break;
    273                                 case ROS_LORN:
    274                                         bstr += " Loran";
    275                                         break;
    276                                 case ROS_DGPS:
    277                                         bstr += " DGPS";
    278                                         break;
    279                                 case ROS_TORN:
    280                                         bstr += " Toran";
    281                                         break;
    282                                 case ROS_OMGA:
    283                                         bstr += " Omega";
    284                                         break;
    285                                 case ROS_SYLD:
    286                                         bstr += " Syledis";
    287                                         break;
    288                                 case ROS_CHKA:
    289                                         bstr += " Chiaka";
    290                                         break;
    291                                 case ROS_PCOM:
    292                                 case ROS_COMB:
    293                                 case ROS_FACS:
    294                                 case ROS_TIME:
    295                                         break;
    296                                 case ROS_PAIS:
    297                                 case ROS_SAIS:
    298                                         bstr += " AIS";
    299                                         break;
    300                                 case ROS_VAIS:
    301                                         vais = true;
    302                                         break;
    303                                 case ROS_VANC:
    304                                         vais = true;
    305                                         Renderer.symbol(Topmarks.TopNorth, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
    306                                         break;
    307                                 case ROS_VASC:
    308                                         vais = true;
    309                                         Renderer.symbol(Topmarks.TopSouth, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
    310                                         break;
    311                                 case ROS_VAEC:
    312                                         vais = true;
    313                                         Renderer.symbol(Topmarks.TopEast, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
    314                                         break;
    315                                 case ROS_VAWC:
    316                                         vais = true;
    317                                         Renderer.symbol(Topmarks.TopWest, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
    318                                         break;
    319                                 case ROS_VAPL:
    320                                         vais = true;
    321                                         Renderer.symbol(Topmarks.TopCan, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
    322                                         break;
    323                                 case ROS_VASL:
    324                                         vais = true;
    325                                         Renderer.symbol(Topmarks.TopCone, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
    326                                         break;
    327                                 case ROS_VAID:
    328                                         vais = true;
    329                                         Renderer.symbol(Topmarks.TopIsol, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
    330                                         break;
    331                                 case ROS_VASW:
    332                                         vais = true;
    333                                         Renderer.symbol(Topmarks.TopSphere, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
    334                                         break;
    335                                 case ROS_VASP:
    336                                         vais = true;
    337                                         Renderer.symbol(Topmarks.TopX, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
    338                                         break;
    339                                 case ROS_VAWK:
    340                                         vais = true;
    341                                         Renderer.symbol(Topmarks.TopCross, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
    342                                         break;
    343                                 default:
    344                                         break;
    345                                 }
    346                         }
    347                         if (!vais) {
    348                                 Renderer.symbol(Beacons.RadarStation);
    349                         }
    350                 }
    351                 if (Renderer.zoom >= 15) {
    352                         if (vais) {
    353                                 Renderer.labelText("V-AIS", new Font("Arial", Font.PLAIN, 40), Symbols.Msymb, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, 70)));
    354                         }
    355                         if (!bstr.isEmpty()) {
    356                                 Renderer.labelText(bstr, new Font("Arial", Font.PLAIN, 40), Symbols.Msymb, new Delta(Handle.TR, AffineTransform.getTranslateInstance(-30, -110)));
    357                         }
    358                 }
    359         }
    360 
    361         class Sect {
    362     int dir;
    363     LitCHR chr;
    364     ColCOL col;
    365     ColCOL alt;
    366     String grp;
    367     double per;
    368     double rng;
    369         }
    370        
    371         @SuppressWarnings("unchecked")
    372         public static void lights() {
    373                 Enum<ColCOL> col = null;
    374                 Enum<ColCOL> tcol = null;
    375                 ObjTab lights = feature.objs.get(Obj.LIGHTS);
    376                 for (AttMap atts : lights.values()) {
    377                         if (atts.containsKey(Att.COLOUR)) {
    378                                 ArrayList<Enum<ColCOL>> cols = (ArrayList<Enum<ColCOL>>) atts.get(Att.COLOUR).val;
    379                                 if (cols.size() == 1) {
    380                                         tcol = cols.get(0);
    381                                         if (col == null) {
    382                                                 col = tcol;
    383                                         } else if (tcol != col) {
    384                                                 col = ColCOL.COL_MAG;
    385                                                 break;
    386                                         }
    387                                 } else {
    388                                         col = ColCOL.COL_MAG;
    389                                         break;
    390                                 }
    391                         }
    392                 }
    393                 Renderer.symbol(Beacons.LightFlare, new Scheme(LightColours.get(col)), new Delta(Handle.BC, AffineTransform.getRotateInstance(Math.toRadians(120))));
    394                 if (Renderer.zoom >= 12) {
    395                         String str = "";
    396                         if (lights.get(1) != null) {
    397                                 for (AttMap atts : lights.values()) {
    398                                         Enum<ColCOL> col1 = null;
    399                                         Enum<ColCOL> col2 = null;
    400                                         double radius = 0.2;
    401                                         double s1 = 361;
    402                                         double s2 = 361;
    403                                         Double dir = null;
    404                                         if (atts.containsKey(Att.COLOUR)) {
    405                                                 ArrayList<Enum<ColCOL>> cols = (ArrayList<Enum<ColCOL>>) atts.get(Att.COLOUR).val;
    406                                                 col1 = cols.get(0);
    407                                                 if (cols.size() > 1)
    408                                                         col2 = cols.get(1);
    409                                         } else {
    410                                                 continue;
    411                                         }
    412                                         if (atts.containsKey(Att.LITRAD)) {
    413                                                 radius = (Double) atts.get(Att.LITRAD).val;
    414                                         }
    415                                         if (atts.containsKey(Att.CATLIT)) {
    416                                                 ArrayList<CatLIT> cats = (ArrayList<CatLIT>) atts.get(Att.CATLIT).val;
    417                                                 if (cats.contains(CatLIT.LIT_DIR)) {
    418                                                         if (atts.containsKey(Att.ORIENT)) {
    419                                                                 dir = (Double) atts.get(Att.ORIENT).val;
    420                                                                 s1 = ((dir - 4) + 360) % 360;
    421                                                                 s2 = (dir + 4) % 360;
    422                                                                 for (AttMap satts : lights.values()) {
    423                                                                         double srad = 0.2;
    424                                                                         double ss1 = 361;
    425                                                                         double ss2 = 361;
    426                                                                         Double sdir = null;
    427                                                                         if (satts == atts)
    428                                                                                 continue;
    429                                                                         if (satts.containsKey(Att.LITRAD)) {
    430                                                                                 srad = (Double) satts.get(Att.LITRAD).val;
    431                                                                         }
    432                                                                         if (srad == radius) {
    433                                                                                 ArrayList<CatLIT> scats = (satts.containsKey(Att.CATLIT)) ? (ArrayList<CatLIT>) satts.get(Att.CATLIT).val : new ArrayList<CatLIT>();
    434                                                                                 if (scats.contains(CatLIT.LIT_DIR)) {
    435                                                                                         if (satts.containsKey(Att.ORIENT)) {
    436                                                                                                 sdir = (Double) satts.get(Att.ORIENT).val;
    437                                                                                                 ss1 = sdir;
    438                                                                                                 ss2 = sdir;
    439                                                                                         }
    440                                                                                 } else {
    441                                                                                         if (satts.containsKey(Att.SECTR1)) {
    442                                                                                                 ss1 = (Double) satts.get(Att.SECTR1).val;
    443                                                                                         }
    444                                                                                         if (satts.containsKey(Att.SECTR2)) {
    445                                                                                                 ss2 = (Double) satts.get(Att.SECTR2).val;
    446                                                                                         }
    447                                                                                 }
    448                                                                                 if ((ss1 > 360) || (ss2 > 360))
    449                                                                                         continue;
    450                                                                                 if (sdir != null) {
    451                                                                                         if (((dir - sdir + 360) % 360) < 8) {
    452                                                                                                 s1 = ((((sdir > dir) ? 360 : 0) + sdir + dir) / 2) % 360;
    453                                                                                         }
    454                                                                                         if (((sdir - dir + 360) % 360) < 8) {
    455                                                                                                 s2 = ((((dir > sdir) ? 360 : 0) + sdir + dir) / 2) % 360;
    456                                                                                         }
    457                                                                                 } else {
    458                                                                                         if (((dir - ss2 + 360) % 360) < 4) {
    459                                                                                                 s1 = ss2;
    460                                                                                         }
    461                                                                                         if (((ss1 - dir + 360) % 360) < 4) {
    462                                                                                                 s2 = ss1;
    463                                                                                         }
    464                                                                                 }
    465                                                                         }
    466                                                                 }
    467                                                         }
    468                                                 }
    469                                         }
    470                                         if ((s1 > 360) && atts.containsKey(Att.SECTR1)) {
    471                                                 s1 = (Double) atts.get(Att.SECTR1).val;
    472                                         } else if (dir == null) {
    473                                                 continue;
    474                                         }
    475                                         if ((s2 > 360) && atts.containsKey(Att.SECTR2)) {
    476                                                 s2 = (Double) atts.get(Att.SECTR2).val;
    477                                         } else if (dir == null) {
    478                                                 continue;
    479                                         }
    480                                         str = "";
    481                                         if (atts.containsKey(Att.LITCHR)) {
    482                                                 str += LightCharacters.get(((ArrayList<LitCHR>) atts.get(Att.LITCHR).val).get(0));
    483                                         }
    484                                         if (atts.containsKey(Att.SIGGRP)) {
    485                                                 str += "(" + atts.get(Att.SIGGRP).val + ")";
    486                                         } else if (!str.isEmpty()) {
    487                                                 str += ".";
    488                                         }
    489                                         if (atts.containsKey(Att.COLOUR)) {
    490                                                 ArrayList<Enum<ColCOL>> cols = (ArrayList<Enum<ColCOL>>) atts.get(Att.COLOUR).val;
    491                                                 str += LightLetters.get(cols.get(0));
    492                                                 if (cols.size() > 1)
    493                                                         str += LightLetters.get(cols.get(1));
    494                                         }
    495                                         if (atts.containsKey(Att.SIGPER)) {
    496                                                 str += "." + df.format(atts.get(Att.SIGPER).val) + "s";
    497                                         }
    498                                         if ((s1 <= 360) && (s2 <= 360) && (s1 != s2))
    499                                                 Renderer.lightSector(LightColours.get(col1), LightColours.get(col2), radius, s1, s2, dir, (Renderer.zoom >= 15) ? str : "");
    500                                 }
    501                                 if (Renderer.zoom >= 15) {
    502                                         class LitSect {
    503                                                 boolean dir;
    504                                                 LitCHR chr;
    505                                                 ColCOL col;
    506                                                 String grp;
    507                                                 double per;
    508                                                 double rng;
    509                                                 double hgt;
    510                                         }
    511                                         ArrayList<LitSect> litatts = new ArrayList<>();
    512                                         for (AttMap atts : lights.values()) {
    513                                                 LitSect sect = new LitSect();
    514                                                 sect.dir = (atts.containsKey(Att.CATLIT) && ((ArrayList<CatLIT>) atts.get(Att.CATLIT).val).contains(CatLIT.LIT_DIR));
    515                                                 sect.chr = atts.containsKey(Att.LITCHR) ? ((ArrayList<LitCHR>) atts.get(Att.LITCHR).val).get(0) : LitCHR.CHR_UNKN;
    516                                                 switch (sect.chr) {
    517                                                 case CHR_AL:
    518                                                         sect.chr = LitCHR.CHR_F;
    519                                                         break;
    520                                                 case CHR_ALOC:
    521                                                         sect.chr = LitCHR.CHR_OC;
    522                                                         break;
    523                                                 case CHR_ALLFL:
    524                                                         sect.chr = LitCHR.CHR_LFL;
    525                                                         break;
    526                                                 case CHR_ALFL:
    527                                                         sect.chr = LitCHR.CHR_FL;
    528                                                         break;
    529                                                 case CHR_ALFFL:
    530                                                         sect.chr = LitCHR.CHR_FFL;
    531                                                         break;
    532                                                 default:
    533                                                         break;
    534                                                 }
    535                                                 sect.grp = atts.containsKey(Att.SIGGRP) ? (String) atts.get(Att.SIGGRP).val : "";
    536                                                 sect.per = atts.containsKey(Att.SIGPER) ? (Double) atts.get(Att.SIGPER).val : 0.0;
    537                                                 sect.rng = atts.containsKey(Att.VALNMR) ? (Double) atts.get(Att.VALNMR).val : 0.0;
    538                                                 sect.hgt = atts.containsKey(Att.HEIGHT) ? (Double) atts.get(Att.HEIGHT).val : 0.0;
    539                                                 ArrayList<ColCOL> cols = (ArrayList<ColCOL>) (atts.containsKey(Att.COLOUR) ? atts.get(Att.COLOUR).val : new ArrayList<>());
    540                                                 sect.col = cols.size() > 0 ? cols.get(0) : ColCOL.COL_UNK;
    541                                                 if ((sect.chr != LitCHR.CHR_UNKN) && (sect.col != null))
    542                                                         litatts.add(sect);
    543                                         }
    544                                         ArrayList<ArrayList<LitSect>> groupings = new ArrayList<>();
    545                                         for (LitSect lit : litatts) {
    546                                                 boolean found = false;
    547                                                 for (ArrayList<LitSect> group : groupings) {
    548                                                         LitSect mem = group.get(0);
    549                                                         if ((lit.dir == mem.dir) && (lit.chr == mem.chr) && (lit.grp.equals(mem.grp)) && (lit.per == mem.per) && (lit.hgt == mem.hgt)) {
    550                                                                 group.add(lit);
    551                                                                 found = true;
    552                                                         }
    553                                                 }
    554                                                 if (!found) {
    555                                                         ArrayList<LitSect> tmp = new ArrayList<>();
    556                                                         tmp.add(lit);
    557                                                         groupings.add(tmp);
    558                                                 }
    559                                         }
    560                                         for (boolean moved = true; moved;) {
    561                                                 moved = false;
    562                                                 for (int i = 0; i < groupings.size() - 1; i++) {
    563                                                         if (groupings.get(i).size() < groupings.get(i + 1).size()) {
    564                                                                 ArrayList<LitSect> tmp = groupings.remove(i);
    565                                                                 groupings.add(i + 1, tmp);
    566                                                                 moved = true;
    567                                                         }
    568                                                 }
    569                                         }
    570                                         class ColRng {
    571                                                 ColCOL col;
    572                                                 double rng;
    573 
    574                                                 public ColRng(ColCOL c, double r) {
    575                                                         col = c;
    576                                                         rng = r;
    577                                                 }
    578                                         }
    579                                         int y = -30;
    580                                         for (ArrayList<LitSect> group : groupings) {
    581                                                 ArrayList<ColRng> colrng = new ArrayList<>();
    582                                                 for (LitSect lit : group) {
    583                                                         boolean found = false;
    584                                                         for (ColRng cr : colrng) {
    585                                                                 if (cr.col == lit.col) {
    586                                                                         if (lit.rng > cr.rng) {
    587                                                                                 cr.rng = lit.rng;
    588                                                                         }
    589                                                                         found = true;
    590                                                                 }
    591                                                         }
    592                                                         if (!found) {
    593                                                                 colrng.add(new ColRng(lit.col, lit.rng));
    594                                                         }
    595                                                 }
    596                                                 for (boolean moved = true; moved;) {
    597                                                         moved = false;
    598                                                         for (int i = 0; i < colrng.size() - 1; i++) {
    599                                                                 if (colrng.get(i).rng < colrng.get(i + 1).rng) {
    600                                                                         ColRng tmp = colrng.remove(i);
    601                                                                         colrng.add(i + 1, tmp);
    602                                                                         moved = true;
    603                                                                 }
    604                                                         }
    605                                                 }
    606                                                 LitSect tmp = group.get(0);
    607                                                 str = (tmp.dir) ? "Dir" : "";
    608                                                 str += LightCharacters.get(tmp.chr);
    609                                                 if (!tmp.grp.isEmpty())
    610                                                         str += "(" + tmp.grp + ")";
    611                                                 else
    612                                                         str += ".";
    613                                                 for (ColRng cr : colrng) {
    614                                                         str += LightLetters.get(cr.col);
    615                                                 }
    616                                                 if ((tmp.per > 0) || (tmp.hgt > 0) || (colrng.get(0).rng > 0))
    617                                                         str += ".";
    618                                                 if (tmp.per > 0)
    619                                                         str += df.format(tmp.per) + "s";
    620                                                 if (tmp.hgt > 0)
    621                                                         str += df.format(tmp.hgt) + "m";
    622                                                 if (colrng.get(0).rng > 0)
    623                                                         str += df.format(colrng.get(0).rng) + ((colrng.size() > 1) ? ((colrng.size() > 2) ? ("-" + df.format(colrng.get(colrng.size() - 1).rng)) : ("/" + df.format(colrng.get(1).rng))) : "") + "M";
    624                                                 Renderer.labelText(str, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.TL, AffineTransform.getTranslateInstance(60, y)));
    625                                                 y += 40;
    626                                                 str = "";
    627                                         }
    628                                 }
    629                         } else {
    630                                 if (Renderer.zoom >= 15) {
    631                                         AttMap atts = lights.get(0);
    632                                         ArrayList<CatLIT> cats = new ArrayList<>();
    633                                         if (atts.containsKey(Att.CATLIT)) {
    634                                                 cats = (ArrayList<CatLIT>) atts.get(Att.CATLIT).val;
    635                                         }
    636                                         str = (cats.contains(CatLIT.LIT_DIR)) ? "Dir" : "";
    637                                         str += (atts.containsKey(Att.MLTYLT)) ? atts.get(Att.MLTYLT).val : "";
    638                                         if (atts.containsKey(Att.LITCHR)) {
    639                                                 LitCHR chr = ((ArrayList<LitCHR>) atts.get(Att.LITCHR).val).get(0);
    640                                                 if (atts.containsKey(Att.SIGGRP)) {
    641                                                         String grp = (String) atts.get(Att.SIGGRP).val;
    642                                                         switch (chr) {
    643                                                         case CHR_QLFL:
    644                                                                 str += String.format("Q(%s)+LFl", grp);
    645                                                                 break;
    646                                                         case CHR_VQLFL:
    647                                                                 str += String.format("VQ(%s)+LFl", grp);
    648                                                                 break;
    649                                                         case CHR_UQLFL:
    650                                                                 str += String.format("UQ(%s)+LFl", grp);
    651                                                                 break;
    652                                                         default:
    653                                                                 str += String.format("%s(%s)", LightCharacters.get(chr), grp);
    654                                                                 break;
    655                                                         }
    656                                                 } else {
    657                                                         str += LightCharacters.get(chr);
    658                                                 }
    659                                         }
    660                                         if (atts.containsKey(Att.COLOUR)) {
    661                                                 ArrayList<ColCOL> cols = (ArrayList<ColCOL>) atts.get(Att.COLOUR).val;
    662                                                 if (!((cols.size() == 1) && (cols.get(0) == ColCOL.COL_WHT))) {
    663                                                         if (!str.isEmpty() && !str.endsWith(")")) {
    664                                                                 str += ".";
    665                                                         }
    666                                                         for (ColCOL acol : cols) {
    667                                                                 str += LightLetters.get(acol);
    668                                                         }
    669                                                 }
    670                                         }
    671                                         str += (cats.contains(CatLIT.LIT_VERT)) ? "(vert)" : "";
    672                                         str += (cats.contains(CatLIT.LIT_HORI)) ? "(hor)" : "";
    673                                         str += (!str.isEmpty() && (atts.containsKey(Att.SIGPER) || atts.containsKey(Att.HEIGHT) || atts.containsKey(Att.VALMXR)) && !str.endsWith(")")) ? "." : "";
    674                                         str += (atts.containsKey(Att.SIGPER)) ? df.format(atts.get(Att.SIGPER).val) + "s" : "";
    675                                         str += (atts.containsKey(Att.HEIGHT)) ? df.format(atts.get(Att.HEIGHT).val) + "m" : "";
    676                                         str += (atts.containsKey(Att.VALNMR)) ? df.format(atts.get(Att.VALNMR).val) + "M" : "";
    677                                         str += (cats.contains(CatLIT.LIT_FRNT)) ? "(Front)" : "";
    678                                         str += (cats.contains(CatLIT.LIT_REAR)) ? "(Rear)" : "";
    679                                         str += (cats.contains(CatLIT.LIT_UPPR)) ? "(Upper)" : "";
    680                                         str += (cats.contains(CatLIT.LIT_LOWR)) ? "(Lower)" : "";
    681                                         Renderer.labelText(str, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.TL, AffineTransform.getTranslateInstance(60, -30)));
    682                                 }
    683                         }
    684                 }
    685         }
     36
     37public class Signals extends Rules {
     38
     39    static final EnumMap<ColCOL, Color> LightColours = new EnumMap<>(ColCOL.class);
     40    static {
     41        LightColours.put(ColCOL.COL_WHT, new Color(0xffff00));
     42        LightColours.put(ColCOL.COL_RED, new Color(0xff0000));
     43        LightColours.put(ColCOL.COL_GRN, new Color(0x00ff00));
     44        LightColours.put(ColCOL.COL_BLU, new Color(0x0000ff));
     45        LightColours.put(ColCOL.COL_YEL, new Color(0xffff00));
     46        LightColours.put(ColCOL.COL_AMB, new Color(0xffc200));
     47        LightColours.put(ColCOL.COL_VIO, new Color(0xee82ee));
     48        LightColours.put(ColCOL.COL_ORG, Color.orange);
     49        LightColours.put(ColCOL.COL_MAG, Color.magenta);
     50    }
     51
     52    static final EnumMap<ColCOL, String> LightLetters = new EnumMap<>(ColCOL.class);
     53    static {
     54        LightLetters.put(ColCOL.COL_WHT, "W");
     55        LightLetters.put(ColCOL.COL_RED, "R");
     56        LightLetters.put(ColCOL.COL_GRN, "G");
     57        LightLetters.put(ColCOL.COL_BLU, "Bu");
     58        LightLetters.put(ColCOL.COL_YEL, "Y");
     59        LightLetters.put(ColCOL.COL_AMB, "Am");
     60        LightLetters.put(ColCOL.COL_VIO, "Vi");
     61        LightLetters.put(ColCOL.COL_ORG, "Or");
     62    }
     63
     64    static final EnumMap<LitCHR, String> LightCharacters = new EnumMap<>(LitCHR.class);
     65    static {
     66        LightCharacters.put(LitCHR.CHR_F, "F");
     67        LightCharacters.put(LitCHR.CHR_FL, "Fl");
     68        LightCharacters.put(LitCHR.CHR_LFL, "LFl");
     69        LightCharacters.put(LitCHR.CHR_Q, "Q");
     70        LightCharacters.put(LitCHR.CHR_VQ, "VQ");
     71        LightCharacters.put(LitCHR.CHR_UQ, "UQ");
     72        LightCharacters.put(LitCHR.CHR_ISO, "Iso");
     73        LightCharacters.put(LitCHR.CHR_OC, "Oc");
     74        LightCharacters.put(LitCHR.CHR_IQ, "IQ");
     75        LightCharacters.put(LitCHR.CHR_IVQ, "IVQ");
     76        LightCharacters.put(LitCHR.CHR_IUQ, "IUQ");
     77        LightCharacters.put(LitCHR.CHR_MO, "Mo");
     78        LightCharacters.put(LitCHR.CHR_FFL, "FFl");
     79        LightCharacters.put(LitCHR.CHR_FLLFL, "FlLFl");
     80        LightCharacters.put(LitCHR.CHR_OCFL, "OcFl");
     81        LightCharacters.put(LitCHR.CHR_FLFL, "FLFl");
     82        LightCharacters.put(LitCHR.CHR_ALOC, "Al.Oc");
     83        LightCharacters.put(LitCHR.CHR_ALLFL, "Al.LFl");
     84        LightCharacters.put(LitCHR.CHR_ALFL, "Al.Fl");
     85        LightCharacters.put(LitCHR.CHR_ALGR, "Al.Gr");
     86        LightCharacters.put(LitCHR.CHR_QLFL, "Q+LFl");
     87        LightCharacters.put(LitCHR.CHR_VQLFL, "VQ+LFl");
     88        LightCharacters.put(LitCHR.CHR_UQLFL, "UQ+LFl");
     89        LightCharacters.put(LitCHR.CHR_AL, "Al");
     90        LightCharacters.put(LitCHR.CHR_ALFFL, "Al.FFl");
     91    }
     92
     93    static final EnumMap<CatFOG, String> fogSignals = new EnumMap<>(CatFOG.class);
     94    static {
     95        fogSignals.put(CatFOG.FOG_EXPL, "Explos");
     96        fogSignals.put(CatFOG.FOG_DIA, "Dia");
     97        fogSignals.put(CatFOG.FOG_SIRN, "Siren");
     98        fogSignals.put(CatFOG.FOG_NAUT, "Horn");
     99        fogSignals.put(CatFOG.FOG_REED, "Horn");
     100        fogSignals.put(CatFOG.FOG_TYPH, "Horn");
     101        fogSignals.put(CatFOG.FOG_BELL, "Bell");
     102        fogSignals.put(CatFOG.FOG_WHIS, "Whis");
     103        fogSignals.put(CatFOG.FOG_GONG, "Gong");
     104        fogSignals.put(CatFOG.FOG_HORN, "Horn");
     105    }
     106
     107    static final DecimalFormat df = new DecimalFormat("#.#");
     108
     109    public static void addSignals() {
     110        if (feature.objs.containsKey(Obj.RADRFL)) reflectors();
     111        if (feature.objs.containsKey(Obj.FOGSIG)) fogSignals();
     112        if (feature.objs.containsKey(Obj.RTPBCN)) radarStations();
     113        if (feature.objs.containsKey(Obj.RADSTA)) radarStations();
     114        if (feature.objs.containsKey(Obj.RDOSTA)) radioStations();
     115        if (feature.objs.containsKey(Obj.LIGHTS)) lights();
     116    }
     117
     118    public static void reflectors() {
     119        if (Renderer.zoom >= 14) {
     120            switch (feature.type) {
     121            case BCNLAT:
     122            case BCNCAR:
     123            case BCNISD:
     124            case BCNSAW:
     125            case BCNSPP:
     126                if ((feature.objs.containsKey(Obj.TOPMAR)) || (feature.objs.containsKey(Obj.DAYMAR))) {
     127                    Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -140)));
     128                } else {
     129                    Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -80)));
     130                }
     131                break;
     132            case LITFLT:
     133            case LITVES:
     134            case BOYINB:
     135                if ((feature.objs.containsKey(Obj.TOPMAR)) || (feature.objs.containsKey(Obj.DAYMAR))) {
     136                    Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -110)));
     137                } else {
     138                    Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -60)));
     139                }
     140                break;
     141            case LITMAJ:
     142            case LITMIN:
     143                if ((feature.objs.containsKey(Obj.TOPMAR)) || (feature.objs.containsKey(Obj.DAYMAR))) {
     144                    Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -90)));
     145                } else {
     146                    Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -30)));
     147                }
     148                break;
     149            case BOYLAT:
     150            case BOYCAR:
     151            case BOYISD:
     152            case BOYSAW:
     153            case BOYSPP:
     154                if ((feature.objs.containsKey(Obj.TOPMAR)) || (feature.objs.containsKey(Obj.DAYMAR))) {
     155                    if (testAttribute(feature.type, Att.BOYSHP, BoySHP.BOY_PILR) || testAttribute(feature.type, Att.BOYSHP, BoySHP.BOY_SPAR)) {
     156                        Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(50, -160)));
     157                    } else {
     158                        Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(25, -80)));
     159                    }
     160                } else {
     161                    if (testAttribute(feature.type, Att.BOYSHP, BoySHP.BOY_PILR) || testAttribute(feature.type, Att.BOYSHP, BoySHP.BOY_SPAR)) {
     162                        Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(30, -100)));
     163                    } else {
     164                        Renderer.symbol(Topmarks.RadarReflector, new Delta(Handle.BC, AffineTransform.getTranslateInstance(10, -50)));
     165                    }
     166                }
     167                break;
     168            default:
     169                break;
     170            }
     171        }
     172    }
     173
     174    public static void fogSignals() {
     175        if (Renderer.zoom >= 11)
     176            Renderer.symbol(Beacons.FogSignal);
     177        if (Renderer.zoom >= 15) {
     178            AttMap atts = feature.objs.get(Obj.FOGSIG).get(0);
     179            if (atts != null) {
     180                String str = "";
     181                if (atts.containsKey(Att.CATFOG)) {
     182                    str += fogSignals.get(((ArrayList<?>) (atts.get(Att.CATFOG).val)).get(0));
     183                }
     184                if (atts.containsKey(Att.SIGGRP)) {
     185                    str += "(" + atts.get(Att.SIGGRP).val + ")";
     186                } else {
     187                    str += " ";
     188                }
     189                if (atts.containsKey(Att.SIGPER)) {
     190                    str += df.format(atts.get(Att.SIGPER).val) + "s";
     191                }
     192                if (atts.containsKey(Att.VALMXR)) {
     193                    str += df.format(atts.get(Att.VALMXR).val) + "M";
     194                }
     195                if (!str.isEmpty()) {
     196                    Renderer.labelText(str, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.TR, AffineTransform.getTranslateInstance(-60, -30)));
     197                }
     198            }
     199        }
     200    }
     201
     202    public static void radarStations() {
     203        if (Renderer.zoom >= 11)
     204            Renderer.symbol(Beacons.RadarStation);
     205        if (Renderer.zoom >= 15) {
     206            String bstr = "";
     207            CatRTB cat = (CatRTB) getAttEnum(Obj.RTPBCN, Att.CATRTB);
     208            String wal = getAttStr(Obj.RTPBCN, Att.RADWAL);
     209            switch (cat) {
     210            case RTB_RAMK:
     211                bstr += " Ramark";
     212                break;
     213            case RTB_RACN:
     214                bstr += " Racon";
     215                String astr = getAttStr(Obj.RTPBCN, Att.SIGGRP);
     216                if (!astr.isEmpty()) {
     217                    bstr += "(" + astr + ")";
     218                }
     219                Double per = (Double) getAttVal(Obj.RTPBCN, Att.SIGPER);
     220                Double mxr = (Double) getAttVal(Obj.RTPBCN, Att.VALMXR);
     221                if ((per != null) || (mxr != null)) {
     222                    bstr += (astr.isEmpty() ? " " : "");
     223                    if (per != null)
     224                        bstr += (per != 0) ? per.toString() + "s" : "";
     225                    if (mxr != null)
     226                        bstr += (mxr != 0) ? mxr.toString() + "M" : "";
     227                }
     228                break;
     229            default:
     230                break;
     231            }
     232            if (!wal.isEmpty()) {
     233                switch (wal) {
     234                case "0.03-X":
     235                    bstr += "(3cm)";
     236                    break;
     237                case "0.10-S":
     238                    bstr += "(10cm)";
     239                    break;
     240                }
     241            }
     242            if (!bstr.isEmpty()) {
     243                Renderer.labelText(bstr, new Font("Arial", Font.PLAIN, 40), Symbols.Msymb, new Delta(Handle.TR, AffineTransform.getTranslateInstance(-30, -70)));
     244            }
     245        }
     246    }
     247
     248    @SuppressWarnings("unchecked")
     249    public static void radioStations() {
     250        boolean vais = false;
     251        String bstr = "";
     252        if (Renderer.zoom >= 11) {
     253            ArrayList<CatROS> cats = (ArrayList<CatROS>) getAttList(Obj.RDOSTA, Att.CATROS);
     254            for (CatROS ros : cats) {
     255                switch (ros) {
     256                case ROS_OMNI:
     257                    bstr += " RC";
     258                    break;
     259                case ROS_DIRL:
     260                    bstr += " RD";
     261                    break;
     262                case ROS_ROTP:
     263                    bstr += " RW";
     264                    break;
     265                case ROS_CNSL:
     266                    bstr += " Consol";
     267                    break;
     268                case ROS_RDF:
     269                    bstr += " RG";
     270                    break;
     271                case ROS_QTA:
     272                    bstr += " R";
     273                    break;
     274                case ROS_AERO:
     275                    bstr += " AeroRC";
     276                    break;
     277                case ROS_DECA:
     278                    bstr += " Decca";
     279                    break;
     280                case ROS_LORN:
     281                    bstr += " Loran";
     282                    break;
     283                case ROS_DGPS:
     284                    bstr += " DGPS";
     285                    break;
     286                case ROS_TORN:
     287                    bstr += " Toran";
     288                    break;
     289                case ROS_OMGA:
     290                    bstr += " Omega";
     291                    break;
     292                case ROS_SYLD:
     293                    bstr += " Syledis";
     294                    break;
     295                case ROS_CHKA:
     296                    bstr += " Chiaka";
     297                    break;
     298                case ROS_PCOM:
     299                case ROS_COMB:
     300                case ROS_FACS:
     301                case ROS_TIME:
     302                    break;
     303                case ROS_PAIS:
     304                case ROS_SAIS:
     305                    bstr += " AIS";
     306                    break;
     307                case ROS_VAIS:
     308                    vais = true;
     309                    break;
     310                case ROS_VANC:
     311                    vais = true;
     312                    Renderer.symbol(Topmarks.TopNorth, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
     313                    break;
     314                case ROS_VASC:
     315                    vais = true;
     316                    Renderer.symbol(Topmarks.TopSouth, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
     317                    break;
     318                case ROS_VAEC:
     319                    vais = true;
     320                    Renderer.symbol(Topmarks.TopEast, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
     321                    break;
     322                case ROS_VAWC:
     323                    vais = true;
     324                    Renderer.symbol(Topmarks.TopWest, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
     325                    break;
     326                case ROS_VAPL:
     327                    vais = true;
     328                    Renderer.symbol(Topmarks.TopCan, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
     329                    break;
     330                case ROS_VASL:
     331                    vais = true;
     332                    Renderer.symbol(Topmarks.TopCone, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
     333                    break;
     334                case ROS_VAID:
     335                    vais = true;
     336                    Renderer.symbol(Topmarks.TopIsol, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
     337                    break;
     338                case ROS_VASW:
     339                    vais = true;
     340                    Renderer.symbol(Topmarks.TopSphere, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
     341                    break;
     342                case ROS_VASP:
     343                    vais = true;
     344                    Renderer.symbol(Topmarks.TopX, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
     345                    break;
     346                case ROS_VAWK:
     347                    vais = true;
     348                    Renderer.symbol(Topmarks.TopCross, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
     349                    break;
     350                default:
     351                    break;
     352                }
     353            }
     354            if (!vais) {
     355                Renderer.symbol(Beacons.RadarStation);
     356            }
     357        }
     358        if (Renderer.zoom >= 15) {
     359            if (vais) {
     360                Renderer.labelText("V-AIS", new Font("Arial", Font.PLAIN, 40), Symbols.Msymb, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, 70)));
     361            }
     362            if (!bstr.isEmpty()) {
     363                Renderer.labelText(bstr, new Font("Arial", Font.PLAIN, 40), Symbols.Msymb, new Delta(Handle.TR, AffineTransform.getTranslateInstance(-30, -110)));
     364            }
     365        }
     366    }
     367
     368    class Sect {
     369        int dir;
     370        LitCHR chr;
     371        ColCOL col;
     372        ColCOL alt;
     373        String grp;
     374        double per;
     375        double rng;
     376    }
     377
     378    @SuppressWarnings("unchecked")
     379    public static void lights() {
     380        Enum<ColCOL> col = null;
     381        Enum<ColCOL> tcol = null;
     382        ObjTab lights = feature.objs.get(Obj.LIGHTS);
     383        for (AttMap atts : lights.values()) {
     384            if (atts.containsKey(Att.COLOUR)) {
     385                ArrayList<Enum<ColCOL>> cols = (ArrayList<Enum<ColCOL>>) atts.get(Att.COLOUR).val;
     386                if (cols.size() == 1) {
     387                    tcol = cols.get(0);
     388                    if (col == null) {
     389                        col = tcol;
     390                    } else if (tcol != col) {
     391                        col = ColCOL.COL_MAG;
     392                        break;
     393                    }
     394                } else {
     395                    col = ColCOL.COL_MAG;
     396                    break;
     397                }
     398            }
     399        }
     400        Renderer.symbol(Beacons.LightFlare, new Scheme(LightColours.get(col)), new Delta(Handle.BC, AffineTransform.getRotateInstance(Math.toRadians(120))));
     401        if (Renderer.zoom >= 12) {
     402            String str = "";
     403            if (lights.get(1) != null) {
     404                for (AttMap atts : lights.values()) {
     405                    Enum<ColCOL> col1 = null;
     406                    Enum<ColCOL> col2 = null;
     407                    double radius = 0.2;
     408                    double s1 = 361;
     409                    double s2 = 361;
     410                    Double dir = null;
     411                    if (atts.containsKey(Att.COLOUR)) {
     412                        ArrayList<Enum<ColCOL>> cols = (ArrayList<Enum<ColCOL>>) atts.get(Att.COLOUR).val;
     413                        col1 = cols.get(0);
     414                        if (cols.size() > 1)
     415                            col2 = cols.get(1);
     416                    } else {
     417                        continue;
     418                    }
     419                    if (atts.containsKey(Att.LITRAD)) {
     420                        radius = (Double) atts.get(Att.LITRAD).val;
     421                    }
     422                    if (atts.containsKey(Att.CATLIT)) {
     423                        ArrayList<CatLIT> cats = (ArrayList<CatLIT>) atts.get(Att.CATLIT).val;
     424                        if (cats.contains(CatLIT.LIT_DIR)) {
     425                            if (atts.containsKey(Att.ORIENT)) {
     426                                dir = (Double) atts.get(Att.ORIENT).val;
     427                                s1 = ((dir - 4) + 360) % 360;
     428                                s2 = (dir + 4) % 360;
     429                                for (AttMap satts : lights.values()) {
     430                                    double srad = 0.2;
     431                                    double ss1 = 361;
     432                                    double ss2 = 361;
     433                                    Double sdir = null;
     434                                    if (satts == atts)
     435                                        continue;
     436                                    if (satts.containsKey(Att.LITRAD)) {
     437                                        srad = (Double) satts.get(Att.LITRAD).val;
     438                                    }
     439                                    if (srad == radius) {
     440                                        ArrayList<CatLIT> scats = (satts.containsKey(Att.CATLIT)) ? (ArrayList<CatLIT>) satts.get(Att.CATLIT).val : new ArrayList<CatLIT>();
     441                                        if (scats.contains(CatLIT.LIT_DIR)) {
     442                                            if (satts.containsKey(Att.ORIENT)) {
     443                                                sdir = (Double) satts.get(Att.ORIENT).val;
     444                                                ss1 = sdir;
     445                                                ss2 = sdir;
     446                                            }
     447                                        } else {
     448                                            if (satts.containsKey(Att.SECTR1)) {
     449                                                ss1 = (Double) satts.get(Att.SECTR1).val;
     450                                            }
     451                                            if (satts.containsKey(Att.SECTR2)) {
     452                                                ss2 = (Double) satts.get(Att.SECTR2).val;
     453                                            }
     454                                        }
     455                                        if ((ss1 > 360) || (ss2 > 360))
     456                                            continue;
     457                                        if (sdir != null) {
     458                                            if (((dir - sdir + 360) % 360) < 8) {
     459                                                s1 = ((((sdir > dir) ? 360 : 0) + sdir + dir) / 2) % 360;
     460                                            }
     461                                            if (((sdir - dir + 360) % 360) < 8) {
     462                                                s2 = ((((dir > sdir) ? 360 : 0) + sdir + dir) / 2) % 360;
     463                                            }
     464                                        } else {
     465                                            if (((dir - ss2 + 360) % 360) < 4) {
     466                                                s1 = ss2;
     467                                            }
     468                                            if (((ss1 - dir + 360) % 360) < 4) {
     469                                                s2 = ss1;
     470                                            }
     471                                        }
     472                                    }
     473                                }
     474                            }
     475                        }
     476                    }
     477                    if ((s1 > 360) && atts.containsKey(Att.SECTR1)) {
     478                        s1 = (Double) atts.get(Att.SECTR1).val;
     479                    } else if (dir == null) {
     480                        continue;
     481                    }
     482                    if ((s2 > 360) && atts.containsKey(Att.SECTR2)) {
     483                        s2 = (Double) atts.get(Att.SECTR2).val;
     484                    } else if (dir == null) {
     485                        continue;
     486                    }
     487                    str = "";
     488                    if (atts.containsKey(Att.LITCHR)) {
     489                        str += LightCharacters.get(((ArrayList<LitCHR>) atts.get(Att.LITCHR).val).get(0));
     490                    }
     491                    if (atts.containsKey(Att.SIGGRP)) {
     492                        str += "(" + atts.get(Att.SIGGRP).val + ")";
     493                    } else if (!str.isEmpty()) {
     494                        str += ".";
     495                    }
     496                    if (atts.containsKey(Att.COLOUR)) {
     497                        ArrayList<Enum<ColCOL>> cols = (ArrayList<Enum<ColCOL>>) atts.get(Att.COLOUR).val;
     498                        str += LightLetters.get(cols.get(0));
     499                        if (cols.size() > 1)
     500                            str += LightLetters.get(cols.get(1));
     501                    }
     502                    if (atts.containsKey(Att.SIGPER)) {
     503                        str += "." + df.format(atts.get(Att.SIGPER).val) + "s";
     504                    }
     505                    if ((s1 <= 360) && (s2 <= 360) && (s1 != s2))
     506                        Renderer.lightSector(LightColours.get(col1), LightColours.get(col2), radius, s1, s2, dir, (Renderer.zoom >= 15) ? str : "");
     507                }
     508                if (Renderer.zoom >= 15) {
     509                    class LitSect {
     510                        boolean dir;
     511                        LitCHR chr;
     512                        ColCOL col;
     513                        String grp;
     514                        double per;
     515                        double rng;
     516                        double hgt;
     517                    }
     518
     519                    ArrayList<LitSect> litatts = new ArrayList<>();
     520                    for (AttMap atts : lights.values()) {
     521                        LitSect sect = new LitSect();
     522                        sect.dir = (atts.containsKey(Att.CATLIT) && ((ArrayList<CatLIT>) atts.get(Att.CATLIT).val).contains(CatLIT.LIT_DIR));
     523                        sect.chr = atts.containsKey(Att.LITCHR) ? ((ArrayList<LitCHR>) atts.get(Att.LITCHR).val).get(0) : LitCHR.CHR_UNKN;
     524                        switch (sect.chr) {
     525                        case CHR_AL:
     526                            sect.chr = LitCHR.CHR_F;
     527                            break;
     528                        case CHR_ALOC:
     529                            sect.chr = LitCHR.CHR_OC;
     530                            break;
     531                        case CHR_ALLFL:
     532                            sect.chr = LitCHR.CHR_LFL;
     533                            break;
     534                        case CHR_ALFL:
     535                            sect.chr = LitCHR.CHR_FL;
     536                            break;
     537                        case CHR_ALFFL:
     538                            sect.chr = LitCHR.CHR_FFL;
     539                            break;
     540                        default:
     541                            break;
     542                        }
     543                        sect.grp = atts.containsKey(Att.SIGGRP) ? (String) atts.get(Att.SIGGRP).val : "";
     544                        sect.per = atts.containsKey(Att.SIGPER) ? (Double) atts.get(Att.SIGPER).val : 0.0;
     545                        sect.rng = atts.containsKey(Att.VALNMR) ? (Double) atts.get(Att.VALNMR).val : 0.0;
     546                        sect.hgt = atts.containsKey(Att.HEIGHT) ? (Double) atts.get(Att.HEIGHT).val : 0.0;
     547                        ArrayList<ColCOL> cols = (ArrayList<ColCOL>) (atts.containsKey(Att.COLOUR) ? atts.get(Att.COLOUR).val : new ArrayList<>());
     548                        sect.col = cols.size() > 0 ? cols.get(0) : ColCOL.COL_UNK;
     549                        if ((sect.chr != LitCHR.CHR_UNKN) && (sect.col != null))
     550                            litatts.add(sect);
     551                    }
     552                    ArrayList<ArrayList<LitSect>> groupings = new ArrayList<>();
     553                    for (LitSect lit : litatts) {
     554                        boolean found = false;
     555                        for (ArrayList<LitSect> group : groupings) {
     556                            LitSect mem = group.get(0);
     557                            if ((lit.dir == mem.dir) && (lit.chr == mem.chr) && (lit.grp.equals(mem.grp)) && (lit.per == mem.per) && (lit.hgt == mem.hgt)) {
     558                                group.add(lit);
     559                                found = true;
     560                            }
     561                        }
     562                        if (!found) {
     563                            ArrayList<LitSect> tmp = new ArrayList<>();
     564                            tmp.add(lit);
     565                            groupings.add(tmp);
     566                        }
     567                    }
     568                    for (boolean moved = true; moved;) {
     569                        moved = false;
     570                        for (int i = 0; i < groupings.size() - 1; i++) {
     571                            if (groupings.get(i).size() < groupings.get(i + 1).size()) {
     572                                ArrayList<LitSect> tmp = groupings.remove(i);
     573                                groupings.add(i + 1, tmp);
     574                                moved = true;
     575                            }
     576                        }
     577                    }
     578                    class ColRng {
     579                        ColCOL col;
     580                        double rng;
     581
     582                        ColRng(ColCOL c, double r) {
     583                            col = c;
     584                            rng = r;
     585                        }
     586                    }
     587
     588                    int y = -30;
     589                    for (ArrayList<LitSect> group : groupings) {
     590                        ArrayList<ColRng> colrng = new ArrayList<>();
     591                        for (LitSect lit : group) {
     592                            boolean found = false;
     593                            for (ColRng cr : colrng) {
     594                                if (cr.col == lit.col) {
     595                                    if (lit.rng > cr.rng) {
     596                                        cr.rng = lit.rng;
     597                                    }
     598                                    found = true;
     599                                }
     600                            }
     601                            if (!found) {
     602                                colrng.add(new ColRng(lit.col, lit.rng));
     603                            }
     604                        }
     605                        for (boolean moved = true; moved;) {
     606                            moved = false;
     607                            for (int i = 0; i < colrng.size() - 1; i++) {
     608                                if (colrng.get(i).rng < colrng.get(i + 1).rng) {
     609                                    ColRng tmp = colrng.remove(i);
     610                                    colrng.add(i + 1, tmp);
     611                                    moved = true;
     612                                }
     613                            }
     614                        }
     615                        LitSect tmp = group.get(0);
     616                        str = (tmp.dir) ? "Dir" : "";
     617                        str += LightCharacters.get(tmp.chr);
     618                        if (!tmp.grp.isEmpty())
     619                            str += "(" + tmp.grp + ")";
     620                        else
     621                            str += ".";
     622                        for (ColRng cr : colrng) {
     623                            str += LightLetters.get(cr.col);
     624                        }
     625                        if ((tmp.per > 0) || (tmp.hgt > 0) || (colrng.get(0).rng > 0))
     626                            str += ".";
     627                        if (tmp.per > 0)
     628                            str += df.format(tmp.per) + "s";
     629                        if (tmp.hgt > 0)
     630                            str += df.format(tmp.hgt) + "m";
     631                        if (colrng.get(0).rng > 0)
     632                            str += df.format(colrng.get(0).rng) + ((colrng.size() > 1) ? ((colrng.size() > 2) ? ("-" + df.format(colrng.get(colrng.size() - 1).rng)) : ("/" + df.format(colrng.get(1).rng))) : "") + "M";
     633                        Renderer.labelText(str, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.TL, AffineTransform.getTranslateInstance(60, y)));
     634                        y += 40;
     635                        str = "";
     636                    }
     637                }
     638            } else {
     639                if (Renderer.zoom >= 15) {
     640                    AttMap atts = lights.get(0);
     641                    ArrayList<CatLIT> cats = new ArrayList<>();
     642                    if (atts.containsKey(Att.CATLIT)) {
     643                        cats = (ArrayList<CatLIT>) atts.get(Att.CATLIT).val;
     644                    }
     645                    str = (cats.contains(CatLIT.LIT_DIR)) ? "Dir" : "";
     646                    str += (atts.containsKey(Att.MLTYLT)) ? atts.get(Att.MLTYLT).val : "";
     647                    if (atts.containsKey(Att.LITCHR)) {
     648                        LitCHR chr = ((ArrayList<LitCHR>) atts.get(Att.LITCHR).val).get(0);
     649                        if (atts.containsKey(Att.SIGGRP)) {
     650                            String grp = (String) atts.get(Att.SIGGRP).val;
     651                            switch (chr) {
     652                            case CHR_QLFL:
     653                                str += String.format("Q(%s)+LFl", grp);
     654                                break;
     655                            case CHR_VQLFL:
     656                                str += String.format("VQ(%s)+LFl", grp);
     657                                break;
     658                            case CHR_UQLFL:
     659                                str += String.format("UQ(%s)+LFl", grp);
     660                                break;
     661                            default:
     662                                str += String.format("%s(%s)", LightCharacters.get(chr), grp);
     663                                break;
     664                            }
     665                        } else {
     666                            str += LightCharacters.get(chr);
     667                        }
     668                    }
     669                    if (atts.containsKey(Att.COLOUR)) {
     670                        ArrayList<ColCOL> cols = (ArrayList<ColCOL>) atts.get(Att.COLOUR).val;
     671                        if (!((cols.size() == 1) && (cols.get(0) == ColCOL.COL_WHT))) {
     672                            if (!str.isEmpty() && !str.endsWith(")")) {
     673                                str += ".";
     674                            }
     675                            for (ColCOL acol : cols) {
     676                                str += LightLetters.get(acol);
     677                            }
     678                        }
     679                    }
     680                    str += (cats.contains(CatLIT.LIT_VERT)) ? "(vert)" : "";
     681                    str += (cats.contains(CatLIT.LIT_HORI)) ? "(hor)" : "";
     682                    str += (!str.isEmpty() && (atts.containsKey(Att.SIGPER) || atts.containsKey(Att.HEIGHT) || atts.containsKey(Att.VALMXR)) && !str.endsWith(")")) ? "." : "";
     683                    str += (atts.containsKey(Att.SIGPER)) ? df.format(atts.get(Att.SIGPER).val) + "s" : "";
     684                    str += (atts.containsKey(Att.HEIGHT)) ? df.format(atts.get(Att.HEIGHT).val) + "m" : "";
     685                    str += (atts.containsKey(Att.VALNMR)) ? df.format(atts.get(Att.VALNMR).val) + "M" : "";
     686                    str += (cats.contains(CatLIT.LIT_FRNT)) ? "(Front)" : "";
     687                    str += (cats.contains(CatLIT.LIT_REAR)) ? "(Rear)" : "";
     688                    str += (cats.contains(CatLIT.LIT_UPPR)) ? "(Upper)" : "";
     689                    str += (cats.contains(CatLIT.LIT_LOWR)) ? "(Lower)" : "";
     690                    Renderer.labelText(str, new Font("Arial", Font.PLAIN, 40), Color.black, new Delta(Handle.TL, AffineTransform.getTranslateInstance(60, -30)));
     691                }
     692            }
     693        }
     694    }
    686695}
Note: See TracChangeset for help on using the changeset viewer.