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

checkstyle

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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}
Note: See TracChangeset for help on using the changeset viewer.