001package armyc2.c5isr.JavaTacticalRenderer;
002
003import static armyc2.c5isr.JavaTacticalRenderer.clsUtility.GetLinetypeFromString;
004
005import android.graphics.Bitmap;
006
007import java.util.ArrayList;
008import java.util.HashMap;
009import java.util.Map;
010
011import armyc2.c5isr.JavaLineArray.Channels;
012import armyc2.c5isr.JavaLineArray.POINT2;
013import armyc2.c5isr.JavaLineArray.Shape2;
014import armyc2.c5isr.JavaLineArray.TacticalLines;
015import armyc2.c5isr.JavaLineArray.arraysupport;
016import armyc2.c5isr.JavaLineArray.lineutility;
017import armyc2.c5isr.graphics2d.AffineTransform;
018import armyc2.c5isr.graphics2d.BasicStroke;
019import armyc2.c5isr.graphics2d.BufferedImage;
020import armyc2.c5isr.graphics2d.Font;
021import armyc2.c5isr.graphics2d.FontMetrics;
022import armyc2.c5isr.graphics2d.FontRenderContext;
023import armyc2.c5isr.graphics2d.Graphics2D;
024import armyc2.c5isr.graphics2d.Line2D;
025import armyc2.c5isr.graphics2d.PathIterator;
026import armyc2.c5isr.graphics2d.Point;
027import armyc2.c5isr.graphics2d.Point2D;
028import armyc2.c5isr.graphics2d.Polygon;
029import armyc2.c5isr.graphics2d.Rectangle2D;
030import armyc2.c5isr.graphics2d.Shape;
031import armyc2.c5isr.graphics2d.TextLayout;
032import armyc2.c5isr.renderer.SinglePointRenderer;
033import armyc2.c5isr.renderer.utilities.Color;
034import armyc2.c5isr.renderer.utilities.EntityCode;
035import armyc2.c5isr.renderer.utilities.ErrorLogger;
036import armyc2.c5isr.renderer.utilities.IPointConversion;
037import armyc2.c5isr.renderer.utilities.ImageInfo;
038import armyc2.c5isr.renderer.utilities.MilStdAttributes;
039import armyc2.c5isr.renderer.utilities.RendererException;
040import armyc2.c5isr.renderer.utilities.RendererSettings;
041import armyc2.c5isr.renderer.utilities.RendererUtilities;
042import armyc2.c5isr.renderer.utilities.ShapeInfo;
043import armyc2.c5isr.renderer.utilities.SymbolID;
044
045/**
046 * This class handles everything having to do with text for a
047 * tactical graphic. Note: labels are handled the same as text modifiers.
048 * 
049 */
050public class Modifier2 {
051    private POINT2[] textPath;
052    private String textID;
053    private String featureID;
054    private String text;
055
056    private Bitmap image;
057    private int iteration;
058    private int justify;
059    private int type;
060    private double lineFactor;
061    private static final String _className = "Modifier2";
062    @Deprecated
063    private boolean isIntegral = false;
064    private boolean fitsMBR = true;
065
066    Modifier2() {
067        textPath = new POINT2[2];
068    }
069
070    private static final int toEnd = 1; // Put next to pt0 on opposite side of line
071    private static final int aboveMiddle = 2;    //use both points
072    private static final int area = 3;   //use one point
073    private static final int screen = 4;   //use one point, screen, cover, guard points
074    private static final int aboveEnd = 5; // Put next to pt0 on line
075    private static final int aboveMiddlePerpendicular = 6; //use both points
076    private static final int aboveStartInside = 7; //place at the start inside the shape
077    private static final int aboveEndInside = 8;  //place at the end inside the shape
078    private static final int areaImage = 9;   //use one point
079    private static double fillAlphaCanObscureText = 50d;
080
081    private static boolean DoublesBack(POINT2 pt0, POINT2 pt1, POINT2 pt2) {
082        boolean result = true;
083        try {
084            double theta1 = Math.atan2(pt2.y - pt1.y, pt2.x - pt1.x);
085            double theta0 = Math.atan2(pt0.y - pt1.y, pt0.x - pt1.x);
086            double beta = Math.abs(theta0 - theta1);
087            if (beta > 0.1) {
088                result = false;
089            }
090
091        } catch (Exception exc) {
092            ErrorLogger.LogException(_className, "DoublesBack",
093                    new RendererException("Failed inside DoublesBack", exc));
094        }
095        return result;
096    }
097
098    /**
099     * Returns a generic label for the symbol per Mil-Std-2525
100     *
101     * @param tg
102     * @return
103     */
104    private static String GetCenterLabel(TGLight tg) {
105        String label = "";
106        try {
107            switch (tg.get_LineType()) {
108                case TacticalLines.SHIP_AOI_RECTANGULAR:
109                case TacticalLines.SHIP_AOI_CIRCULAR:
110                    label = "AOI";
111                    break;
112                case TacticalLines.DEFENDED_AREA_RECTANGULAR:
113                case TacticalLines.DEFENDED_AREA_CIRCULAR:
114                    label = "DA";
115                    break;
116                case TacticalLines.NOTACK:
117                    label = "N";
118                    break;
119                case TacticalLines.LAUNCH_AREA:
120                    label = "LA";
121                    break;
122                case TacticalLines.SL:
123                    label = "SL";
124                    break;
125                case TacticalLines.TC:
126                    label = "TC";
127                    break;
128                case TacticalLines.AARROZ:
129                    label = "AARROZ";
130                    break;
131                case TacticalLines.UAROZ:
132                    label = "UAROZ";
133                    break;
134                case TacticalLines.WEZ:
135                    label = "WEZ";
136                    break;
137                case TacticalLines.FEZ:
138                    label = "FEZ";
139                    break;
140                case TacticalLines.JEZ:
141                    label = "JEZ";
142                    break;
143                case TacticalLines.IFF_OFF:
144                    label = "IFF OFF";
145                    break;
146                case TacticalLines.IFF_ON:
147                    label = "IFF ON";
148                    break;
149                case TacticalLines.BCL_REVD:
150                case TacticalLines.BCL:
151                    label = "BCL";
152                    break;
153                case TacticalLines.ICL:
154                    label = "ICL";
155                    break;
156                case TacticalLines.FEBA:
157                    label = "FEBA";
158                    break;
159                case TacticalLines.BDZ:
160                    label = "BDZ";
161                    break;
162                case TacticalLines.JTAA:
163                    label = "JTAA";
164                    break;
165                case TacticalLines.SAA:
166                    label = "SAA";
167                    break;
168                case TacticalLines.SGAA:
169                    label = "SGAA";
170                    break;
171                case TacticalLines.ASSAULT:
172                    label = "ASLT";
173                    break;
174                case TacticalLines.SAAFR:
175                    label = "SAAFR";
176                    break;
177                case TacticalLines.AC:
178                    label = "AC";
179                    break;
180                case TacticalLines.SECURE:
181                case TacticalLines.SEIZE:
182                    label = "S";
183                    break;
184                case TacticalLines.RETAIN:
185                    label = "R";
186                    break;
187                case TacticalLines.PENETRATE:
188                    label = "P";
189                    break;
190                case TacticalLines.OCCUPY:
191                    label = "O";
192                    break;
193                case TacticalLines.ISOLATE:
194                    label = "I";
195                    break;
196                case TacticalLines.FIX:
197                    label = "F";
198                    break;
199                case TacticalLines.DISRUPT:
200                    label = "D";
201                    break;
202                case TacticalLines.CANALIZE:
203                case TacticalLines.CLEAR:
204                    label = "C";
205                    break;
206                case TacticalLines.BREACH:
207                case TacticalLines.BYPASS:
208                    label = "B";
209                    break;
210                case TacticalLines.CORDONKNOCK:
211                    label = "C/K";
212                    break;
213                case TacticalLines.CORDONSEARCH:
214                    label = "C/S";
215                    break;
216                case TacticalLines.UXO:
217                    label = "UXO";
218                    break;
219                case TacticalLines.RETIRE:
220                    label = "R";
221                    break;
222                case TacticalLines.FPOL:
223                    label = "P(F)";
224                    break;
225                case TacticalLines.RPOL:
226                    label = "P(R)";
227                    break;
228                case TacticalLines.BRDGHD:
229                case TacticalLines.BRDGHD_GE:
230                    if (SymbolID.getVersion(tg.get_SymbolId()) >= SymbolID.Version_2525E)
231                        label = "BL";
232                    else
233                        label = "B";
234                    break;
235                case TacticalLines.HOLD:
236                case TacticalLines.HOLD_GE:
237                    //label="HOLDING LINE";
238                    label = "HL";
239                    break;
240                case TacticalLines.PL:
241                    label = "PL";
242                    break;
243                case TacticalLines.LL:
244                    label = "LL";
245                    break;
246                case TacticalLines.EWL:
247                    label = "EWL";
248                    break;
249                case TacticalLines.SCREEN:
250                    label = "S";
251                    break;
252                case TacticalLines.COVER:
253                    label = "C";
254                    break;
255                case TacticalLines.GUARD:
256                    label = "G";
257                    break;
258                case TacticalLines.RIP:
259                    label = "RIP";
260                    break;
261                case TacticalLines.WITHDRAW:
262                    label = "W";
263                    break;
264                case TacticalLines.WDRAWUP:
265                    label = "WP";
266                    break;
267                case TacticalLines.CATK:
268                case TacticalLines.CATKBYFIRE:
269                    label = "CATK";
270                    break;
271                case TacticalLines.FLOT:
272                    label = "FLOT";
273                    break;
274                case TacticalLines.LC:
275                    label = "LC";
276                    break;
277                case TacticalLines.ASSY:
278                    label = "AA";
279                    break;
280                case TacticalLines.EA:
281                    label = "EA";
282                    break;
283                case TacticalLines.DZ:
284                    label = "DZ";
285                    break;
286                case TacticalLines.EZ:
287                    label = "EZ";
288                    break;
289                case TacticalLines.LZ:
290                    label = "LZ";
291                    break;
292                case TacticalLines.LAA:
293                    label = "LAA";
294                    break;
295                case TacticalLines.PZ:
296                    label = "PZ";
297                    break;
298                case TacticalLines.MRR:
299                    label = "MRR";
300                    break;
301                case TacticalLines.SC:
302                    label = "SC";
303                    break;
304                case TacticalLines.LLTR:
305                    label = "LLTR";
306                    break;
307                case TacticalLines.ROZ:
308                    label = "ROZ";
309                    break;
310                case TacticalLines.FAADZ:
311                    label = "SHORADEZ";
312                    break;
313                case TacticalLines.HIDACZ:
314                    label = "HIDACZ";
315                    break;
316                case TacticalLines.MEZ:
317                    label = "MEZ";
318                    break;
319                case TacticalLines.LOMEZ:
320                    label = "LOMEZ";
321                    break;
322                case TacticalLines.HIMEZ:
323                    label = "HIMEZ";
324                    break;
325                case TacticalLines.WFZ:
326                    label = "WFZ";
327                    break;
328                case TacticalLines.MINED:
329                case TacticalLines.FENCED:
330                    label = "M";
331                    break;
332                case TacticalLines.PNO:
333                    label = "(P)";
334                    break;
335                case TacticalLines.OBJ:
336                    label = "OBJ";
337                    break;
338                case TacticalLines.NAI:
339                    label = "NAI";
340                    break;
341                case TacticalLines.TAI:
342                    label = "TAI";
343                    break;
344                case TacticalLines.BASE_CAMP_REVD:
345                case TacticalLines.BASE_CAMP:
346                    label = "BC";
347                    break;
348                case TacticalLines.GUERILLA_BASE_REVD:
349                case TacticalLines.GUERILLA_BASE:
350                    label = "GB";
351                    break;
352                case TacticalLines.LINTGTS:
353                    label = "SMOKE";
354                    break;
355                case TacticalLines.FPF:
356                    label = "FPF";
357                    break;
358                case TacticalLines.ATKPOS:
359                    label = "ATK";
360                    break;
361                case TacticalLines.FCL:
362                    label = "FCL";
363                    break;
364                case TacticalLines.LOA:
365                    label = "LOA";
366                    break;
367                case TacticalLines.LOD:
368                    label = "LD";
369                    break;
370                case TacticalLines.PLD:
371                    label = "PLD";
372                    break;
373                case TacticalLines.DELAY:
374                    label = "D";
375                    break;
376                case TacticalLines.RELEASE:
377                    label = "RL";
378                    break;
379                case TacticalLines.HOL:
380                    label = "HOL";
381                    break;
382                case TacticalLines.BHL:
383                    label = "BHL";
384                    break;
385                case TacticalLines.SMOKE:
386                    label = "SMOKE";
387                    break;
388                case TacticalLines.NFL:
389                    label = "NFL";
390                    break;
391                case TacticalLines.MFP:
392                    label = "MFP";
393                    break;
394                case TacticalLines.FSCL:
395                    label = "FSCL";
396                    break;
397                case TacticalLines.CFL:
398                    label = "CFL";
399                    break;
400                case TacticalLines.RFL:
401                    label = "RFL";
402                    break;
403                case TacticalLines.AO:
404                    label = "AO";
405                    break;
406                case TacticalLines.BOMB:
407                    label = "BOMB";
408                    break;
409                case TacticalLines.TGMF:
410                    label = "TGMF";
411                    break;
412                case TacticalLines.FSA:
413                    label = "FSA";
414                    break;
415                case TacticalLines.FSA_CIRCULAR:
416                case TacticalLines.FSA_RECTANGULAR:
417                    label = "FSA";
418                    break;
419                case TacticalLines.ACA:
420                case TacticalLines.ACA_CIRCULAR:
421                case TacticalLines.ACA_RECTANGULAR:
422                    label = "ACA";
423                    break;
424                case TacticalLines.FFA:
425                case TacticalLines.FFA_CIRCULAR:
426                case TacticalLines.FFA_RECTANGULAR:
427                    label = "FFA";
428                    break;
429                case TacticalLines.NFA:
430                case TacticalLines.NFA_CIRCULAR:
431                case TacticalLines.NFA_RECTANGULAR:
432                    label = "NFA";
433                    break;
434                case TacticalLines.RFA:
435                case TacticalLines.RFA_CIRCULAR:
436                case TacticalLines.RFA_RECTANGULAR:
437                    label = "RFA";
438                    break;
439                case TacticalLines.ATI:
440                case TacticalLines.ATI_CIRCULAR:
441                case TacticalLines.ATI_RECTANGULAR:
442                    label = "ATI ZONE";
443                    break;
444                case TacticalLines.PAA:
445                case TacticalLines.PAA_CIRCULAR:
446                case TacticalLines.PAA_RECTANGULAR:
447                    label = "PAA";
448                    break;
449                case TacticalLines.CFFZ:
450                case TacticalLines.CFFZ_CIRCULAR:
451                case TacticalLines.CFFZ_RECTANGULAR:
452                    label = "CFF ZONE";
453                    break;
454                case TacticalLines.CFZ:
455                case TacticalLines.CFZ_CIRCULAR:
456                case TacticalLines.CFZ_RECTANGULAR:
457                    label = "CF ZONE";
458                    break;
459                case TacticalLines.SENSOR:
460                case TacticalLines.SENSOR_CIRCULAR:
461                case TacticalLines.SENSOR_RECTANGULAR:
462                    label = "SENSOR ZONE";
463                    break;
464                case TacticalLines.CENSOR:
465                case TacticalLines.CENSOR_CIRCULAR:
466                case TacticalLines.CENSOR_RECTANGULAR:
467                    label = "CENSOR ZONE";
468                    break;
469                case TacticalLines.DA:
470                case TacticalLines.DA_CIRCULAR:
471                case TacticalLines.DA_RECTANGULAR:
472                    label = "DA";
473                    break;
474                case TacticalLines.ZOR:
475                case TacticalLines.ZOR_CIRCULAR:
476                case TacticalLines.ZOR_RECTANGULAR:
477                    label = "ZOR";
478                    break;
479                case TacticalLines.TBA:
480                case TacticalLines.TBA_CIRCULAR:
481                case TacticalLines.TBA_RECTANGULAR:
482                    label = "TBA";
483                    break;
484                case TacticalLines.TVAR:
485                case TacticalLines.TVAR_CIRCULAR:
486                case TacticalLines.TVAR_RECTANGULAR:
487                    label = "TVAR";
488                    break;
489                case TacticalLines.KILLBOXBLUE:
490                case TacticalLines.KILLBOXBLUE_CIRCULAR:
491                case TacticalLines.KILLBOXBLUE_RECTANGULAR:
492                    label = "BKB";
493                    break;
494                case TacticalLines.KILLBOXPURPLE:
495                case TacticalLines.KILLBOXPURPLE_CIRCULAR:
496                case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
497                    label = "PKB";
498                    break;
499                case TacticalLines.MSR:
500                case TacticalLines.MSR_ONEWAY:
501                case TacticalLines.MSR_TWOWAY:
502                case TacticalLines.MSR_ALT:
503                    label = "MSR";
504                    break;
505                case TacticalLines.ASR:
506                case TacticalLines.ASR_ONEWAY:
507                case TacticalLines.ASR_TWOWAY:
508                case TacticalLines.ASR_ALT:
509                    label = "ASR";
510                    break;
511                case TacticalLines.ROUTE:
512                case TacticalLines.ROUTE_ONEWAY:
513                case TacticalLines.ROUTE_ALT:
514                    label = "ROUTE";
515                    break;
516                case TacticalLines.LDLC:
517                    label = "LD/LC";
518                    break;
519                case TacticalLines.AIRHEAD:
520                    label = "AIRHEAD LINE";
521                    break;
522                case TacticalLines.BLOCK:
523                case TacticalLines.BEARING:
524                    label = "B";
525                    break;
526                case TacticalLines.BEARING_J:
527                    label = "J";
528                    break;
529                case TacticalLines.BEARING_RDF:
530                    label = "RDF";
531                    break;
532                case TacticalLines.ELECTRO:
533                    label = "E";
534                    break;
535                case TacticalLines.BEARING_EW:
536                    label = "EW";
537                    break;
538                case TacticalLines.ACOUSTIC:
539                case TacticalLines.ACOUSTIC_AMB:
540                    label = "A";
541                    break;
542                case TacticalLines.TORPEDO:
543                    label = "T";
544                    break;
545                case TacticalLines.OPTICAL:
546                    label = "O";
547                    break;
548                case TacticalLines.DHA:
549                    label = "DHA";
550                    break;
551                case TacticalLines.FARP:
552                    label = "FARP";
553                    break;
554                case TacticalLines.BSA:
555                    label = "BSA";
556                    break;
557                case TacticalLines.DSA:
558                    label = "DSA";
559                    break;
560                case TacticalLines.CSA:
561                    label = "CSA";
562                    break;
563                case TacticalLines.RSA:
564                    label = "RSA";
565                    break;
566                case TacticalLines.CONTAIN:
567                    label = "C";
568                    break;
569                case TacticalLines.OBSFAREA:
570                    label = "FREE";
571                    break;
572                default:
573                    break;
574            }
575        } catch (Exception exc) {
576            //clsUtility.WriteFile("Error in Modifier2.GetCenterLabel");
577            ErrorLogger.LogException(_className, "GetCenterLabel",
578                    new RendererException("Failed inside GetCenterLabel", exc));
579        }
580        return label;
581    }
582    //non CPOF clients using best fit need these accessors
583
584    public POINT2[] get_TextPath() {
585        return textPath;
586    }
587
588    protected void set_TextPath(POINT2[] value) {
589        textPath = value;
590    }
591
592    @Deprecated
593    protected void set_IsIntegral(boolean value) {
594        isIntegral = value;
595    }
596
597    @Deprecated
598    protected boolean get_IsIntegral() {
599        return isIntegral;
600    }
601
602    private static void AddOffsetModifier(TGLight tg,
603            String text,
604            int type,
605            double lineFactor,
606            int startIndex,
607            int endIndex,
608            double spaces,
609            String rightOrLeft) {
610        if (rightOrLeft == null || tg.Pixels == null || tg.Pixels.size() < 2 || endIndex >= tg.Pixels.size()) {
611            return;
612        }
613
614        POINT2 pt0 = tg.Pixels.get(startIndex);
615        POINT2 pt1 = tg.Pixels.get(endIndex);
616        if (rightOrLeft.equals("left")) {
617            pt0.x -= spaces;
618            pt1.x -= spaces;
619        } else {
620            pt0.x += spaces;
621            pt1.x += spaces;
622        }
623        AddModifier2(tg, text, type, lineFactor, pt0, pt1, false);
624    }
625
626    /**
627     *
628     * @param tg
629     * @param text
630     * @param type
631     * @param lineFactor
632     * @param ptStart
633     * @param ptEnd
634     */
635    private static void AddModifier(TGLight tg,
636            String text,
637            int type,
638            double lineFactor,
639            POINT2 ptStart,
640            POINT2 ptEnd) {
641        if (tg.Pixels == null || tg.Pixels.size() < 2) {
642            return;
643        }
644        AddModifier2(tg, text, type, lineFactor, ptStart, ptEnd, false);
645    }
646
647    private static void AddModifier2(TGLight tg,
648            String text,
649            int type,
650            double lineFactor,
651            POINT2 pt0,
652            POINT2 pt1,
653            boolean isIntegral) {
654        AddModifier2(tg, text, type, lineFactor, pt0, pt1, isIntegral, null);
655    }
656
657    private static void AddModifier2(TGLight tg,
658            String text,
659            int type,
660            double lineFactor,
661            POINT2 pt0,
662            POINT2 pt1,
663            boolean isIntegral,
664            String modifierType) {
665        try {
666            if (text == null || text.equals("")) {
667                return;
668            }
669
670            Modifier2 modifier = new Modifier2();
671            modifier.set_IsIntegral(isIntegral);
672            modifier.text = text;
673            modifier.type = type;
674            modifier.lineFactor = lineFactor;
675            modifier.textPath[0] = pt0;
676            modifier.textPath[1] = pt1;
677            modifier.textID = modifierType;
678            tg.modifiers.add(modifier);
679        } catch (Exception exc) {
680            ErrorLogger.LogException(_className, "AddModifier",
681                    new RendererException("Failed inside AddModifier", exc));
682        }
683    }
684
685    private static void AddIntegralModifier(TGLight tg,
686            String text,
687            int type,
688            double lineFactor,
689            int startIndex,
690            int endIndex) {
691        AddIntegralModifier(tg, text, type, lineFactor, startIndex, endIndex, true);
692    }
693
694    private static void AddIntegralModifier(TGLight tg,
695            String text,
696            int type,
697            double lineFactor,
698            int startIndex,
699            int endIndex,
700            Boolean isIntegral) {
701        AddIntegralModifier(tg, text, type, lineFactor, startIndex, endIndex, isIntegral, null);
702    }
703
704    private static void AddIntegralModifier(TGLight tg,
705            String text,
706            int type,
707            double lineFactor,
708            int startIndex,
709            int endIndex,
710            Boolean isIntegral,
711            String modifierType) {
712        if (tg.Pixels == null || tg.Pixels.isEmpty() || endIndex >= tg.Pixels.size()) {
713            return;
714        }
715        AddIntegralAreaModifier(tg, text, type, lineFactor, tg.Pixels.get(startIndex), tg.Pixels.get(endIndex), isIntegral, modifierType);
716    }
717
718    /**
719     * Creates and adds center modifiers for generic areas
720     *
721     * @param tg
722     * @param text
723     * @param type
724     * @param lineFactor
725     * @param pt0
726     * @param pt1
727     */
728    private static void AddAreaModifier(TGLight tg,
729            String text,
730            int type,
731            double lineFactor,
732            POINT2 pt0,
733            POINT2 pt1) {
734        AddIntegralAreaModifier(tg, text, type, lineFactor, pt0, pt1, true);
735    }
736
737    /**
738     * sets modifier.textId to the modifier type, e.g. label, T, T1, etc.
739     *
740     * @param tg
741     * @param text
742     * @param type
743     * @param lineFactor
744     * @param pt0
745     * @param pt1
746     * @param modifierType
747     */
748    private static void AddAreaModifier(TGLight tg,
749            String text,
750            int type,
751            double lineFactor,
752            POINT2 pt0,
753            POINT2 pt1,
754            String modifierType) {
755        AddIntegralAreaModifier(tg, text, type, lineFactor, pt0, pt1, true, modifierType);
756    }
757
758    private static void AddIntegralAreaModifier(TGLight tg,
759            String text,
760            int type,
761            double lineFactor,
762            POINT2 pt0,
763            POINT2 pt1,
764            Boolean isIntegral) {
765        AddIntegralAreaModifier(tg,text,type,lineFactor,pt0, pt1, isIntegral, null);
766    }
767
768    private static void AddIntegralAreaModifier(TGLight tg,
769            Bitmap image,
770            int type,
771            double lineFactor,
772            POINT2 pt0,
773            POINT2 pt1,
774            Boolean isIntegral) {
775        try {
776            if (image == null || image.equals("")) {
777                return;
778            }
779
780            Modifier2 modifier = new Modifier2();
781            modifier.set_IsIntegral(isIntegral);
782            modifier.image = image;
783            if (image == null || image.equals("")) {
784                return;
785            }
786
787            if (pt0 == null || pt1 == null) {
788                return;
789            }
790
791            modifier.type = type;
792            modifier.lineFactor = lineFactor;
793            modifier.textPath[0] = pt0;
794            modifier.textPath[1] = pt1;
795            tg.modifiers.add(modifier);
796        } catch (Exception exc) {
797            ErrorLogger.LogException(_className, "AddAreaModifier",
798                    new RendererException("Failed inside AddAreaModifier", exc));
799        }
800    }
801
802    private static void AddIntegralAreaModifier(TGLight tg,
803            String text,
804            int type,
805            double lineFactor,
806            POINT2 pt0,
807            POINT2 pt1,
808            Boolean isIntegral,
809            String modifierType) {
810        if (pt0 == null || pt1 == null) {
811            return;
812        }
813        AddModifier2(tg, text, type, lineFactor, pt0, pt1, isIntegral, modifierType);
814    }
815
816    /**
817     * Returns symbol MBR. Assumes points have been initialized with value of
818     * 0th point
819     *
820     * @param tg the tactical graphic object
821     * @param ptUl OUT - MBR upper left
822     * @param ptUr OUT - MBR upper right
823     * @param ptLr OUT - MBR lower right
824     * @param ptLl OUT - MBR lower left
825     */
826    public static void GetMBR(TGLight tg,
827            POINT2 ptUl,
828            POINT2 ptUr,
829            POINT2 ptLr,
830            POINT2 ptLl) {
831        try {
832            int j = 0;
833            double x = 0;
834            double y = 0;
835            ptUl.x = tg.Pixels.get(0).x;
836            ptUl.y = tg.Pixels.get(0).y;
837            ptUr.x = tg.Pixels.get(0).x;
838            ptUr.y = tg.Pixels.get(0).y;
839            ptLl.x = tg.Pixels.get(0).x;
840            ptLl.y = tg.Pixels.get(0).y;
841            ptLr.x = tg.Pixels.get(0).x;
842            ptLr.y = tg.Pixels.get(0).y;
843            int n = tg.Pixels.size();
844            //for (j = 1; j < tg.Pixels.size(); j++) 
845            for (j = 1; j < n; j++) {
846                x = tg.Pixels.get(j).x;
847                y = tg.Pixels.get(j).y;
848                if (x < ptLl.x) {
849                    ptLl.x = x;
850                    ptUl.x = x;
851                }
852                if (x > ptLr.x) {
853                    ptLr.x = x;
854                    ptUr.x = x;
855                }
856                if (y > ptLl.y) {
857                    ptLl.y = y;
858                    ptLr.y = y;
859                }
860                if (y < ptUl.y) {
861                    ptUl.y = y;
862                    ptUr.y = y;
863                }
864            }
865        } catch (Exception exc) {
866            ErrorLogger.LogException(_className, "GetMBR",
867                    new RendererException("Failed inside GetMBR", exc));
868        }
869    }
870
871    /**
872     * Tests segment of a Boundary
873     *
874     * @param tg
875     * @param g2d
876     * @param middleSegment
877     * @return
878     */
879    private static boolean GetBoundarySegmentTooShort(TGLight tg,
880            Graphics2D g2d,
881            int middleSegment) {
882        boolean lineTooShort = false;
883        try {
884            //int middleSegment = tg.Pixels.size() / 2 - 1;
885            g2d.setFont(tg.get_Font());
886            FontMetrics metrics = g2d.getFontMetrics();
887            String echelonSymbol = null;
888            int stringWidthEchelonSymbol = 0;
889
890            POINT2 pt0 = tg.Pixels.get(middleSegment);
891            POINT2 pt1 = tg.Pixels.get(middleSegment + 1);
892            double dist = lineutility.CalcDistanceDouble(pt0, pt1);
893
894            echelonSymbol = tg.get_EchelonSymbol();
895
896            if (echelonSymbol != null) {
897                stringWidthEchelonSymbol = metrics.stringWidth(echelonSymbol);
898            }
899
900            int tWidth = 0, t1Width = 0;
901            if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
902                tWidth = metrics.stringWidth(tg.get_Name());
903            }
904            if (tg.get_T1() != null && !tg.get_T1().isEmpty()) {
905                t1Width = metrics.stringWidth(tg.get_T1());
906            }
907
908            int totalWidth = stringWidthEchelonSymbol;
909            if (totalWidth < tWidth) {
910                totalWidth = tWidth;
911            }
912            if (totalWidth < t1Width) {
913                totalWidth = t1Width;
914            }
915
916            switch (tg.get_LineType()) {
917                case TacticalLines.BOUNDARY:
918                    if (dist < 1.25 * (totalWidth)) {
919                        lineTooShort = true;
920                    }
921                    break;
922                default:
923                    break;
924            }
925        } catch (Exception exc) {
926            ErrorLogger.LogException(_className, "GetBoundaryLineTooShort",
927                    new RendererException("Failed inside GetBoundaryLineTooShort", exc));
928        }
929        return lineTooShort;
930    }
931
932    /**
933     * Handles the line breaks for Boundary and Engineer Work Line
934     *
935     * @param tg
936     * @param g2d
937     */
938    private static void AddBoundaryModifiers(TGLight tg,
939            Graphics2D g2d,
940            Object clipBounds) {
941        try {
942            int j = 0;
943            double csFactor = 1d;
944            Boolean foundSegment = false;
945            POINT2 pt0 = null, pt1 = null, ptLast = null;
946            double TLineFactor = 0, T1LineFactor = 0;
947            Boolean lineTooShort = false;
948            String countryCode = "";
949            if(!tg.get_AS().equals("")) {
950                countryCode = " (" + tg.get_AS() + ")";
951            }
952            if (tg.get_Client().equals("cpof3d")) {
953                csFactor = 0.85d;
954            }
955
956            int middleSegment = getVisibleMiddleSegment(tg, clipBounds);
957            //for (j = 0; j < tg.Pixels.size() - 1; j++) {
958            for (j = middleSegment; j == middleSegment; j++) {
959                /* if (tg.get_Client().equalsIgnoreCase("ge")) {
960                    if (j != middleSegment) {
961                        continue;
962                    }
963                }*/
964
965                pt0 = tg.Pixels.get(j);
966                pt1 = tg.Pixels.get(j + 1);
967                if (pt0.x < pt1.x) {
968                    TLineFactor = -1.3;
969                    T1LineFactor = 1;
970                } else if (pt0.x == pt1.x) {
971                    if (pt1.y < pt0.y) {
972                        TLineFactor = -1;
973                        T1LineFactor = 1;
974                    } else {
975                        TLineFactor = 1;
976                        T1LineFactor = -1;
977                    }
978                } else {
979                    TLineFactor = 1;
980                    T1LineFactor = -1.3;
981                }
982                //is the segment too short?
983                lineTooShort = GetBoundarySegmentTooShort(tg, g2d, j);
984
985                if (lineTooShort == false) {
986                    foundSegment = true;
987                    AddIntegralModifier(tg, tg.get_Name() + countryCode, aboveMiddle, TLineFactor * csFactor, j, j + 1, true);
988                    //the echelon symbol
989                    if (tg.get_EchelonSymbol() != null && !tg.get_EchelonSymbol().equals("")) {
990                        AddIntegralModifier(tg, tg.get_EchelonSymbol(), aboveMiddle, -0.20 * csFactor, j, j + 1, true);
991                    }
992                    //the T1 modifier
993                    AddIntegralModifier(tg, tg.get_T1(), aboveMiddle, T1LineFactor * csFactor, j, j + 1, true);
994                }
995            }//end for loop
996            if (foundSegment == false) {
997                pt0 = new POINT2();
998                pt1 = new POINT2();
999                // Get boundary middle segment
1000                final String echelonSymbol = tg.get_EchelonSymbol();
1001                final FontMetrics metrics = g2d.getFontMetrics();
1002                double modDist = 0;
1003
1004                if (echelonSymbol != null) {
1005                    modDist = 1.5 * metrics.stringWidth(echelonSymbol);
1006                }
1007
1008                final double segDist = lineutility.CalcDistanceDouble(tg.Pixels.get(middleSegment), tg.Pixels.get(middleSegment + 1));
1009
1010                g2d.setFont(tg.get_Font());
1011                POINT2 midpt = lineutility.MidPointDouble(tg.Pixels.get(middleSegment), tg.Pixels.get(middleSegment + 1), 0);
1012                POINT2 ptTemp = null;
1013                if (segDist < modDist) {
1014                    ptTemp = lineutility.ExtendAlongLineDouble(midpt, tg.Pixels.get(middleSegment), modDist / 2);
1015                    pt0.x = ptTemp.x;
1016                    pt0.y = ptTemp.y;
1017                    ptTemp = lineutility.ExtendAlongLineDouble(midpt, tg.Pixels.get(middleSegment + 1), modDist / 2);
1018                } else {
1019                    ptTemp = tg.Pixels.get(middleSegment);
1020                    pt0.x = ptTemp.x;
1021                    pt0.y = ptTemp.y;
1022                    ptTemp = tg.Pixels.get(middleSegment + 1);
1023                }
1024                pt1.x = ptTemp.x;
1025                pt1.y = ptTemp.y;
1026
1027                AddIntegralModifier(tg, tg.get_Name() + countryCode, aboveMiddle, TLineFactor * csFactor, middleSegment, middleSegment + 1, true);
1028                //the echelon symbol
1029                if (echelonSymbol != null && !echelonSymbol.equals("")) {
1030                    AddIntegralModifier(tg, echelonSymbol, aboveMiddle, -0.2020 * csFactor, middleSegment, middleSegment + 1, true);
1031                }
1032                //the T1 modifier
1033                AddIntegralModifier(tg, tg.get_T1(), aboveMiddle, T1LineFactor * csFactor, middleSegment, middleSegment + 1, true);
1034            }//end if foundSegment==false
1035        } catch (Exception exc) {
1036            ErrorLogger.LogException(_className, "AddBoundaryModifiers",
1037                    new RendererException("Failed inside AddBoundaryModifiers", exc));
1038        }
1039    }
1040
1041    /**
1042     * added for USAS
1043     *
1044     * @param tg
1045     * @param metrics
1046     * @deprecated
1047     */
1048    private static void AddNameAboveDTG(TGLight tg, FontMetrics metrics) {
1049        try {
1050            double csFactor = 1;
1051            if (tg.get_Client().equals("cpof3d")) {
1052                csFactor = 0.667;
1053            }
1054            String label = GetCenterLabel(tg);
1055            POINT2 pt0 = new POINT2(tg.Pixels.get(0));
1056            POINT2 pt1 = new POINT2(tg.Pixels.get(1));
1057            int lastIndex = tg.Pixels.size() - 1;
1058            int nextToLastIndex = tg.Pixels.size() - 2;
1059            POINT2 ptLast = new POINT2(tg.Pixels.get(lastIndex));
1060            POINT2 ptNextToLast = new POINT2(tg.Pixels.get(nextToLastIndex));
1061            shiftModifierPath(tg, pt0, pt1, ptLast, ptNextToLast);
1062            double stringWidth = metrics.stringWidth(label + " " + tg.get_Name());
1063            AddIntegralAreaModifier(tg, label + " " + tg.get_Name(), toEnd, 0, pt0, pt1, false);
1064            pt1 = lineutility.ExtendAlongLineDouble(tg.Pixels.get(0), tg.Pixels.get(1), -1.5 * stringWidth);
1065            AddModifier2(tg, tg.get_DTG(), aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
1066            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
1067            AddIntegralAreaModifier(tg, label + " " + tg.get_Name(), toEnd, 0, ptLast, ptNextToLast, false);
1068            pt0 = tg.Pixels.get(lastIndex);
1069            pt1 = lineutility.ExtendAlongLineDouble(tg.Pixels.get(lastIndex), tg.Pixels.get(nextToLastIndex), -1.5 * stringWidth);
1070            AddModifier2(tg, tg.get_DTG(), aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
1071            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
1072        } catch (Exception exc) {
1073            ErrorLogger.LogException(_className, "AddNameAboveDTG",
1074                    new RendererException("Failed inside AddNameAboveDTG", exc));
1075        }
1076    }
1077
1078    /**
1079     * shifts the path for modifiers that use toEnd to prevent vertical paths
1080     *
1081     * @param tg
1082     * @param pt0
1083     * @param pt1
1084     * @param ptLast
1085     * @param ptNextToLast
1086     */
1087    private static void shiftModifierPath(TGLight tg,
1088            POINT2 pt0,
1089            POINT2 pt1,
1090            POINT2 ptLast,
1091            POINT2 ptNextToLast) {
1092        try {
1093            POINT2 p0 = null, p1 = null;
1094            double last = -1.0;
1095            switch (tg.get_LineType()) {
1096                case TacticalLines.BOUNDARY:
1097                    for (int j = 0; j < tg.Pixels.size() - 1; j++) {
1098                        p0 = tg.Pixels.get(j);
1099                        p1 = tg.Pixels.get(j + 1);
1100                        //if(p0.x==p1.x)
1101                        if (Math.abs(p0.x - p1.x) < 1) {
1102                            p1.x += last;
1103                            last = -last;
1104                        }
1105                    }
1106                    break;
1107                case TacticalLines.PDF:
1108                case TacticalLines.PL:
1109                case TacticalLines.FEBA:
1110                case TacticalLines.LOA:
1111                case TacticalLines.LOD:
1112                case TacticalLines.RELEASE:
1113                case TacticalLines.HOL:
1114                case TacticalLines.BHL:
1115                case TacticalLines.LDLC:
1116                case TacticalLines.LL:
1117                case TacticalLines.EWL:
1118                case TacticalLines.FCL:
1119                case TacticalLines.PLD:
1120                case TacticalLines.NFL:
1121                case TacticalLines.FLOT:
1122                case TacticalLines.LC:
1123                case TacticalLines.HOLD:
1124                case TacticalLines.BRDGHD:
1125                case TacticalLines.HOLD_GE:
1126                case TacticalLines.BRDGHD_GE:
1127                    //if (pt0 != null && pt1 != null && pt0.x == pt1.x) 
1128                    if (pt0 != null && pt1 != null && Math.abs(pt0.x - pt1.x) < 1) {
1129                        pt1.x += 1;
1130                    }
1131                    //if (ptLast != null && ptNextToLast != null && ptNextToLast.x == ptLast.x) 
1132                    if (ptLast != null && ptNextToLast != null && Math.abs(ptNextToLast.x - ptLast.x) < 1) {
1133                        ptNextToLast.x += 1;
1134                    }
1135                    break;
1136                default:
1137                    return;
1138            }
1139        } catch (Exception exc) {
1140            ErrorLogger.LogException(_className, "shiftModifierPath",
1141                    new RendererException("Failed inside shiftModifierPath", exc));
1142        }
1143    }
1144
1145    /**
1146     * Adds label on line
1147     *
1148     * Replaces areasWithENY()
1149     *
1150     * @param label
1151     * @param tg
1152     * @param g2d
1153     * @param twoLabelOnly - true if only add two instances of label to line (used with N modifier)
1154     *                     Ignored if RendererSettings.TwoLabelOnly is true
1155     */
1156    private static void addModifierOnLine(String label, TGLight tg, Graphics2D g2d, boolean twoLabelOnly) {
1157        if (label == null || label.isEmpty()) {
1158            return;
1159        }
1160        try {
1161            if (!RendererSettings.getInstance().getTwoLabelOnly() && !twoLabelOnly) {
1162                FontMetrics metrics = g2d.getFontMetrics();
1163                final int stringWidth = metrics.stringWidth(label);
1164                boolean foundLongSegment = false;
1165
1166                for (int j = 0; j < tg.Pixels.size() - 1; j++) {
1167                    POINT2 pt0 = tg.Pixels.get(j);
1168                    POINT2 pt1 = tg.Pixels.get(j + 1);
1169                    double dist = lineutility.CalcDistanceDouble(pt0, pt1);
1170                    if (dist > 1.5 * stringWidth) {
1171                        foundLongSegment = true;
1172                        AddIntegralAreaModifier(tg, label, aboveMiddle, 0, pt0, pt1, false);
1173                    }
1174                }
1175                if (!foundLongSegment) {
1176                    // did not find a long enough segment
1177                    int middleSegment = tg.Pixels.size() / 2 - 1;
1178                    int middleSegment2 = tg.Pixels.size() - 2;
1179                    if (tg.Pixels.size() > 3) {
1180                        middleSegment = tg.Pixels.size() / 4;
1181                        middleSegment2 = 3 * tg.Pixels.size() / 4;
1182                    }
1183                    if (middleSegment != 0) {
1184                        AddIntegralModifier(tg, label, aboveMiddle, 0, middleSegment, middleSegment + 1, false);
1185                    }
1186                    AddIntegralModifier(tg, label, aboveMiddle, 0, middleSegment2, middleSegment2 + 1, false);
1187                }
1188            } else if (tg.Pixels.size() > 0) {
1189                // 2 labels one to the left and the other to the right of graphic.
1190                POINT2 leftPt = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
1191                POINT2 rightPt = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
1192
1193                for (int j = 1; j < tg.Pixels.size() - 1; j++) {
1194                    POINT2 midPt = lineutility.MidPointDouble(tg.Pixels.get(j), tg.Pixels.get(j + 1), 0);
1195                    if (midPt.x <= leftPt.x) {
1196                        leftPt = midPt;
1197                    }
1198                    if (midPt.x >= rightPt.x) {
1199                        rightPt = midPt;
1200                    }
1201                }
1202
1203                if (leftPt != rightPt)
1204                    AddAreaModifier(tg, label, aboveMiddle, 0, leftPt, leftPt);
1205                AddAreaModifier(tg, label, aboveMiddle, 0, rightPt, rightPt);
1206            }
1207        } catch (Exception exc) {
1208            ErrorLogger.LogException(_className, "addModifierOnLine",
1209                    new RendererException("Failed inside addModifierOnLine", exc));
1210        }
1211    }
1212
1213    private static void addModifierOnLine(String label, TGLight tg, Graphics2D g2d) {
1214        addModifierOnLine(label, tg, g2d, false);
1215    }
1216
1217    /**
1218     * Adds N modifier on line
1219     */
1220    private static void addNModifier(TGLight tg, Graphics2D g2d) {
1221        if (tg.isHostile()) {
1222            addModifierOnLine(tg.get_N(), tg, g2d, true);
1223        }
1224    }
1225
1226    private static void addModifierBottomSegment(TGLight tg, String text) {
1227        int index = 0;
1228        double y = tg.Pixels.get(index).y + tg.Pixels.get(index + 1).y;
1229        for (int i = 1; i < tg.Pixels.size() - 1; i++) {
1230            if (tg.Pixels.get(i).y + tg.Pixels.get(i + 1).y > y) {
1231                index = i;
1232                y = tg.Pixels.get(index).y + tg.Pixels.get(index + 1).y;
1233            }
1234        }
1235        AddIntegralModifier(tg, text, aboveMiddle, 0, index, index + 1, false);
1236    }
1237
1238    private static void addModifierTopSegment(TGLight tg, String text) {
1239        int index = 0;
1240        double y = tg.Pixels.get(index).y + tg.Pixels.get(index + 1).y;
1241        for (int i = 1; i < tg.Pixels.size() - 1; i++) {
1242            if (tg.Pixels.get(i).y + tg.Pixels.get(i + 1).y < y) {
1243                index = i;
1244                y = tg.Pixels.get(index).y + tg.Pixels.get(index + 1).y;
1245            }
1246        }
1247        AddIntegralModifier(tg, text, aboveMiddle, 0, index, index + 1, false);
1248    }
1249
1250    private static void addDTG(TGLight tg, int type, double lineFactor1, double lineFactor2, POINT2 pt0, POINT2 pt1, FontMetrics metrics) {
1251        if (pt0 == null || pt1 == null)
1252            return;
1253
1254        double maxDTGWidth;
1255        if (pt0.x == pt1.x && pt0.y == pt1.y) {
1256            POINT2 ptUl = new POINT2(), ptUr = new POINT2(), ptLr = new POINT2(), ptLl = new POINT2();
1257            GetMBR(tg, ptUl, ptUr, ptLr, ptLl);
1258            maxDTGWidth = lineutility.CalcDistanceDouble(ptUl, ptUr);
1259        } else {
1260            maxDTGWidth = lineutility.CalcDistanceDouble(pt0, pt1);
1261        }
1262
1263        String dash = "";
1264        if (tg.get_DTG() != null && tg.get_DTG1() != null && !tg.get_DTG().isEmpty() && !tg.get_DTG1().isEmpty()) {
1265            dash = " - ";
1266        }
1267
1268        String combinedDTG = tg.get_DTG() + dash + tg.get_DTG1();
1269
1270        double stringWidth = metrics.stringWidth(combinedDTG);
1271
1272        if (stringWidth < maxDTGWidth) {
1273            // Add on one line
1274            AddModifier(tg, combinedDTG, type, lineFactor1, pt0, pt1);
1275        } else {
1276            // add on two lines
1277            // Use min and max on lineFactors. Always want W1 on top. This fixes when lineFactor < 0 W1 should use lineFactor1
1278            AddModifier(tg, tg.get_DTG() + dash, type, Math.min(lineFactor1, lineFactor2), pt0, pt1);
1279            AddModifier(tg, tg.get_DTG1(), type, Math.max(lineFactor1, lineFactor2), pt0, pt1);
1280        }
1281    }
1282
1283    private static int getVisibleMiddleSegment(TGLight tg, Object clipBounds) {
1284        int middleSegment = -1;
1285        try {
1286            Polygon clipBoundsPoly = null;
1287            Rectangle2D clipRect = null;
1288            boolean useClipRect; // true if clipBounds is Rectangle2D otherwise use clipBoundsPoly
1289            POINT2 pt0 = null, pt1 = null;
1290            double dist = 0;
1291            POINT2 lastPt = null;
1292            long lineType = tg.get_LineType();
1293            //we want the middle segment to be visible
1294            middleSegment = (tg.Pixels.size() + 1) / 2 - 1;
1295
1296            boolean foundVisibleSegment = false;
1297            if (clipBounds == null) {
1298                return middleSegment;
1299            }
1300
1301            if (ArrayList.class.isAssignableFrom(clipBounds.getClass())) {
1302                useClipRect = false;
1303                clipBoundsPoly = new Polygon();
1304                ArrayList<Point2D> clipArray = (ArrayList<Point2D>) clipBounds;
1305                for (int j = 0; j < clipArray.size(); j++) {
1306                    int x = (int) (clipArray.get(j)).getX();
1307                    int y = (int) (clipArray.get(j)).getY();
1308                    clipBoundsPoly.addPoint(x, y);
1309                }
1310            } else if (Rectangle2D.class.isAssignableFrom(clipBounds.getClass())) {
1311                useClipRect = true;
1312                clipRect = (Rectangle2D) clipBounds;
1313            } else {
1314                return middleSegment;
1315            }
1316
1317            //walk through the segments to find the first visible segment from the middle
1318            for (int j = middleSegment; j < tg.Pixels.size() - 1; j++) {
1319                pt0 = tg.Pixels.get(j);
1320                pt1 = tg.Pixels.get(j + 1);
1321                dist = lineutility.CalcDistanceDouble(pt0, pt1);
1322                if (dist < 5) {
1323                    continue;
1324                }
1325                //diagnostic
1326                if (j > 0 && lineType == TacticalLines.BOUNDARY) {
1327                    if (lastPt == null) {
1328                        lastPt = tg.Pixels.get(j - 1);
1329                    }
1330                    if (DoublesBack(lastPt, pt0, pt1)) {
1331                        continue;
1332                    }
1333
1334                    lastPt = null;
1335                }
1336                //if either of the points is within the bound then most of the segment is visible
1337                if (!useClipRect) {
1338                    if (clipBoundsPoly.contains(pt0.x, pt0.y) || clipBoundsPoly.contains(pt1.x, pt1.y)) {
1339                        middleSegment = j;
1340                        foundVisibleSegment = true;
1341                        break;
1342                    }
1343                } else {
1344                    if (clipRect.contains(pt0.x, pt0.y) || clipRect.contains(pt1.x, pt1.y)) {
1345                        middleSegment = j;
1346                        foundVisibleSegment = true;
1347                        break;
1348                    }
1349                }
1350            }
1351
1352            if (!foundVisibleSegment) {
1353                for (int j = middleSegment; j > 0; j--) {
1354                    pt0 = tg.Pixels.get(j);
1355                    pt1 = tg.Pixels.get(j - 1);
1356                    dist = lineutility.CalcDistanceDouble(pt0, pt1);
1357                    if (dist < 5) {
1358                        continue;
1359                    }
1360                    //diagnostic
1361                    if (lineType == TacticalLines.BOUNDARY) {
1362                        if (lastPt == null) {
1363                            lastPt = tg.Pixels.get(j - 1);
1364                        }
1365
1366                        if ( DoublesBack(lastPt, pt0, pt1)) {
1367                            continue;
1368                        }
1369
1370                        lastPt = null;
1371                    }
1372                    //if either of the points is within the bound then most of the segment is visible
1373                    if (!useClipRect) {
1374                        if (clipBoundsPoly.contains(pt0.x, pt0.y) || clipBoundsPoly.contains(pt1.x, pt1.y)) {
1375                            middleSegment = j - 1;
1376                            foundVisibleSegment = true;
1377                            break;
1378                        }
1379                    } else {
1380                        if (clipRect.contains(pt0.x, pt0.y) || clipRect.contains(pt1.x, pt1.y)) {
1381                            middleSegment = j - 1;
1382                            foundVisibleSegment = true;
1383                            break;
1384                        }
1385                    }
1386                }
1387            }
1388
1389            if (!foundVisibleSegment) {
1390                middleSegment = tg.Pixels.size() / 2 - 1;
1391            }
1392        } catch (Exception exc) {
1393            ErrorLogger.LogException(_className, "getMiddleSegment",
1394                    new RendererException("Failed inside getMiddleSegment", exc));
1395        }
1396        return middleSegment;
1397    }
1398
1399    /**
1400     * called repeatedly by RemoveModifiers to remove modifiers which fall
1401     * outside the symbol MBR
1402     *
1403     * @param tg
1404     * @param modifierType
1405     */
1406    private static void removeModifier(TGLight tg,
1407            String modifierType) {
1408        try {
1409            int j = 0;
1410            Modifier2 modifier = null;
1411            int n = tg.Pixels.size();
1412            //for (j = 0; j < tg.modifiers.size(); j++) 
1413            for (j = 0; j < n; j++) {
1414                modifier = tg.modifiers.get(j);
1415
1416                if (modifier.textID == null) {
1417                    continue;
1418                }
1419
1420                if (modifier.textID.equalsIgnoreCase(modifierType)) {
1421                    tg.modifiers.remove(modifier);
1422                    break;
1423                }
1424            }
1425        } catch (Exception exc) {
1426            ErrorLogger.LogException(_className, "removeModifier",
1427                    new RendererException("Failed inside removeModifier", exc));
1428        }
1429    }
1430
1431    /**
1432     * removes text modifiers for CPOF tactical areas which do not fit inside
1433     * the symbol MBR
1434     *
1435     * @param tg
1436     * @param g2d
1437     * @param isTextFlipped true if text is flipped from the last segment
1438     * orientation
1439     * @param iteration the instance count for this modifier
1440     */
1441    public static void RemoveModifiers(TGLight tg,
1442            Graphics2D g2d,
1443            boolean isTextFlipped,
1444            int iteration) {
1445        try {
1446            //CPOF clients only
1447            if (!tg.get_Client().equalsIgnoreCase("cpof2d") && !tg.get_Client().equalsIgnoreCase("cpof3d")) {
1448                return;
1449            }
1450
1451            int j = 0;
1452            Polygon mbrPoly = null;
1453            //if it's a change 1 rectangular area then use the pixels instead of the mbr
1454            //because those use aboveMiddle to build angular text
1455            switch (tg.get_LineType()) {
1456                case TacticalLines.RECTANGULAR:
1457                case TacticalLines.CUED_ACQUISITION:
1458                case TacticalLines.ACA_RECTANGULAR: //aboveMiddle modifiers: slanted text
1459                case TacticalLines.FFA_RECTANGULAR:
1460                case TacticalLines.NFA_RECTANGULAR:
1461                case TacticalLines.RFA_RECTANGULAR:
1462                case TacticalLines.KILLBOXBLUE_RECTANGULAR:
1463                case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
1464                case TacticalLines.FSA_RECTANGULAR:
1465                case TacticalLines.SHIP_AOI_RECTANGULAR:
1466                case TacticalLines.DEFENDED_AREA_RECTANGULAR:
1467                case TacticalLines.ATI_RECTANGULAR:
1468                case TacticalLines.CFFZ_RECTANGULAR:
1469                case TacticalLines.SENSOR_RECTANGULAR:
1470                case TacticalLines.CENSOR_RECTANGULAR:
1471                case TacticalLines.DA_RECTANGULAR:
1472                case TacticalLines.CFZ_RECTANGULAR:
1473                case TacticalLines.ZOR_RECTANGULAR:
1474                case TacticalLines.TBA_RECTANGULAR:
1475                case TacticalLines.TVAR_RECTANGULAR:
1476                case TacticalLines.ACA_CIRCULAR:
1477                case TacticalLines.CIRCULAR:
1478                case TacticalLines.BDZ:
1479                case TacticalLines.FSA_CIRCULAR:
1480                case TacticalLines.NOTACK:
1481                case TacticalLines.ATI_CIRCULAR:
1482                case TacticalLines.CFFZ_CIRCULAR:
1483                case TacticalLines.SENSOR_CIRCULAR:
1484                case TacticalLines.CENSOR_CIRCULAR:
1485                case TacticalLines.DA_CIRCULAR:
1486                case TacticalLines.CFZ_CIRCULAR:
1487                case TacticalLines.ZOR_CIRCULAR:
1488                case TacticalLines.TBA_CIRCULAR:
1489                case TacticalLines.TVAR_CIRCULAR:
1490                case TacticalLines.FFA_CIRCULAR:
1491                case TacticalLines.NFA_CIRCULAR:
1492                case TacticalLines.RFA_CIRCULAR:
1493                case TacticalLines.KILLBOXBLUE_CIRCULAR:
1494                case TacticalLines.KILLBOXPURPLE_CIRCULAR:
1495                    if (tg.modifiers == null || tg.modifiers.isEmpty() || iteration != 1) {
1496                        return;
1497                    }
1498
1499                    mbrPoly = new Polygon();
1500                    int n = tg.Pixels.size();
1501                    //for (j = 0; j < tg.Pixels.size(); j++) 
1502                    for (j = 0; j < n; j++) {
1503                        mbrPoly.addPoint((int) tg.Pixels.get(j).x, (int) tg.Pixels.get(j).y);
1504                    }
1505
1506                    break;
1507                default:    //area modifiers: horizontal text
1508                    if (clsUtility.isClosedPolygon(tg.get_LineType()) == false || iteration != 0) {
1509                        return;
1510                    }
1511                    if (tg.modifiers == null || tg.modifiers.isEmpty()) {
1512                        return;
1513                    }
1514
1515                    mbrPoly = new Polygon();
1516                    int t = tg.Pixels.size();
1517                    //for (j = 0; j < tg.Pixels.size(); j++) 
1518                    for (j = 0; j < t; j++) {
1519                        mbrPoly.addPoint((int) tg.Pixels.get(j).x, (int) tg.Pixels.get(j).y);
1520                    }
1521            }
1522
1523            Font font = null;
1524            font = tg.get_Font();    //might have to change this
1525            if (font == null) {
1526                font = g2d.getFont();
1527            }
1528            g2d.setFont(font);
1529            FontMetrics metrics = g2d.getFontMetrics();
1530
1531            double stringWidth = 0, stringHeight = 0;
1532            boolean wfits = true, w1fits = true, ww1fits = true, hfits = true, h1fits = true, h2fits = true;
1533            Modifier2 modifier = null;
1534            String modifierType = "";
1535            String s = "";
1536            POINT2 pt0 = null, pt1 = null, pt2 = null, pt3 = null, pt4 = null;
1537            double lineFactor = 0;
1538            double x = 0, y = 0;
1539            double x1 = 0, y1 = 0, x2 = 0, y2 = 0;            //logic as follows:
1540            //we have to loop through to determine if each modifiers fits and set its fitsMBR member
1541            //then run a 2nd loop to remove groups of modifiers based on whether any of the others do not fit
1542            //e.g. if W does not fit then remove W and W1 modifiers
1543            int n = tg.modifiers.size();
1544            //for (j = 0; j < tg.modifiers.size(); j++) 
1545            for (j = 0; j < n; j++) {
1546                modifier = tg.modifiers.get(j);
1547                if (modifier.textID == null || modifier.textID.isEmpty()) {
1548                    continue;
1549                }
1550
1551                modifierType = modifier.textID;
1552                lineFactor = modifier.lineFactor;
1553
1554                if (isTextFlipped) {
1555                    lineFactor = -lineFactor;
1556                }
1557
1558                s = modifier.text;
1559                if (s == null || s.equals("")) {
1560                    continue;
1561                }
1562                stringWidth = (double) metrics.stringWidth(s) + 1;
1563                stringHeight = (double) font.getSize();
1564
1565                if (modifier.type == area) {
1566                    pt0 = modifier.textPath[0];
1567                    x1 = pt0.x;
1568                    y1 = pt0.y;
1569                    x = (int) x1 - (int) stringWidth / 2;
1570                    y = (int) y1 + (int) (stringHeight / 2) + (int) (1.25 * lineFactor * stringHeight);
1571                    //pt1 = modifier.textPath[1];
1572                    x2 = (int) x1 + (int) stringWidth / 2;
1573                    y2 = (int) y1 + (int) (stringHeight / 2) + (int) (1.25 * lineFactor * stringHeight);
1574                    if (mbrPoly.contains(x, y) && mbrPoly.contains(x2, y2)) {
1575                        modifier.fitsMBR = true;
1576                    } else {
1577                        modifier.fitsMBR = false;
1578                    }
1579                } else if (modifier.type == aboveMiddle) {
1580                    pt0 = modifier.textPath[0];
1581                    pt1 = modifier.textPath[1];
1582                    //double dist=lineutility.CalcDistanceDouble(pt0, pt1);
1583                    POINT2 ptCenter = lineutility.MidPointDouble(pt0, pt1, 0);
1584                    pt0 = lineutility.ExtendAlongLineDouble(ptCenter, pt0, stringWidth / 2);
1585                    pt1 = lineutility.ExtendAlongLineDouble(ptCenter, pt1, stringWidth / 2);
1586
1587                    if (lineFactor >= 0) {
1588                        pt2 = lineutility.ExtendDirectedLine(ptCenter, pt0, pt0, 3, Math.abs((lineFactor) * stringHeight));
1589                    } else {
1590                        pt2 = lineutility.ExtendDirectedLine(ptCenter, pt0, pt0, 2, Math.abs((lineFactor) * stringHeight));
1591                    }
1592
1593                    if (lineFactor >= 0) {
1594                        pt3 = lineutility.ExtendDirectedLine(ptCenter, pt1, pt1, 3, Math.abs((lineFactor) * stringHeight));
1595                    } else {
1596                        pt3 = lineutility.ExtendDirectedLine(ptCenter, pt1, pt1, 2, Math.abs((lineFactor) * stringHeight));
1597                    }
1598
1599                    x1 = pt2.x;
1600                    y1 = pt2.y;
1601                    x2 = pt3.x;
1602                    y2 = pt3.y;
1603                    if (mbrPoly.contains(x1, y1) && mbrPoly.contains(x2, y2)) {
1604                        modifier.fitsMBR = true;
1605                    } else {
1606                        modifier.fitsMBR = false;
1607                    }
1608                } else {
1609                    modifier.fitsMBR = true;
1610                }
1611            }
1612            n = tg.modifiers.size();
1613            //for (j = 0; j < tg.modifiers.size(); j++) 
1614            for (j = 0; j < n; j++) {
1615                modifier = tg.modifiers.get(j);
1616                if (modifier.textID == null || modifier.textID.isEmpty()) {
1617                    continue;
1618                }
1619
1620                if (modifier.fitsMBR == false) {
1621                    if (modifier.textID.equalsIgnoreCase("W")) {
1622                        wfits = false;
1623                    } else if (modifier.textID.equalsIgnoreCase("W1")) {
1624                        w1fits = false;
1625                    } else if (modifier.textID.equalsIgnoreCase("W+W1")) {
1626                        ww1fits = false;
1627                    } else if (modifier.textID.equalsIgnoreCase("H")) {
1628                        hfits = false;
1629                    } else if (modifier.textID.equalsIgnoreCase("H1")) {
1630                        h1fits = false;
1631                    } else if (modifier.textID.equalsIgnoreCase("H2")) {
1632                        h2fits = false;
1633                    }
1634                }
1635            }
1636            if (wfits == false || w1fits == false) {
1637                removeModifier(tg, "W");
1638                removeModifier(tg, "W1");
1639            }
1640            if (ww1fits == false) {
1641                removeModifier(tg, "W+W1");
1642            }
1643            if (hfits == false || h1fits == false || h2fits == false) {
1644                removeModifier(tg, "H");
1645                removeModifier(tg, "H1");
1646                removeModifier(tg, "H2");
1647            }
1648
1649        } catch (Exception exc) {
1650            ErrorLogger.LogException(_className, "RemoveModifeirs",
1651                    new RendererException("Failed inside RemoveModifiers", exc));
1652        }
1653    }
1654
1655    /**
1656     * Calculates a segment in the pixels middle by length to hold a string.
1657     *
1658     * @param tg
1659     * @param stringWidth
1660     * @param segPt0
1661     * @param segPt1
1662     */
1663    private static void getPixelsMiddleSegment(TGLight tg,
1664            double stringWidth,
1665            POINT2 segPt0,
1666            POINT2 segPt1) {
1667        try {
1668            switch (tg.get_LineType()) {
1669                case TacticalLines.CFL:
1670                    break;
1671                default:
1672                    return;
1673            }
1674            int totalLength = 0;
1675            int j = 0;
1676            double dist = 0;
1677            double mid = 0;
1678            double remainder = 0;
1679            POINT2 pt0 = null, pt1 = null, pt2 = null, pt3 = null;
1680            POINT2 midPt = null;
1681            //first get the total length of all the segments
1682            int n = tg.Pixels.size();
1683            //for (j = 0; j < tg.Pixels.size() - 1; j++) 
1684            for (j = 0; j < n - 1; j++) {
1685                dist = lineutility.CalcDistanceDouble(tg.Pixels.get(j), tg.Pixels.get(j + 1));
1686                totalLength += dist;
1687            }
1688            mid = totalLength / 2;
1689            totalLength = 0;
1690            //walk thru the segments to find the middle
1691            //for (j = 0; j < tg.Pixels.size() - 1; j++) 
1692            for (j = 0; j < n - 1; j++) {
1693                dist = lineutility.CalcDistanceDouble(tg.Pixels.get(j), tg.Pixels.get(j + 1));
1694                totalLength += dist;
1695                if (totalLength >= mid)//current segment contains the middle
1696                {
1697                    remainder = totalLength - mid;
1698                    pt0 = tg.Pixels.get(j);
1699                    pt1 = tg.Pixels.get(j + 1);
1700                    //calculate the pixels mid point
1701                    midPt = lineutility.ExtendAlongLineDouble2(pt1, pt0, remainder);
1702                    pt2 = lineutility.ExtendAlongLineDouble2(midPt, pt0, stringWidth / 2);
1703                    pt3 = lineutility.ExtendAlongLineDouble2(midPt, pt1, stringWidth / 2);
1704                    segPt0.x = pt2.x;
1705                    segPt0.y = pt2.y;
1706                    segPt1.x = pt3.x;
1707                    segPt1.y = pt3.y;
1708                    break;
1709                }
1710            }
1711        } catch (Exception exc) {
1712            ErrorLogger.LogException(_className, "getPixelsMidpoint",
1713                    new RendererException("Failed inside getPixelsMidpoint", exc));
1714        }
1715    }
1716
1717    private static double getChange1Height(TGLight tg) {
1718        double height = 0;
1719        try {
1720            switch (tg.get_LineType()) {
1721                //case TacticalLines.PAA_RECTANGULAR:
1722                case TacticalLines.FSA_RECTANGULAR:
1723                case TacticalLines.SHIP_AOI_RECTANGULAR:
1724                case TacticalLines.DEFENDED_AREA_RECTANGULAR:
1725                case TacticalLines.FFA_RECTANGULAR:
1726                case TacticalLines.ACA_RECTANGULAR:
1727                case TacticalLines.NFA_RECTANGULAR:
1728                case TacticalLines.RFA_RECTANGULAR:
1729                case TacticalLines.ATI_RECTANGULAR:
1730                case TacticalLines.CFFZ_RECTANGULAR:
1731                case TacticalLines.SENSOR_RECTANGULAR:
1732                case TacticalLines.CENSOR_RECTANGULAR:
1733                case TacticalLines.DA_RECTANGULAR:
1734                case TacticalLines.CFZ_RECTANGULAR:
1735                case TacticalLines.ZOR_RECTANGULAR:
1736                case TacticalLines.TBA_RECTANGULAR:
1737                case TacticalLines.TVAR_RECTANGULAR:
1738                case TacticalLines.KILLBOXBLUE_RECTANGULAR:
1739                case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
1740                    break;
1741                default:
1742                    return 0;
1743            }
1744            double x1 = tg.Pixels.get(0).x;
1745            double y1 = tg.Pixels.get(0).y;
1746            double x2 = tg.Pixels.get(1).x;
1747            double y2 = tg.Pixels.get(1).y;
1748            double deltax = x2 - x1;
1749            double deltay = y2 - y1;
1750            height = Math.sqrt(deltax * deltax + deltay * deltay);
1751        } catch (Exception exc) {
1752            ErrorLogger.LogException(_className, "getChange1Height",
1753                    new RendererException("Failed inside getChange1Height", exc));
1754        }
1755        return height;
1756    }
1757
1758    /**
1759     * scale the line factor for closed areas
1760     *
1761     * @param tg
1762     */
1763    private static void scaleModifiers(TGLight tg) {
1764        try {
1765            if (RendererSettings.getInstance().getAutoCollapseModifiers() == false) {
1766                return;
1767            }
1768            if (!tg.get_Client().equalsIgnoreCase("ge")) {
1769                return;
1770            }
1771            //exit if there are no modifiers or it's not a closed area
1772            if (tg.modifiers == null || tg.modifiers.isEmpty()) {
1773                return;
1774            }
1775            int linetype = tg.get_LineType();
1776            boolean isClosedPolygon = clsUtility.isClosedPolygon(linetype);
1777            boolean isChange1Area = clsUtility.IsChange1Area(linetype);
1778            if (!isClosedPolygon && !isChange1Area) {
1779                return;
1780            }
1781            switch(linetype)
1782            {
1783                case TacticalLines.PAA_CIRCULAR:
1784                case TacticalLines.PAA_RECTANGULAR:
1785                case TacticalLines.RECTANGULAR_TARGET:
1786                case TacticalLines.RANGE_FAN:
1787                case TacticalLines.RANGE_FAN_SECTOR:
1788                case TacticalLines.RADAR_SEARCH:
1789                    return;
1790                default:
1791                    break;
1792            }
1793            POINT2 ptUl = new POINT2(), ptUr = new POINT2(), ptLr = new POINT2(), ptLl = new POINT2();
1794            GetMBR(tg, ptUl, ptUr, ptLr, ptLl);
1795            int sz = tg.get_Font().getSize();
1796            //heightMBR is half the MBR height
1797            //double heightMBR=Math.abs(ptLr.y-ptUr.y)/2;
1798            double heightMBR = 0;
1799            double change1Height = getChange1Height(tg);
1800            if (change1Height <= 0) {
1801                heightMBR = Math.abs(ptLr.y - ptUr.y) / 2;
1802            } else {
1803                heightMBR = change1Height;
1804            }
1805
1806            double heightModifiers = 0;
1807            ArrayList<Modifier2> modifiers = tg.modifiers;
1808            Modifier2 modifier = null;
1809            double minLF = Integer.MAX_VALUE;
1810            int j = 0;
1811            boolean isValid = false;
1812            for (j = 0; j < modifiers.size(); j++) {
1813                modifier = modifiers.get(j);
1814                //if(modifier.type == area)
1815                //type3Area=true;
1816                if (modifier.type == toEnd) {
1817                    continue;
1818                }
1819                if (modifier.type == aboveMiddle && isChange1Area == false) {
1820                    continue;
1821                }
1822                if (modifier.lineFactor < minLF) {
1823                    minLF = modifier.lineFactor;
1824                }
1825                isValid = true;
1826            }
1827            //if there are no 'area' modifiers then exit early
1828            if (!isValid) {
1829                return;
1830            }
1831
1832            heightModifiers = Math.abs(minLF) * sz;
1833            boolean expandModifiers = false, shrinkModifiers = false;
1834            if (heightModifiers > heightMBR) {
1835                shrinkModifiers = true;
1836            } else if (heightModifiers < 0.5 * heightMBR) {
1837                expandModifiers = true;
1838            }
1839
1840            boolean addEllipsis = false;
1841            //modifierE is ellipses modifier
1842            Modifier2 modifierE = new Modifier2();
1843            if (expandModifiers) {
1844                double factor = heightMBR / heightModifiers;
1845                factor = 1 + (factor - 1) / 4;
1846                if (factor > 2) {
1847                    factor = 2;
1848                }
1849                for (j = 0; j < modifiers.size(); j++) {
1850                    modifier = modifiers.get(j);
1851                    if(modifier.type==aboveMiddle)
1852                    {
1853                        if(isChange1Area==false)
1854                            continue;
1855                    }
1856                    else if(modifier.type!=area)
1857                            continue;
1858                    
1859                    modifier.lineFactor *= factor;
1860                }
1861            } else if (shrinkModifiers) {
1862                double deltaLF = (heightModifiers - heightMBR) / sz;
1863                double newLF = 0;
1864                //use maxLF for the ellipsis modifier
1865                double maxLF = 0;
1866                for (j = 0; j < modifiers.size(); j++) {
1867                    modifier = modifiers.get(j);
1868                    if(modifier.type==aboveMiddle)
1869                    {
1870                        if(isChange1Area==false)
1871                            continue;
1872                    }
1873                    else if(modifier.type!=area)
1874                            continue;
1875                    newLF = modifier.lineFactor + deltaLF;
1876                    if (Math.abs(newLF * sz) >= heightMBR) {
1877                        //flag the modifier to remove
1878                        if (modifier.lineFactor > minLF) {
1879                            modifierE.type = modifier.type;
1880                            modifier.type = 7;
1881                            if (!modifier.text.isEmpty()) {
1882                                addEllipsis = true;
1883                            }
1884                        }
1885                        modifier.lineFactor = newLF;
1886                        //modifierE.type=area;
1887                        //modifierE.type=modifier.type;
1888                        modifierE.textPath = modifier.textPath;
1889                        continue;
1890                    }
1891                    modifier.lineFactor = newLF;
1892                }
1893                ArrayList<Modifier2> modifiers2 = new ArrayList();
1894                for (j = 0; j < modifiers.size(); j++) {
1895                    modifier = modifiers.get(j);
1896                    if (modifier.type != 7) {
1897                        if (modifier.lineFactor > maxLF) {
1898                            maxLF = modifier.lineFactor;
1899                        }
1900                        modifiers2.add(modifier);
1901                    }
1902                }
1903                if (addEllipsis) {
1904                    Character letter = (char) 9679;
1905                    String s = Character.toString(letter);
1906                    String echelonSymbol = s + s + s;
1907                    modifierE.text = echelonSymbol;
1908                    modifierE.lineFactor = maxLF + 1;
1909                    modifiers2.add(modifierE);
1910                }
1911                tg.modifiers = modifiers2;
1912            }   //end shrink modifiers
1913        } catch (Exception exc) {
1914            ErrorLogger.LogException(_className, "scaleModifiers",
1915                    new RendererException("Failed inside scaleModifiers", exc));
1916        }
1917    }
1918
1919    /**
1920     * Calculate modifiers identical to addModifiers except use geodesic
1921     * calculations for the center point.
1922     *
1923     * @param tg
1924     * @param g2d
1925     * @param clipBounds
1926     * @param converter
1927     */
1928    public static void AddModifiersGeo(TGLight tg,
1929            Graphics2D g2d,
1930            Object clipBounds,
1931            IPointConversion converter) {
1932        try {
1933            //exit early for those not affected
1934            if (tg.Pixels == null || tg.Pixels.isEmpty()) {
1935                return;
1936            }
1937            ArrayList<POINT2> origPoints = null;
1938            Font font = tg.get_Font();
1939            if (font == null) {
1940                font = g2d.getFont();
1941            }
1942            g2d.setFont(font);
1943
1944            boolean shiftLines = Channels.getShiftLines();
1945            boolean usas = false, foundSegment = false;
1946            double csFactor = 1d, dist = 0, dist2 = 0;//this will be used for text spacing the 3d map (CommandCight)
1947            POINT2 midPt = null;
1948            int northestPtIndex = 0;
1949            int southestPtIndex = 0;
1950            POINT2 northestPt = null;
1951            POINT2 southestPt = null;
1952
1953            Rectangle2D clipRect = null;
1954            ArrayList<Point2D> clipArray = null;
1955            if (clipBounds != null && ArrayList.class.isAssignableFrom(clipBounds.getClass())) {
1956                clipArray = (ArrayList<Point2D>) clipBounds;
1957            }
1958            if (clipBounds != null && Rectangle2D.Double.class.isAssignableFrom(clipBounds.getClass())) {
1959                clipRect = (Rectangle2D.Double) clipBounds;
1960            }
1961
1962            FontMetrics metrics = g2d.getFontMetrics();
1963            int stringWidth = 0, stringWidth2 = 0;
1964            String WDash = ""; // Dash between W and W1 if they're not empty
1965            String TSpace = "", TDash = ""; // Space or dash between label and T modifier if T isn't empty
1966            if (tg.get_DTG() != null && tg.get_DTG1() != null && !tg.get_DTG().isEmpty() && !tg.get_DTG1().isEmpty()) {
1967                WDash = " - ";
1968            }
1969            if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
1970                TSpace = " ";
1971                TDash = " - ";
1972            }
1973
1974            if (tg.get_Client().equals("cpof3d")) {
1975                csFactor = 0.9d;
1976            }
1977                        
1978            switch (tg.get_LineType()) {
1979                case TacticalLines.SERIES:
1980                case TacticalLines.STRIKWARN:
1981                case TacticalLines.MSR:
1982                case TacticalLines.MSR_ONEWAY:
1983                case TacticalLines.MSR_TWOWAY:
1984                case TacticalLines.MSR_ALT:
1985                case TacticalLines.ASR:
1986                case TacticalLines.ASR_ONEWAY:
1987                case TacticalLines.ASR_TWOWAY:
1988                case TacticalLines.ASR_ALT:
1989                case TacticalLines.ROUTE:
1990                case TacticalLines.ROUTE_ONEWAY:
1991                case TacticalLines.ROUTE_ALT:
1992                case TacticalLines.DHA_REVD:
1993                case TacticalLines.DHA:
1994                case TacticalLines.EPW:
1995                case TacticalLines.UXO:
1996                case TacticalLines.FARP:
1997                case TacticalLines.BSA:
1998                case TacticalLines.DSA:
1999                case TacticalLines.CSA:
2000                case TacticalLines.RSA:
2001                case TacticalLines.THUNDERSTORMS:
2002                case TacticalLines.ICING:
2003                case TacticalLines.FREEFORM:
2004                case TacticalLines.RHA:
2005                case TacticalLines.LINTGT:
2006                case TacticalLines.LINTGTS:
2007                case TacticalLines.FPF:
2008                case TacticalLines.GAP:
2009                case TacticalLines.DEPICT:
2010                case TacticalLines.AIRHEAD:
2011                case TacticalLines.FSA:
2012                case TacticalLines.DIRATKAIR:
2013                case TacticalLines.OBJ:
2014                case TacticalLines.AO:
2015                case TacticalLines.ACA:
2016                case TacticalLines.FFA:
2017                case TacticalLines.PAA:
2018                case TacticalLines.NFA:
2019                case TacticalLines.RFA:
2020                case TacticalLines.ATI:
2021                case TacticalLines.CFFZ:
2022                case TacticalLines.CFZ:
2023                case TacticalLines.TBA:
2024                case TacticalLines.TVAR:
2025                case TacticalLines.KILLBOXBLUE:
2026                case TacticalLines.KILLBOXPURPLE:
2027                case TacticalLines.ZOR:
2028                case TacticalLines.DA:
2029                case TacticalLines.SENSOR:
2030                case TacticalLines.CENSOR:
2031                case TacticalLines.SMOKE:
2032                case TacticalLines.BATTLE:
2033                case TacticalLines.PNO:
2034                case TacticalLines.PDF:
2035                case TacticalLines.NAI:
2036                case TacticalLines.TAI:
2037                case TacticalLines.BASE_CAMP_REVD:
2038                case TacticalLines.BASE_CAMP:
2039                case TacticalLines.GUERILLA_BASE_REVD:
2040                case TacticalLines.GUERILLA_BASE:
2041                case TacticalLines.GENERIC_AREA:
2042                case TacticalLines.ATKPOS:
2043                case TacticalLines.ASSAULT:
2044                case TacticalLines.WFZ:
2045                case TacticalLines.OBSFAREA:
2046                case TacticalLines.OBSAREA:
2047                case TacticalLines.ROZ:
2048                case TacticalLines.AARROZ:
2049                case TacticalLines.UAROZ:
2050                case TacticalLines.WEZ:
2051                case TacticalLines.FEZ:
2052                case TacticalLines.JEZ:
2053                case TacticalLines.FAADZ:
2054                case TacticalLines.HIDACZ:
2055                case TacticalLines.MEZ:
2056                case TacticalLines.LOMEZ:
2057                case TacticalLines.HIMEZ:
2058                case TacticalLines.SAAFR:
2059                case TacticalLines.AC:
2060                case TacticalLines.MRR:
2061                case TacticalLines.SL:
2062                case TacticalLines.TC:
2063                case TacticalLines.SC:
2064                case TacticalLines.LLTR:
2065                case TacticalLines.AIRFIELD:
2066                case TacticalLines.GENERAL:
2067                case TacticalLines.JTAA:
2068                case TacticalLines.SAA:
2069                case TacticalLines.SGAA:
2070                case TacticalLines.FORT_REVD:
2071                case TacticalLines.FORT:
2072                case TacticalLines.ENCIRCLE:
2073                case TacticalLines.ASSY:
2074                case TacticalLines.EA:
2075                case TacticalLines.DZ:
2076                case TacticalLines.EZ:
2077                case TacticalLines.LZ:
2078                case TacticalLines.PZ:
2079                case TacticalLines.LAA:
2080                case TacticalLines.BOUNDARY:
2081                case TacticalLines.MINED:
2082                case TacticalLines.FENCED:
2083                case TacticalLines.PL:
2084                case TacticalLines.FEBA:
2085                case TacticalLines.FCL:
2086                case TacticalLines.HOLD:
2087                case TacticalLines.BRDGHD:
2088                case TacticalLines.HOLD_GE:
2089                case TacticalLines.BRDGHD_GE:
2090                case TacticalLines.LOA:
2091                case TacticalLines.LOD:
2092                case TacticalLines.LL:
2093                case TacticalLines.EWL:
2094                case TacticalLines.RELEASE:
2095                case TacticalLines.HOL:
2096                case TacticalLines.BHL:
2097                case TacticalLines.LDLC:
2098                case TacticalLines.PLD:
2099                case TacticalLines.NFL:
2100                case TacticalLines.MFP:
2101                case TacticalLines.FSCL:
2102                case TacticalLines.BCL_REVD:
2103                case TacticalLines.BCL:
2104                case TacticalLines.ICL:
2105                case TacticalLines.IFF_OFF:
2106                case TacticalLines.IFF_ON:
2107                case TacticalLines.GENERIC_LINE:
2108                case TacticalLines.CFL:
2109                case TacticalLines.RFL:
2110                case TacticalLines.FLOT:
2111                case TacticalLines.LC:
2112                case TacticalLines.CATK:
2113                case TacticalLines.CATKBYFIRE:
2114                case TacticalLines.IL:
2115                case TacticalLines.DRCL:
2116                case TacticalLines.RETIRE:
2117                case TacticalLines.FPOL:
2118                case TacticalLines.RPOL:
2119                case TacticalLines.WITHDRAW:
2120                case TacticalLines.WDRAWUP:
2121                case TacticalLines.BEARING:
2122                case TacticalLines.BEARING_J:
2123                case TacticalLines.BEARING_RDF:
2124                case TacticalLines.ELECTRO:
2125                case TacticalLines.BEARING_EW:
2126                case TacticalLines.ACOUSTIC:
2127                case TacticalLines.ACOUSTIC_AMB:
2128                case TacticalLines.TORPEDO:
2129                case TacticalLines.OPTICAL:
2130                case TacticalLines.RIP:
2131                case TacticalLines.BOMB:
2132                case TacticalLines.ZONE:
2133                case TacticalLines.AT:
2134                case TacticalLines.STRONG:
2135                case TacticalLines.MSDZ:
2136                case TacticalLines.SCREEN:
2137                case TacticalLines.COVER:
2138                case TacticalLines.GUARD:
2139                case TacticalLines.DELAY:
2140                case TacticalLines.TGMF:
2141                case TacticalLines.BIO:
2142                case TacticalLines.CHEM:
2143                case TacticalLines.NUC:
2144                case TacticalLines.RAD:
2145                case TacticalLines.MINE_LINE:
2146                case TacticalLines.ANCHORAGE_LINE:
2147                case TacticalLines.ANCHORAGE_AREA:
2148                case TacticalLines.SPT:
2149                case TacticalLines.AIRAOA:
2150                case TacticalLines.AAAAA:
2151                case TacticalLines.MAIN:
2152                case TacticalLines.DIRATKSPT:
2153                case TacticalLines.DIRATKGND:
2154                case TacticalLines.LAUNCH_AREA:
2155                case TacticalLines.DEFENDED_AREA_CIRCULAR:
2156                case TacticalLines.RECTANGULAR:
2157                case TacticalLines.CIRCULAR:
2158                case TacticalLines.RECTANGULAR_TARGET:
2159                case TacticalLines.LINE:
2160                case TacticalLines.ASLTXING:
2161                    origPoints = lineutility.getDeepCopy(tg.Pixels);
2162                    break;
2163                default:    //exit early for those not applicable
2164                    return;
2165            }
2166
2167            int linetype = tg.get_LineType();
2168            int j = 0, k = 0;
2169            double x = 0, y = 0;
2170
2171            int lastIndex = tg.Pixels.size() - 1;
2172            int nextToLastIndex = tg.Pixels.size() - 2;
2173            POINT2 pt0 = new POINT2(tg.Pixels.get(0));
2174            POINT2 pt1 = null;
2175            POINT2 pt2 = null, pt3 = null;
2176            POINT2 ptLast = new POINT2(tg.Pixels.get(lastIndex));
2177            POINT2 ptNextToLast = null;
2178            double DPIScaleFactor = RendererSettings.getInstance().getDeviceDPI() / 96.0;
2179
2180            if (lastIndex > 0) {
2181                ptNextToLast = new POINT2(tg.Pixels.get(nextToLastIndex));
2182            }
2183
2184            if (tg.Pixels.size() > 1) {
2185                pt1 = new POINT2(tg.Pixels.get(1));
2186            }
2187
2188            //prevent vertical paths for modifiers that use toEnd
2189            shiftModifierPath(tg, pt0, pt1, ptLast, ptNextToLast);
2190
2191            String label = GetCenterLabel(tg);
2192            String v=tg.get_V();
2193            String ap=tg.get_AP();
2194            Object[] pts = tg.Pixels.toArray();
2195            //need this for areas and some lines
2196            POINT2 ptCenter = null;
2197            if (converter != null) //cpof uses latlonconverter so cpof passes null for this
2198            {
2199                ptCenter = mdlGeodesic.geodesic_center(tg.LatLongs);
2200                if (ptCenter != null) {
2201                    Point2D pt22 = converter.GeoToPixels(new Point2D.Double(ptCenter.x, ptCenter.y));
2202                    ptCenter.x = pt22.getX();
2203                    ptCenter.y = pt22.getY();
2204                } else {
2205                    ptCenter = lineutility.CalcCenterPointDouble2(pts, pts.length);
2206                }
2207            } else {
2208                ptCenter = lineutility.CalcCenterPointDouble2(pts, pts.length);
2209            }
2210
2211            int middleSegment = (tg.Pixels.size() + 1) / 2 - 1;
2212            int middleSegment2 = 0;
2213
2214            if (clipRect != null) {
2215                middleSegment = getVisibleMiddleSegment(tg, clipRect);
2216            } else if (clipArray != null) {
2217                middleSegment = getVisibleMiddleSegment(tg, clipArray);
2218            }
2219            if (tg.Pixels.size() > 2) {
2220                pt2 = tg.Pixels.get(2);
2221            }
2222            if (tg.Pixels.size() > 3) {
2223                pt3 = tg.Pixels.get(3);
2224            }
2225            double TLineFactor = 0, T1LineFactor = 0;
2226            POINT2 lr = new POINT2(tg.Pixels.get(0));
2227            POINT2 ll = new POINT2(tg.Pixels.get(0));
2228            POINT2 ul = new POINT2(tg.Pixels.get(0));
2229            POINT2 ur = new POINT2(tg.Pixels.get(0));
2230            int index = 0;
2231            int nextIndex = 0;
2232            int size = tg.Pixels.size();
2233            Line2D line = null;
2234                        
2235                        double dAngle0, dAngle1;
2236            int stringHeight;
2237
2238            switch (linetype) {
2239                case TacticalLines.PL:
2240                    AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), toEnd, T1LineFactor, pt0, pt1, false);
2241                    AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), toEnd, T1LineFactor, ptLast, ptNextToLast, false);
2242                    break;
2243                case TacticalLines.FEBA:
2244                    AddIntegralAreaModifier(tg, label, toEnd, 0, pt0, pt1, false);
2245                    AddIntegralAreaModifier(tg, label, toEnd, 0, ptLast, ptNextToLast, false);
2246                    break;
2247                // T before label
2248                case TacticalLines.FSCL:
2249                    pt0 = tg.Pixels.get(0);
2250                    pt1 = tg.Pixels.get(1);
2251                    pt2 = tg.Pixels.get(tg.Pixels.size() - 1);
2252                    pt3 = tg.Pixels.get(tg.Pixels.size() - 2);
2253                    dist = lineutility.CalcDistanceDouble(pt0, pt1);
2254                    dist2 = lineutility.CalcDistanceDouble(pt2, pt3);
2255                    stringWidth = (int) ((double) metrics.stringWidth(tg.get_Name() + " " + label));
2256                    stringWidth2 = (int) ((double) metrics.stringWidth(tg.get_DTG()));
2257                    if (stringWidth2 > stringWidth) {
2258                        stringWidth = stringWidth2;
2259                    }
2260
2261                    if (tg.Pixels.size() == 2) //one segment
2262                    {
2263                        pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2264                        AddModifier2(tg, tg.get_Name() + " " + label , aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2265                        AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2266                        AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2267                        if (dist > 3.5 * stringWidth)//was 28stringwidth+5
2268                        {
2269                            pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
2270                            pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
2271                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2272                            AddModifier2(tg, tg.get_Name() + " " + label, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2273                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2274                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2275                        }
2276                    } else //more than one semgent
2277                    {
2278                        double dist3 = lineutility.CalcDistanceDouble(pt0, pt2);
2279                        if (dist > stringWidth + 5 || dist >= dist2 || dist3 > stringWidth + 5) {
2280                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2281                            AddModifier2(tg, tg.get_Name() + " " + label, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2282                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2283                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2284                        }
2285                        if (dist2 > stringWidth + 5 || dist2 > dist || dist3 > stringWidth + 5) {
2286                            pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
2287                            pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
2288                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2289                            AddModifier2(tg, tg.get_Name() + " " + label, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2290                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2291                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2292                        }
2293                    }
2294                    break;
2295                // T after label
2296                case TacticalLines.ICL:
2297                case TacticalLines.NFL:
2298                case TacticalLines.BCL_REVD:
2299                case TacticalLines.RFL:
2300                    pt0 = tg.Pixels.get(0);
2301                    pt1 = tg.Pixels.get(1);
2302                    pt2 = tg.Pixels.get(tg.Pixels.size() - 1);
2303                    pt3 = tg.Pixels.get(tg.Pixels.size() - 2);
2304                    dist = lineutility.CalcDistanceDouble(pt0, pt1);
2305                    dist2 = lineutility.CalcDistanceDouble(pt2, pt3);
2306                    stringWidth = (int) ((double) metrics.stringWidth(tg.get_Name() + " " + label));
2307                    stringWidth2 = (int) ((double) metrics.stringWidth(tg.get_DTG()));
2308                    if (stringWidth2 > stringWidth) {
2309                        stringWidth = stringWidth2;
2310                    }
2311
2312                    if (tg.Pixels.size() == 2) //one segment
2313                    {
2314                        pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2315                        AddModifier2(tg, label + TSpace + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2316                        AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2317                        AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2318                        if (dist > 3.5 * stringWidth)//was 28stringwidth+5
2319                        {
2320                            pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
2321                            pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
2322                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2323                            AddModifier2(tg, label + TSpace + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2324                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2325                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2326                        }
2327                    } else //more than one semgent
2328                    {
2329                        double dist3 = lineutility.CalcDistanceDouble(pt0, pt2);
2330                        if (dist > stringWidth + 5 || dist >= dist2 || dist3 > stringWidth + 5) {
2331                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2332                            AddModifier2(tg, label + TSpace + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2333                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2334                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2335                        }
2336                        if (dist2 > stringWidth + 5 || dist2 > dist || dist3 > stringWidth + 5) {
2337                            pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
2338                            pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
2339                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2340                            AddModifier2(tg, label + TSpace + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2341                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2342                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2343                        }
2344                    }
2345                    break;
2346                case TacticalLines.BCL:
2347                    pt0 = tg.Pixels.get(0);
2348                    pt1 = tg.Pixels.get(1);
2349                    pt2 = tg.Pixels.get(tg.Pixels.size() - 1);
2350                    pt3 = tg.Pixels.get(tg.Pixels.size() - 2);
2351                    dist = lineutility.CalcDistanceDouble(pt0, pt1);
2352                    dist2 = lineutility.CalcDistanceDouble(pt2, pt3);
2353                    String TMod = ""; // Don't add parenthesis if T modifier is empty
2354                    if (tg.get_Name() != null && !tg.get_Name().isEmpty())
2355                        TMod = " (" + tg.get_Name() + ")";
2356                    stringWidth = (int) ((double) metrics.stringWidth(label + TMod));
2357                    stringWidth2 = (int) ((double) metrics.stringWidth(tg.get_DTG()));
2358                    if (stringWidth2 > stringWidth) {
2359                        stringWidth = stringWidth2;
2360                    }
2361
2362                    if (tg.Pixels.size() == 2) //one segment
2363                    {
2364                        pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2365                        AddModifier2(tg, label + TMod, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2366                        AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2367                        AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2368                        if (dist > 3.5 * stringWidth)//was 28stringwidth+5
2369                        {
2370                            pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
2371                            pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
2372                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2373                            AddModifier2(tg, label + TMod, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2374                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2375                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2376                        }
2377                    } else //more than one semgent
2378                    {
2379                        double dist3 = lineutility.CalcDistanceDouble(pt0, pt2);
2380                        if (dist > stringWidth + 5 || dist >= dist2 || dist3 > stringWidth + 5) {
2381                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2382                            AddModifier2(tg, label + TMod, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2383                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2384                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2385                        }
2386                        if (dist2 > stringWidth + 5 || dist2 > dist || dist3 > stringWidth + 5) {
2387                            pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
2388                            pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
2389                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2390                            AddModifier2(tg, label + TMod, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2391                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2392                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2393                        }
2394                    }
2395                    break;
2396                case TacticalLines.DIRATKSPT:
2397                case TacticalLines.DIRATKAIR:
2398                case TacticalLines.DIRATKGND:
2399                    midPt = lineutility.MidPointDouble(pt0, pt1, 0);
2400                    //midPt=lineutility.MidPointDouble(pt0, midPt, 0);
2401                    AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, 0, pt0, midPt, false);
2402                    addDTG(tg, aboveMiddle, csFactor, 2 * csFactor, pt0, pt1, metrics);
2403                    break;
2404                case TacticalLines.SPT:
2405                case TacticalLines.AIRAOA:
2406                case TacticalLines.AAAAA:
2407                case TacticalLines.MAIN:
2408                    if (tg.Pixels.size() == 3) //one segment
2409                    {
2410                        midPt = lineutility.MidPointDouble(pt0, pt1, 0);
2411                        AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, aboveMiddle, 0, midPt, midPt, false);
2412                        AddIntegralAreaModifier(tg, tg.get_DTG1(), aboveMiddle, csFactor, midPt, midPt, false);
2413                        AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, 2 * csFactor, midPt, midPt, false);
2414
2415                    } else if (tg.Pixels.size() == 4) //2 segments
2416                    {
2417                        midPt = lineutility.MidPointDouble(pt1, pt2, 0);
2418                        AddIntegralAreaModifier(tg,tg.get_DTG() + WDash, aboveMiddle, 0, midPt, midPt, false);
2419                        AddIntegralAreaModifier(tg, tg.get_DTG1(), aboveMiddle, csFactor, midPt, midPt, false);
2420                        AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, 2 * csFactor, midPt, midPt, false);
2421                    } else // 3 or more segments
2422                    {
2423                        midPt = lineutility.MidPointDouble(pt1, pt2, 0);
2424                        AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, aboveMiddle, -csFactor / 2, midPt, midPt, false);
2425                        AddIntegralAreaModifier(tg, tg.get_DTG1(), aboveMiddle, csFactor / 2, midPt, midPt, false);
2426                        midPt = lineutility.MidPointDouble(pt2, pt3, 0);
2427                        AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, -csFactor / 2, midPt, midPt, false);
2428                    }
2429                    break;
2430                case TacticalLines.LL:
2431                case TacticalLines.LOD:
2432                case TacticalLines.LDLC:
2433                case TacticalLines.PLD:
2434                case TacticalLines.RELEASE:
2435                case TacticalLines.HOL:
2436                case TacticalLines.BHL:
2437                case TacticalLines.FCL:
2438                case TacticalLines.HOLD:
2439                case TacticalLines.BRDGHD:
2440                case TacticalLines.HOLD_GE:
2441                case TacticalLines.BRDGHD_GE:
2442                case TacticalLines.LOA:
2443                case TacticalLines.IFF_OFF:
2444                case TacticalLines.IFF_ON:
2445                    AddIntegralAreaModifier(tg, label, aboveEnd, -csFactor, pt0, pt1, false);
2446                    AddIntegralAreaModifier(tg, label, aboveEnd, -csFactor, ptLast, ptNextToLast, false);
2447                    break;
2448                case TacticalLines.EWL:
2449                    AddIntegralAreaModifier(tg, label, aboveEnd, -csFactor, pt0, pt1, false);
2450                    AddIntegralAreaModifier(tg, label, aboveEnd, -csFactor, ptLast, ptNextToLast, false);
2451                    tg.set_EchelonSymbol("");
2452                    if (clipRect != null) {
2453                        AddBoundaryModifiers(tg, g2d, clipRect);
2454                    } else {
2455                        AddBoundaryModifiers(tg, g2d, clipArray);
2456                    }
2457                    break;
2458                case TacticalLines.AIRFIELD:
2459                    ur = new POINT2();
2460                    ul = new POINT2();
2461                    ll = new POINT2();
2462                    lr = new POINT2();
2463                    Modifier2.GetMBR(tg, ul, ur, lr, ll);
2464                    stringWidth = metrics.stringWidth(tg.get_H());
2465                    pt0.x = ur.x + stringWidth / 2 + 1;
2466                    //pt0.x=ptUr.x+1;
2467                    //pt0.y=(ptUr.y+ptLr.y)/2-metrics.getFont().getSize()
2468                    pt0.y = (ur.y + lr.y) / 2 - font.getSize();
2469                    AddIntegralAreaModifier(tg, tg.get_H(), area, csFactor, pt0, pt0, false);
2470                    break;
2471                case TacticalLines.LAUNCH_AREA:
2472                case TacticalLines.DEFENDED_AREA_CIRCULAR:
2473                    AddIntegralAreaModifier(tg, label + TDash + tg.get_Name(), area, 0, ptCenter, ptCenter, false);
2474                    break;
2475                case TacticalLines.JTAA:
2476                case TacticalLines.SAA:
2477                case TacticalLines.SGAA:
2478                    addNModifier(tg, g2d);
2479                    AddIntegralAreaModifier(tg, label + TDash + tg.get_Name(), area, 0, ptCenter, ptCenter, false);
2480                    addDTG(tg, area, csFactor, 2 * csFactor, ptCenter, ptCenter, metrics);
2481                    break;
2482                case TacticalLines.FORT:
2483                case TacticalLines.ZONE:
2484                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, ptCenter, ptCenter, false);
2485                    break;
2486                case TacticalLines.BDZ:
2487                    AddIntegralAreaModifier(tg, label, area, 0, pt0, pt0, false);
2488                    break;
2489                case TacticalLines.ASSAULT:
2490                case TacticalLines.ATKPOS:
2491                case TacticalLines.OBJ:
2492                case TacticalLines.NAI:
2493                case TacticalLines.TAI:
2494                case TacticalLines.BASE_CAMP_REVD:
2495                case TacticalLines.GUERILLA_BASE_REVD:
2496                case TacticalLines.ASSY:
2497                case TacticalLines.EA:
2498                case TacticalLines.DZ:
2499                case TacticalLines.EZ:
2500                case TacticalLines.LZ:
2501                case TacticalLines.PZ:
2502                case TacticalLines.AO:
2503                    AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), area, 0, ptCenter, ptCenter, false);
2504                    break;
2505                case TacticalLines.BASE_CAMP:
2506                case TacticalLines.GUERILLA_BASE:
2507                    AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), area, -1 * csFactor, ptCenter, ptCenter, false);
2508                    AddModifier(tg, tg.get_H(), area, 0, ptCenter, ptCenter);
2509                    addDTG(tg, area, 1 * csFactor, 2 * csFactor, ptCenter, ptCenter, metrics);
2510                    addNModifier(tg, g2d);
2511                    addModifierBottomSegment(tg, tg.get_EchelonSymbol());
2512                    break;
2513                case TacticalLines.GENERIC_AREA:
2514                    AddIntegralAreaModifier(tg, tg.get_H() + " " + tg.get_Name(), area, -0.5 * csFactor, ptCenter, ptCenter, false);
2515                    addDTG(tg, area, 0.5 * csFactor, 1.5 * csFactor, ptCenter, ptCenter, metrics);
2516                    addNModifier(tg, g2d);
2517                    break;
2518                case TacticalLines.AIRHEAD:
2519                    GetMBR(tg, ul, ur, lr, ll);
2520                    AddIntegralAreaModifier(tg, label, aboveMiddle, csFactor, ll, lr, false);
2521                    break;
2522                case TacticalLines.AC:
2523                case TacticalLines.LLTR:
2524                case TacticalLines.MRR:
2525                case TacticalLines.SL:
2526                case TacticalLines.TC:
2527                case TacticalLines.SAAFR:
2528                case TacticalLines.SC:
2529                    AddIntegralModifier(tg, "Name: " + tg.get_Name(), aboveMiddle, -7 * csFactor, middleSegment, middleSegment + 1, false);
2530                    AddIntegralModifier(tg, "Width: " + tg.get_AM(), aboveMiddle, -6 * csFactor, middleSegment, middleSegment + 1, false);
2531                    AddIntegralModifier(tg, "Min Alt: " + tg.get_X(), aboveMiddle, -5 * csFactor, middleSegment, middleSegment + 1, false);
2532                    AddIntegralModifier(tg, "Max Alt: " + tg.get_X1(), aboveMiddle, -4 * csFactor, middleSegment, middleSegment + 1, false);
2533                    AddIntegralModifier(tg, "DTG Start: " + tg.get_DTG(), aboveMiddle, -3 * csFactor, middleSegment, middleSegment + 1, false);
2534                    AddIntegralModifier(tg, "DTG End: " + tg.get_DTG1(), aboveMiddle, -2 * csFactor, middleSegment, middleSegment + 1, false);
2535                    AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, 0, middleSegment, middleSegment + 1, false);
2536                    break;
2537                case TacticalLines.BEARING_J:
2538                case TacticalLines.BEARING_RDF:
2539                case TacticalLines.BEARING:
2540                case TacticalLines.ELECTRO:
2541                case TacticalLines.BEARING_EW:
2542                case TacticalLines.ACOUSTIC:
2543                case TacticalLines.ACOUSTIC_AMB:
2544                case TacticalLines.TORPEDO:
2545                case TacticalLines.OPTICAL:
2546                    midPt = lineutility.MidPointDouble(pt0, pt1, 0);
2547                    AddIntegralAreaModifier(tg, label, aboveMiddle, 0, midPt, midPt, true);
2548                    pt3 = lineutility.ExtendDirectedLine(pt0, pt1, pt1, 3, font.getSize() / 2.0);
2549                    AddIntegralAreaModifier(tg, tg.get_H(), aboveMiddle, 1, pt3, pt3, true);
2550                    break;
2551                case TacticalLines.ACA:
2552                    AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), area, -3 * csFactor, ptCenter, ptCenter, false);
2553                    AddIntegralAreaModifier(tg, tg.get_T1(), area, -2 * csFactor, ptCenter, ptCenter, false);
2554                    AddIntegralAreaModifier(tg, "MIN ALT: " + tg.get_X(), area, -1 * csFactor, ptCenter, ptCenter, false, "H");
2555                    AddIntegralAreaModifier(tg, "MAX ALT: " + tg.get_X1(), area, 0, ptCenter, ptCenter, false, "H1");
2556                    AddIntegralAreaModifier(tg, tg.get_Location(), area, 1 * csFactor, ptCenter, ptCenter, false, "H2");
2557                    addDTG(tg, area, 2 * csFactor, 3 * csFactor, ptCenter, ptCenter, metrics);
2558                    break;
2559                case TacticalLines.MFP:
2560                    pt0 = tg.Pixels.get(middleSegment);
2561                    pt1 = tg.Pixels.get(middleSegment + 1);
2562                    AddIntegralModifier(tg, label, aboveMiddle, 0, middleSegment, middleSegment + 1, true);
2563                    AddIntegralModifier(tg, tg.get_DTG() + WDash, aboveEnd, 1 * csFactor, 0, 1, false);
2564                    AddIntegralModifier(tg, tg.get_DTG1(), aboveEnd, 2 * csFactor, 0, 1, false);
2565                    break;
2566                case TacticalLines.LINTGT:
2567                    AddIntegralModifier(tg, ap, aboveMiddle, -0.7 * csFactor, middleSegment, middleSegment + 1, false);
2568                    break;
2569                case TacticalLines.LINTGTS:
2570                    AddIntegralModifier(tg, ap, aboveMiddle, -0.7 * csFactor, middleSegment, middleSegment + 1, false);
2571                    AddIntegralModifier(tg, label, aboveMiddle, 0.7 * csFactor, middleSegment, middleSegment + 1, false);
2572                    break;
2573                case TacticalLines.FPF:
2574                    AddIntegralModifier(tg, ap, aboveMiddle, -0.7 * csFactor, 0, 1, false);
2575                    AddIntegralModifier(tg, label, aboveMiddle, .7 * csFactor, 0, 1, false);
2576                    AddIntegralModifier(tg, tg.get_T1(), aboveMiddle, 1.7 * csFactor, 0, 1, false);
2577                    AddIntegralModifier(tg, v, aboveMiddle, 2.7 * csFactor, 0, 1, false);
2578                    break;
2579                case TacticalLines.AT:
2580                    AddIntegralAreaModifier(tg, ap, area, 0, ptCenter, ptCenter, false);
2581                    break;
2582                case TacticalLines.RECTANGULAR:
2583                case TacticalLines.CIRCULAR:
2584                    AddIntegralAreaModifier(tg, ap, area, 0, pt0, pt0, false);
2585                    break;
2586                case TacticalLines.RECTANGULAR_TARGET:
2587                    stringWidth = metrics.stringWidth(tg.get_Name());
2588                    POINT2 offsetCenterPoint = new POINT2(ptCenter.x + ((double) stringWidth) / 2.0, ptCenter.y);
2589                    AddIntegralAreaModifier(tg, tg.get_Name(), area, -1 * csFactor, offsetCenterPoint, offsetCenterPoint, false);
2590                    break;
2591                case TacticalLines.SMOKE:
2592                    AddIntegralAreaModifier(tg, ap, area, -csFactor, ptCenter, ptCenter, false);
2593                    AddIntegralAreaModifier(tg, label, area, 0, ptCenter, ptCenter, false);
2594                    addDTG(tg, area, 1 * csFactor, 2 * csFactor, ptCenter, ptCenter, metrics);
2595                    break;
2596                case TacticalLines.LINE:
2597                    AddIntegralModifier(tg, tg.get_Name(), aboveMiddle, csFactor, middleSegment, middleSegment + 1, false);
2598                    break;
2599                case TacticalLines.MINED:
2600                    if (tg.isHostile()) {
2601                        pt1 = lineutility.MidPointDouble(pt0, pt1, 0);
2602                        AddIntegralAreaModifier(tg, tg.get_N(), aboveMiddle, 0, pt0, pt1, true);
2603                        if (middleSegment != 0) {
2604                            pt0 = tg.Pixels.get(middleSegment);
2605                            pt1 = tg.Pixels.get(middleSegment + 1);
2606                            pt1 = lineutility.MidPointDouble(pt0, pt1, 0);
2607                            AddIntegralAreaModifier(tg, tg.get_N(), aboveMiddle, 0, pt0, pt1, true);
2608                        }
2609                    }
2610                    GetMBR(tg, ul, ur, lr, ll);
2611                    AddIntegralAreaModifier(tg, tg.get_H(), aboveMiddle, -1.5 * csFactor, ul, ur, false);
2612                    AddIntegralAreaModifier(tg, tg.get_DTG(), aboveMiddle, 1.5 * csFactor, ll, lr, false);
2613                    addModifierOnLine("M", tg, g2d);
2614                    AddIntegralAreaModifier(tg, getImageModifier(tg), areaImage, 0, ptCenter, ptCenter, false);
2615                    break;
2616                case TacticalLines.FENCED:
2617                    if (tg.isHostile()) {
2618                        pt1 = lineutility.MidPointDouble(pt0, pt1, 0);
2619                        AddIntegralAreaModifier(tg, tg.get_N(), aboveMiddle, 0, pt0, pt1, true);
2620                        if (middleSegment != 0) {
2621                            pt0 = tg.Pixels.get(middleSegment);
2622                            pt1 = tg.Pixels.get(middleSegment + 1);
2623                            pt1 = lineutility.MidPointDouble(pt0, pt1, 0);
2624                            AddIntegralAreaModifier(tg, tg.get_N(), aboveMiddle, 0, pt0, pt1, true);
2625                        }
2626                    }
2627                    addModifierOnLine("M", tg, g2d);
2628                    AddIntegralAreaModifier(tg, getImageModifier(tg), areaImage, 0, ptCenter, ptCenter, false);
2629                    break;
2630                case TacticalLines.ASLTXING:
2631                    if (tg.Pixels.get(1).y > tg.Pixels.get(0).y) {
2632                        pt0 = tg.Pixels.get(1);
2633                        pt1 = tg.Pixels.get(3);
2634                        pt2 = tg.Pixels.get(0);
2635                        pt3 = tg.Pixels.get(2);
2636                    } else {
2637                        pt0 = tg.Pixels.get(0);
2638                        pt1 = tg.Pixels.get(2);
2639                        pt2 = tg.Pixels.get(1);
2640                        pt3 = tg.Pixels.get(3);
2641                    }
2642                    pt2 = lineutility.ExtendAlongLineDouble2(pt0, pt2, -20);
2643                    pt3 = lineutility.ExtendAlongLineDouble2(pt1, pt3, -20);
2644                    addDTG(tg, aboveMiddle, 0, csFactor, pt2, pt3, metrics);
2645                    break;
2646                case TacticalLines.SERIES:
2647                case TacticalLines.DRCL:
2648                    addModifierTopSegment(tg, tg.get_Name());
2649                    break;
2650                case TacticalLines.STRIKWARN:
2651                    AddIntegralModifier(tg, "1", aboveMiddle, 0, index, index + 1, true);
2652                    AddIntegralModifier(tg, "2", aboveMiddle, 0, size/2, size/2 + 1, true);
2653                    break;
2654                case TacticalLines.SCREEN:
2655                case TacticalLines.COVER:
2656                case TacticalLines.GUARD:
2657                    if (tg.Pixels.size() == 4) {
2658                        pt1 = new POINT2(tg.Pixels.get(1));
2659                        pt2 = new POINT2(tg.Pixels.get(2));
2660                        AddIntegralAreaModifier(tg, label, area, 0, pt1, pt1, true);
2661                        AddIntegralAreaModifier(tg, label, area, 0, pt2, pt2, true);
2662                    } else {
2663                        stringHeight = (int) (0.5 * (double) font.getSize());
2664                        dAngle0 = Math.atan2(tg.Pixels.get(0).y - tg.Pixels.get(1).y, tg.Pixels.get(0).x - tg.Pixels.get(1).x);
2665                        dAngle1 = Math.atan2(tg.Pixels.get(0).y - tg.Pixels.get(2).y, tg.Pixels.get(0).x - tg.Pixels.get(2).x);
2666                        pt0 = new POINT2(tg.Pixels.get(0));
2667                        pt0.x -= 30 * Math.cos(dAngle0);
2668                        pt0.y -= 30 * Math.sin(dAngle0) + stringHeight;
2669                        pt1 = new POINT2(tg.Pixels.get(0));
2670                        pt1.x -= 30 * Math.cos(dAngle1);
2671                        pt1.y -= 30 * Math.sin(dAngle1) + stringHeight;
2672                        AddIntegralAreaModifier(tg, label, area, 0, pt0, pt0, true);
2673                        AddIntegralAreaModifier(tg, label, area, 0, pt1, pt1, true);
2674                    }
2675                    break;
2676                case TacticalLines.MSR_ONEWAY:
2677                case TacticalLines.ASR_ONEWAY:
2678                case TacticalLines.ROUTE_ONEWAY:
2679                case TacticalLines.MSR_TWOWAY:
2680                case TacticalLines.ASR_TWOWAY:
2681                case TacticalLines.MSR_ALT:
2682                case TacticalLines.ASR_ALT:
2683                case TacticalLines.ROUTE_ALT:
2684                    stringWidth = (int) (1.5 * (double) metrics.stringWidth(label + TSpace + tg.get_Name()));
2685                    double arrowOffset = 10 * DPIScaleFactor;
2686                    if (linetype == TacticalLines.MSR_TWOWAY || linetype == TacticalLines.ASR_TWOWAY)
2687                        arrowOffset = 25 * DPIScaleFactor;
2688                    boolean isAlt = linetype == TacticalLines.MSR_ALT || linetype == TacticalLines.ASR_ALT || linetype == TacticalLines.ROUTE_ALT;
2689                    if (isAlt) {
2690                        stringWidth2 = (int) (1.5 * (double) metrics.stringWidth("ALT"));
2691                        if (stringWidth2 > stringWidth) {
2692                            stringWidth = stringWidth2;
2693                        }
2694                    }
2695
2696                    foundSegment = false;
2697                    //acevedo - 11/30/2017 - adding option to render only 2 labels.
2698                    if (RendererSettings.getInstance().getTwoLabelOnly() == false) {
2699                        for (j = 0; j < tg.Pixels.size() - 1; j++) {
2700                            pt0 = tg.Pixels.get(j);
2701                            pt1 = tg.Pixels.get(j + 1);
2702                            dist = lineutility.CalcDistanceDouble(pt0, pt1);
2703                            int arrowSide = arraysupport.SupplyRouteArrowSide(pt0, pt1);
2704                            if (dist < stringWidth) {
2705                                continue;
2706                            } else {
2707                                if (arrowSide == 1 || arrowSide == 2) {
2708                                    // Shift points to account for arrow shift with DPI
2709                                    pt0 = lineutility.ExtendDirectedLine(pt1, pt0, pt0, arrowSide, arrowOffset);
2710                                    pt1 = lineutility.ExtendDirectedLine(pt1, pt0, pt1, arrowSide, arrowOffset);
2711                                    AddModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -1.7 * csFactor, pt0, pt1);
2712                                    if (isAlt)
2713                                        AddModifier(tg, "ALT", aboveMiddle, 0, pt0, pt1);
2714                                } else {
2715                                    AddModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1);
2716                                    if (isAlt) {
2717                                        pt0 = lineutility.ExtendDirectedLine(pt1, pt0, pt0, arrowSide, arrowOffset);
2718                                        pt1 = lineutility.ExtendDirectedLine(pt1, pt0, pt1, arrowSide, arrowOffset);
2719                                        AddModifier(tg, "ALT", aboveMiddle, 0, pt0, pt1);
2720                                    }
2721                                }
2722                                foundSegment = true;
2723                            }
2724                        }
2725                        if (foundSegment == false) {
2726                            pt0 = tg.Pixels.get(middleSegment);
2727                            pt1 = tg.Pixels.get(middleSegment + 1);
2728                            int arrowSide = arraysupport.SupplyRouteArrowSide(pt0, pt1);
2729                            if (arrowSide == 1 || arrowSide == 2) {
2730                                // Shift points to account for arrow shift with DPI
2731                                pt0 = lineutility.ExtendDirectedLine(pt1, pt0, pt0, arrowSide, arrowOffset);
2732                                pt1 = lineutility.ExtendDirectedLine(pt1, pt0, pt1, arrowSide, arrowOffset);
2733                                AddModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -1.7 * csFactor, pt0, pt1);
2734                                if (isAlt)
2735                                    AddModifier(tg, "ALT", aboveMiddle, 0, pt0, pt1);
2736                            } else {
2737                                AddModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1);
2738                                if (isAlt) {
2739                                    pt0 = lineutility.ExtendDirectedLine(pt1, pt0, pt0, arrowSide, arrowOffset);
2740                                    pt1 = lineutility.ExtendDirectedLine(pt1, pt0, pt1, arrowSide, arrowOffset);
2741                                    AddModifier(tg, "ALT", aboveMiddle, 0, pt0, pt1);
2742                                }
2743                            }
2744                        }
2745                    }
2746                    else {
2747                        // 2 labels one to the north and the other to the south of graphic.
2748                        northestPtIndex = 0;
2749                        northestPt = tg.Pixels.get(northestPtIndex);
2750                        southestPtIndex = 0;
2751                        southestPt = tg.Pixels.get(southestPtIndex);
2752
2753                        for (j = 0; j < tg.Pixels.size() - 1; j++) {
2754                            pt0 = tg.Pixels.get(j);
2755                            if (pt0.y >= northestPt.y) {
2756                                northestPt = pt0;
2757                                northestPtIndex = j;
2758                            }
2759                            if (pt0.y <= southestPt.y) {
2760                                southestPt = pt0;
2761                                southestPtIndex = j;
2762                            }
2763                        }
2764
2765                        AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -1.7 * csFactor, northestPtIndex, northestPtIndex + 1, false);
2766                        if (isAlt)
2767                            AddIntegralModifier(tg, "ALT", aboveMiddle, -0.7 * csFactor, northestPtIndex, northestPtIndex + 1, false);
2768
2769                        if (northestPtIndex != southestPtIndex) {
2770                            AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -1.7 * csFactor, southestPtIndex, southestPtIndex + 1, false);
2771                            if (isAlt)
2772                                AddIntegralModifier(tg, "ALT", aboveMiddle, -0.7 * csFactor, southestPtIndex, southestPtIndex + 1, false);
2773                        }
2774                    }//else
2775                    break;
2776                case TacticalLines.DHA_REVD:
2777                    AddIntegralAreaModifier(tg, "DETAINEE", area, -1.5 * csFactor, ptCenter, ptCenter, false);
2778                    AddIntegralAreaModifier(tg, "HOLDING", area, -0.5 * csFactor, ptCenter, ptCenter, false);
2779                    AddIntegralAreaModifier(tg, "AREA", area, 0.5 * csFactor, ptCenter, ptCenter, false);
2780                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 1.5 * csFactor, ptCenter, ptCenter, false);
2781                    break;
2782                case TacticalLines.EPW:
2783                    AddIntegralAreaModifier(tg, "EPW", area, -1.5 * csFactor, ptCenter, ptCenter, false);
2784                    AddIntegralAreaModifier(tg, "HOLDING", area, -0.5 * csFactor, ptCenter, ptCenter, false);
2785                    AddIntegralAreaModifier(tg, "AREA", area, 0.5 * csFactor, ptCenter, ptCenter, false);
2786                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 1.5 * csFactor, ptCenter, ptCenter, false);
2787                    break;
2788                case TacticalLines.UXO:
2789                    addModifierOnLine("UXO", tg, g2d);
2790                    break;
2791                case TacticalLines.GENERAL:
2792                    addNModifier(tg, g2d);
2793                    break;
2794                case TacticalLines.DHA:
2795                case TacticalLines.FARP:
2796                    AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, false);
2797                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 0.5 * csFactor, ptCenter, ptCenter, false);
2798                    break;
2799                case TacticalLines.BSA:
2800                case TacticalLines.DSA:
2801                case TacticalLines.CSA:
2802                case TacticalLines.RSA:
2803                    AddIntegralAreaModifier(tg, label, area, 0, ptCenter, ptCenter, false);
2804                    break;
2805                case TacticalLines.RHA:
2806                    AddIntegralAreaModifier(tg, "REFUGEE", area, -1.5 * csFactor, ptCenter, ptCenter, false);
2807                    AddIntegralAreaModifier(tg, "HOLDING", area, -0.5 * csFactor, ptCenter, ptCenter, false);
2808                    AddIntegralAreaModifier(tg, "AREA", area, 0.5 * csFactor, ptCenter, ptCenter, false);
2809                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 1.5 * csFactor, ptCenter, ptCenter, false);
2810                    break;
2811                case TacticalLines.MSR:
2812                case TacticalLines.ASR:
2813                case TacticalLines.ROUTE:
2814                    //AddIntegralModifier(tg, label + tg.get_Name(), aboveMiddle, -1*csFactor, middleSegment, middleSegment + 1,false);
2815                    foundSegment = false;
2816                    //acevedo - 11/30/2017 - adding option to render only 2 labels.
2817                    if (RendererSettings.getInstance().getTwoLabelOnly() == false) {
2818                        for (j = 0; j < tg.Pixels.size() - 1; j++) {
2819                            pt0 = tg.Pixels.get(j);
2820                            pt1 = tg.Pixels.get(j + 1);
2821                            stringWidth = (int) (1.5 * (double) metrics.stringWidth(label + TSpace + tg.get_Name()));
2822                            dist = lineutility.CalcDistanceDouble(pt0, pt1);
2823                            if (dist < stringWidth) {
2824                                continue;
2825                            } else {
2826                                AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -1 * csFactor, j, j + 1, false);
2827                                foundSegment = true;
2828                            }
2829                        }
2830                        if (foundSegment == false) {
2831                            AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -1 * csFactor, middleSegment, middleSegment + 1, false);
2832                        }
2833                    }
2834                    else {
2835                        // 2 labels one to the north and the other to the south of graphic.
2836                        for (j = 0; j < tg.Pixels.size()  ; j++) {
2837                            pt0 = tg.Pixels.get(j);
2838
2839                            if (northestPt == null)
2840                            {
2841                                northestPt = pt0;
2842                                northestPtIndex = j;
2843                            }
2844                            if (southestPt == null)
2845                            {
2846                                southestPt = pt0;
2847                                southestPtIndex = j;
2848                            }
2849                            if (pt0.y >= northestPt.y)
2850                            {
2851                                northestPt = pt0;
2852                                northestPtIndex = j;
2853                            }
2854
2855                            if (pt0.y <= southestPt.y)
2856                            {
2857                                southestPt = pt0;
2858                                southestPtIndex = j;
2859                            }
2860                        }//for
2861                        middleSegment = northestPtIndex;
2862                        middleSegment2 = southestPtIndex;
2863
2864                        if (middleSegment  == tg.Pixels.size() -1) {
2865                            middleSegment-=1;
2866                        }
2867                        if (middleSegment2  == tg.Pixels.size() -1) {
2868                            middleSegment2-=1;
2869                        }
2870                        if (middleSegment == middleSegment2) {
2871                            middleSegment2-=1;
2872                        }
2873
2874                       // if (middleSegment != middleSegment2) {
2875                        AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, 0, middleSegment, middleSegment + 1, false);
2876                        //}
2877                        AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, 0, middleSegment2, middleSegment2 + 1, false);
2878
2879                    }//else
2880                    break;
2881                case TacticalLines.GAP:
2882                    if (tg.Pixels.get(1).y > tg.Pixels.get(0).y) {
2883                        pt0 = tg.Pixels.get(1);
2884                        pt1 = tg.Pixels.get(3);
2885                        pt2 = tg.Pixels.get(0);
2886                        pt3 = tg.Pixels.get(2);
2887                    } else {
2888                        pt0 = tg.Pixels.get(0);
2889                        pt1 = tg.Pixels.get(2);
2890                        pt2 = tg.Pixels.get(1);
2891                        pt3 = tg.Pixels.get(3);
2892                    }
2893                    pt2 = lineutility.ExtendAlongLineDouble2(pt0, pt2, -20);
2894                    pt3 = lineutility.ExtendAlongLineDouble2(pt1, pt3, -20);
2895                    AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, 0, pt0, pt1, false);
2896                    addDTG(tg, aboveMiddle, 0, csFactor, pt2, pt3, metrics);
2897                    break;
2898                case TacticalLines.BIO:
2899                case TacticalLines.CHEM:
2900                case TacticalLines.NUC:
2901                case TacticalLines.RAD:
2902                    AddIntegralAreaModifier(tg, getImageModifier(tg), areaImage, 0, ptCenter, ptCenter, false);
2903                    break;
2904                case TacticalLines.ANCHORAGE_LINE:
2905                    AddIntegralAreaModifier(tg, getImageModifier(tg), aboveMiddle, -0.15 * csFactor, tg.Pixels.get(middleSegment), tg.Pixels.get(middleSegment+1), false);
2906                    break;
2907                case TacticalLines.ANCHORAGE_AREA:
2908                    // Add anchor on segment with lowest midpoint
2909                    y = pt0.y + pt1.y;
2910                    index = 0;
2911                    for (j = 1; j < size - 1; j++) {
2912                        if (y < tg.Pixels.get(j).y + tg.Pixels.get(j + 1).y) {
2913                            index = j;
2914                            y = tg.Pixels.get(index).y + tg.Pixels.get(index + 1).y;
2915                        }
2916                    }
2917                    AddIntegralAreaModifier(tg, getImageModifier(tg), aboveMiddle, -0.25 * csFactor, tg.Pixels.get(index), tg.Pixels.get(index + 1), false);
2918                    break;
2919                case TacticalLines.MINE_LINE:
2920                    AddIntegralAreaModifier(tg, getImageModifier(tg), aboveMiddle, -0.2 * csFactor, tg.Pixels.get(middleSegment), tg.Pixels.get(middleSegment + 1), false);
2921                    if (tg.isHostile()) {
2922                        AddIntegralAreaModifier(tg, tg.get_N(), toEnd, 0.0, pt0, pt1, false);
2923                        AddIntegralAreaModifier(tg, tg.get_N(), toEnd, 0.0, ptLast, ptNextToLast, false);
2924                    }
2925                    break;
2926                case TacticalLines.DEPICT:
2927                    GetMBR(tg, ul, ur, lr, ll);
2928                    addNModifier(tg, g2d);
2929                    AddIntegralAreaModifier(tg, getImageModifier(tg), areaImage, 0, ptCenter, ptCenter, false);
2930                    break;
2931                case TacticalLines.FFA:
2932                case TacticalLines.RFA:
2933                case TacticalLines.NFA:
2934                    AddIntegralAreaModifier(tg, label, area, -1 * csFactor, ptCenter, ptCenter, false);
2935                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, ptCenter, ptCenter, false);
2936                    addDTG(tg, area, 1 * csFactor, 2 * csFactor, ptCenter, ptCenter, metrics);
2937                    break;
2938                case TacticalLines.PAA:
2939                    addModifierOnLine("PAA", tg, g2d);
2940                    AddIntegralAreaModifier(tg, tg.get_Name(), area, -0.5 * csFactor, ptCenter, ptCenter, false);
2941                    addDTG(tg, area, 0.5 * csFactor, 1.5 * csFactor, ptCenter, ptCenter, metrics);
2942                    break;
2943                case TacticalLines.FSA:
2944                    AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), area, -0.5 * csFactor, ptCenter, ptCenter, false);
2945                    addDTG(tg, area, 0.5 * csFactor, 1.5 * csFactor, ptCenter, ptCenter, metrics);
2946                    break;
2947                case TacticalLines.ATI:
2948                case TacticalLines.CFFZ:
2949                case TacticalLines.CFZ:
2950                case TacticalLines.TBA:
2951                case TacticalLines.TVAR:
2952                case TacticalLines.ZOR:
2953                case TacticalLines.DA:
2954                case TacticalLines.SENSOR:
2955                case TacticalLines.CENSOR:
2956                case TacticalLines.KILLBOXBLUE:
2957                case TacticalLines.KILLBOXPURPLE:
2958                    AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, false);
2959                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 0.5 * csFactor, ptCenter, ptCenter, false);
2960                    GetMBR(tg, ul, ur, lr, ll);
2961                    POINT2 ptLeft = ul;
2962                    POINT2 ptRight = ur;
2963                    if (tg.get_Client().equalsIgnoreCase("ge")) {
2964                        ptLeft.x -= font.getSize() / 2;
2965                        ptRight.x -= font.getSize() / 2;
2966                    }
2967                    AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, toEnd, 0.5 * csFactor, ptLeft, ptRight, false, "W");
2968                    AddIntegralAreaModifier(tg, tg.get_DTG1(), toEnd, 1.5 * csFactor, ptLeft, ptRight, false, "W1");
2969                    break;
2970                case TacticalLines.BATTLE:
2971                case TacticalLines.STRONG:
2972                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, ptCenter, ptCenter, false);
2973                    addModifierBottomSegment(tg, tg.get_EchelonSymbol());
2974                    break;
2975                case TacticalLines.PNO:
2976                    AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), area, 0, ptCenter, ptCenter, false);
2977                    addModifierBottomSegment(tg, tg.get_EchelonSymbol());
2978                    addNModifier(tg, g2d);
2979                    break;
2980                case TacticalLines.WFZ:
2981                    AddIntegralAreaModifier(tg, label, area, -1.5 * csFactor, ptCenter, ptCenter, true);
2982                    AddIntegralAreaModifier(tg, tg.get_Name(), area, -0.5 * csFactor, ptCenter, ptCenter, true);
2983                    AddIntegralAreaModifier(tg, "TIME FROM: " + tg.get_DTG(), area, 0.5 * csFactor, ptCenter, ptCenter, true, "W");
2984                    AddIntegralAreaModifier(tg, "TIME TO: " + tg.get_DTG1(), area, 1.5 * csFactor, ptCenter, ptCenter, true, "W1");
2985                    break;
2986                case TacticalLines.OBSFAREA:
2987                    AddIntegralAreaModifier(tg, label, area, -1.5 * csFactor, ptCenter, ptCenter, false);
2988                    AddIntegralAreaModifier(tg, tg.get_Name(), area, -0.5 * csFactor, ptCenter, ptCenter, false);
2989                    AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, area, 0.5 * csFactor, ptCenter, ptCenter, false, "W");
2990                    AddIntegralAreaModifier(tg, tg.get_DTG1(), area, 1.5 * csFactor, ptCenter, ptCenter, false, "W1");
2991                    break;
2992                case TacticalLines.OBSAREA:
2993                    AddIntegralAreaModifier(tg, tg.get_Name(), area, -1 * csFactor, ptCenter, ptCenter, true);
2994                    AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, area, 0, ptCenter, ptCenter, true, "W");
2995                    AddIntegralAreaModifier(tg, tg.get_DTG1(), area, 1 * csFactor, ptCenter, ptCenter, true, "W1");
2996                    break;
2997                case TacticalLines.ROZ:
2998                case TacticalLines.AARROZ:
2999                case TacticalLines.UAROZ:
3000                case TacticalLines.WEZ:
3001                case TacticalLines.FEZ:
3002                case TacticalLines.JEZ:
3003                case TacticalLines.FAADZ:
3004                case TacticalLines.HIDACZ:
3005                case TacticalLines.MEZ:
3006                case TacticalLines.LOMEZ:
3007                case TacticalLines.HIMEZ:
3008                    AddIntegralAreaModifier(tg, label, area, -2.5, ptCenter, ptCenter, false, "");
3009                    AddIntegralAreaModifier(tg, tg.get_Name(), area, -1.5, ptCenter, ptCenter, false, "T");
3010                    AddIntegralAreaModifier(tg, "MIN ALT: " + tg.get_X(), area, -0.5, ptCenter, ptCenter, false, "H");
3011                    AddIntegralAreaModifier(tg, "MAX ALT: " + tg.get_X1(), area, 0.5, ptCenter, ptCenter, false, "H1");
3012                    AddIntegralAreaModifier(tg, "TIME FROM: " + tg.get_DTG(), area, 1.5, ptCenter, ptCenter, false, "W");
3013                    AddIntegralAreaModifier(tg, "TIME TO: " + tg.get_DTG1(), area, 2.5, ptCenter, ptCenter, false, "W1");
3014                    break;
3015                case TacticalLines.ENCIRCLE:
3016                    if (tg.isHostile()) {
3017                        AddIntegralModifier(tg, tg.get_N(), aboveMiddle, 0, 0, 1, true);
3018                        AddIntegralModifier(tg, tg.get_N(), aboveMiddle, 0, middleSegment, middleSegment + 1, true);
3019                    }
3020                    break;
3021                case TacticalLines.LAA:
3022                    AddIntegralAreaModifier(tg, getImageModifier(tg), areaImage, 0, ptCenter, ptCenter, false);
3023                    AddIntegralAreaModifier(tg, label, area, -1 * csFactor, ptCenter, ptCenter, false);
3024                    break;
3025                case TacticalLines.BOUNDARY:
3026                    if (clipRect != null) {
3027                        AddBoundaryModifiers(tg, g2d, clipRect);
3028                    } else {
3029                        AddBoundaryModifiers(tg, g2d, clipArray);
3030                    }
3031                    break;
3032                case TacticalLines.CFL:
3033                    stringWidth = (int) ((double) metrics.stringWidth(label + TSpace + tg.get_Name()));
3034                    stringWidth2 = (int) ((double) metrics.stringWidth(tg.get_DTG() + WDash + tg.get_DTG1()));
3035                    if (stringWidth2 > stringWidth) {
3036                        stringWidth = stringWidth2;
3037                    }
3038                    pt0 = new POINT2(tg.Pixels.get(middleSegment));
3039                    pt1 = new POINT2(tg.Pixels.get(middleSegment + 1));
3040                    getPixelsMiddleSegment(tg, stringWidth, pt0, pt1);
3041                    AddModifier2(tg, label + TSpace + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
3042                    addDTG(tg, aboveMiddle, 0.7 * csFactor, 1.7 * csFactor, pt0, pt1, metrics);
3043                    break;
3044                case TacticalLines.FLOT:
3045                    if (tg.get_H().equals("1")) {
3046                        label = "LC";
3047                    } else if (tg.get_H().equals("2")) {
3048                        label = "";
3049                    }
3050                    AddIntegralAreaModifier(tg, label, toEnd, 0, pt0, pt1, false);
3051                    AddIntegralAreaModifier(tg, label, toEnd, 0, ptLast, ptNextToLast, false);
3052
3053                    if (tg.isHostile()) {
3054                        AddIntegralAreaModifier(tg, tg.get_N(), toEnd, -1 * csFactor, pt0, pt1, false);
3055                        AddIntegralAreaModifier(tg, tg.get_N(), toEnd, -1 * csFactor, ptLast, ptNextToLast, false);
3056                    }
3057                    break;
3058                case TacticalLines.LC:
3059                    double shiftFactor = 1d;
3060                    if (shiftLines) {
3061                        shiftFactor = 0.5d;
3062                    }
3063                    if (tg.isHostile()) {
3064                        if (pt0.x < pt1.x) {
3065                            TLineFactor = -shiftFactor;//was -1
3066                        } else {
3067                            TLineFactor = shiftFactor;//was 1
3068                        }
3069                        AddIntegralAreaModifier(tg, tg.get_N(), toEnd, TLineFactor, pt0, pt1, false);
3070                        if (ptNextToLast.x < ptLast.x) {
3071                            TLineFactor = -shiftFactor;//was -1
3072                        } else {
3073                            TLineFactor = shiftFactor;//was 1
3074                        }
3075                        AddIntegralAreaModifier(tg, tg.get_N(), toEnd, TLineFactor, ptLast, ptNextToLast, false);
3076                    }
3077                    AddIntegralAreaModifier(tg, label, toEnd, 0, pt0, pt1, false);
3078                    AddIntegralAreaModifier(tg, label, toEnd, 0, ptLast, ptNextToLast, false);
3079                    break;
3080                case TacticalLines.CATK:
3081                    AddIntegralModifier(tg, label, aboveMiddle, 0, 1, 0, false);
3082                    break;
3083                case TacticalLines.CATKBYFIRE:
3084                    stringWidth = (int) (1.5 * (double) metrics.stringWidth(label));
3085                    pt2 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
3086                    AddModifier2(tg, label, aboveMiddle, 0, pt1, pt2, false);
3087                    break;
3088                case TacticalLines.IL:
3089                    AddIntegralModifier(tg, tg.get_Name(), aboveMiddle, 0, 1, 0, false);
3090                    break;
3091                case TacticalLines.RETIRE:
3092                case TacticalLines.WITHDRAW:
3093                case TacticalLines.WDRAWUP:
3094                case TacticalLines.FPOL:
3095                case TacticalLines.RPOL:
3096                    AddIntegralModifier(tg, label, aboveMiddle, 0, 0, 1, true);
3097                    break;
3098                case TacticalLines.RIP:
3099                case TacticalLines.BOMB:
3100                case TacticalLines.TGMF:
3101                    AddIntegralAreaModifier(tg, label, area, 0, ptCenter, ptCenter, true);
3102                    break;
3103                case TacticalLines.MSDZ:
3104                    AddIntegralAreaModifier(tg, "1", area, 0, pt1, pt1, true);
3105                    AddIntegralAreaModifier(tg, "2", area, 0, pt2, pt2, true);
3106                    AddIntegralAreaModifier(tg, "3", area, 0, pt3, pt3, true);
3107                    break;
3108                case TacticalLines.DELAY:
3109                    AddIntegralModifier(tg, tg.get_DTG(), aboveMiddle, -1 * csFactor, 0, 1, false);
3110                    AddIntegralModifier(tg, label, aboveMiddle, 0, 0, 1, true);
3111                    break;
3112                case TacticalLines.GENERIC_LINE:
3113                    pt0 = tg.Pixels.get(0);
3114                    pt1 = tg.Pixels.get(1);
3115                    pt2 = tg.Pixels.get(tg.Pixels.size() - 1);
3116                    pt3 = tg.Pixels.get(tg.Pixels.size() - 2);
3117                    dist = lineutility.CalcDistanceDouble(pt0, pt1);
3118                    dist2 = lineutility.CalcDistanceDouble(pt2, pt3);
3119                    stringWidth = (int) ((double) metrics.stringWidth(tg.get_H() + " " + tg.get_Name()));
3120                    stringWidth2 = (int) ((double) metrics.stringWidth(tg.get_DTG()));
3121                    if (stringWidth2 > stringWidth) {
3122                        stringWidth = stringWidth2;
3123                    }
3124
3125                    if (tg.Pixels.size() == 2) //one segment
3126                    {
3127                        pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
3128                        AddModifier2(tg, tg.get_H() + " " + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
3129                        AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
3130                        AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
3131                        if (dist > 3.5 * stringWidth)//was 28stringwidth+5
3132                        {
3133                            pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
3134                            pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
3135                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
3136                            AddModifier2(tg, tg.get_H() + " " + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
3137                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
3138                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
3139                        }
3140                    } else //more than one semgent
3141                    {
3142                        double dist3 = lineutility.CalcDistanceDouble(pt0, pt2);
3143                        if (dist > stringWidth + 5 || dist >= dist2 || dist3 > stringWidth + 5) {
3144                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
3145                            AddModifier2(tg, tg.get_H() + " " + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
3146                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
3147                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
3148                        }
3149                        if (dist2 > stringWidth + 5 || dist2 > dist || dist3 > stringWidth + 5) {
3150                            pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
3151                            pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
3152                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
3153                            AddModifier2(tg, tg.get_H() + " " + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
3154                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
3155                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
3156                        }
3157                    }
3158                    break;
3159                default:
3160                    break;
3161            }
3162            scaleModifiers(tg);
3163            tg.Pixels = origPoints;
3164        } catch (Exception exc) {
3165            ErrorLogger.LogException(_className, "AddModifiersGeo",
3166                    new RendererException("Failed inside AddModifiersGeo", exc));
3167        }
3168
3169    }
3170
3171    /**
3172     * RFA, NFA, FFA need these for line spacing
3173     *
3174     * @param tg
3175     * @return
3176     */
3177    private static int getRFALines(TGLight tg) {
3178        int lines = 1;
3179        try {
3180            if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
3181                lines++;
3182            }
3183            if (tg.get_DTG() != null && !tg.get_DTG().isEmpty()) {
3184                lines++;
3185            } else if (tg.get_DTG1() != null && !tg.get_DTG1().isEmpty()) {
3186                lines++;
3187            }
3188        } catch (Exception exc) {
3189            ErrorLogger.LogException(_className, "AddModifiers",
3190                    new RendererException("Failed inside AddModifiers", exc));
3191        }
3192        return lines;
3193    }
3194
3195    /**
3196     * Added sector range fan modifiers based using the calculated orientation
3197     * indicator points
3198     *
3199     * @param tg
3200     * @param converter
3201     * @return
3202     */
3203    private static void addSectorModifiers(TGLight tg, IPointConversion converter) {
3204        try {
3205            if (tg.get_LineType() == TacticalLines.RANGE_FAN_SECTOR) {
3206                ArrayList<Double> AM = new ArrayList<>();
3207                ArrayList<Double> AN = new ArrayList<>();
3208                //get the number of sectors
3209                String X = tg.get_X();
3210                String[] altitudes = null;
3211                String[] am = tg.get_AM().split(",");
3212                String[] an = tg.get_AN().split(",");
3213                int numSectors = an.length / 2;
3214                //there must be at least one sector
3215                if (numSectors < 1) {
3216                    return;
3217                }
3218                if (!X.isEmpty()) {
3219                    altitudes = X.split(",");
3220                }
3221                try {
3222                    for (String s : am) {
3223                        AM.add(Double.parseDouble(s));
3224                    }
3225                    for (String s : an) {
3226                        AN.add(Double.parseDouble(s));
3227                    }
3228                } catch (NumberFormatException e) {
3229                    return;
3230                }
3231                if (numSectors + 1 > AM.size()) {
3232                    if (Double.parseDouble(am[0]) != 0d) {
3233                        AM.add(0, 0d);
3234                    }
3235                }
3236
3237                int n = tg.Pixels.size();
3238                //pt0 and pt1 are points for the location indicator
3239                POINT2 pt0 = tg.Pixels.get(n - 5);
3240                POINT2 pt1 = tg.Pixels.get(n - 4);
3241                Point2D pt02d = new Point2D.Double(pt0.x, pt0.y);
3242                Point2D pt12d = new Point2D.Double(pt1.x, pt1.y);
3243                pt02d = converter.PixelsToGeo(pt02d);
3244                pt12d = converter.PixelsToGeo(pt12d);
3245                pt0.x = pt02d.getX();
3246                pt0.y = pt02d.getY();
3247                pt1.x = pt12d.getX();
3248                pt1.y = pt12d.getY();
3249                //azimuth of the orientation indicator
3250                double az12 = mdlGeodesic.GetAzimuth(pt0, pt1);
3251
3252                POINT2 pt2 = null;
3253                ArrayList<POINT2> locModifier = new ArrayList();
3254                //diagnostic
3255                POINT2 ptLeft = null, ptRight = null;
3256                ArrayList<POINT2> locAZModifier = new ArrayList();
3257                //end section
3258                Point2D pt22d = null;
3259                double radius = 0;
3260                for (int k = 0; k < numSectors; k++) {
3261                    if (AM.size() < k + 2) {
3262                        break;
3263                    }
3264                    radius = (AM.get(k) + AM.get(k + 1)) / 2;
3265                    pt2 = mdlGeodesic.geodesic_coordinate(pt0, radius, az12);
3266                    //need locModifier in geo pixels
3267                    pt22d = new Point2D.Double(pt2.x, pt2.y);
3268                    pt22d = converter.GeoToPixels(pt22d);
3269                    pt2.x = pt22d.getX();
3270                    pt2.y = pt22d.getY();
3271                    locModifier.add(pt2);
3272                    //diagnostic
3273                    if (tg.get_HideOptionalLabels())
3274                        continue;
3275                    ptLeft = mdlGeodesic.geodesic_coordinate(pt0, radius, AN.get(2 * k));
3276                    //need ptLeft in geo pixels
3277                    pt22d = new Point2D.Double(ptLeft.x, ptLeft.y);
3278                    pt22d = converter.GeoToPixels(pt22d);
3279                    ptLeft.x = pt22d.getX();
3280                    ptLeft.y = pt22d.getY();
3281                    ptRight = mdlGeodesic.geodesic_coordinate(pt0, radius, AN.get(2 * k + 1));
3282                    //need ptRight in geo pixels
3283                    pt22d = new Point2D.Double(ptRight.x, ptRight.y);
3284                    pt22d = converter.GeoToPixels(pt22d);
3285                    ptRight.x = pt22d.getX();
3286                    ptRight.y = pt22d.getY();
3287                    locAZModifier.add(ptLeft);
3288                    locAZModifier.add(ptRight);
3289                    //end section
3290                }
3291                if (altitudes != null) {
3292                    for (int k = 0; k < altitudes.length; k++) {
3293                        if (k >= locModifier.size()) {
3294                            break;
3295                        }
3296                        pt0 = locModifier.get(k);
3297                        AddAreaModifier(tg, "ALT " + altitudes[k], area, 0, pt0, pt0);
3298                    }
3299                }
3300
3301                if (!tg.get_HideOptionalLabels()) {
3302                    for (int k = 0; k < numSectors; k++) {
3303                        pt0 = locModifier.get(k);
3304                        AddAreaModifier(tg, "RG " + AM.get(k + 1), area, -1, pt0, pt0);
3305                        ptLeft = locAZModifier.get(2 * k);
3306                        ptRight = locAZModifier.get(2 * k + 1);
3307                        AddAreaModifier(tg, an[2 * k], area, 0, ptLeft, ptLeft);
3308                        AddAreaModifier(tg, an[2 * k + 1], area, 0, ptRight, ptRight);
3309                    }
3310                }
3311            } else if (tg.get_LineType() == TacticalLines.RADAR_SEARCH) {
3312                // Copies functionality from RANGE_FAN_SECTOR with one sector and different modifiers
3313                String strLeftRightMinMax = tg.get_LRMM();
3314                String[] sector = strLeftRightMinMax.split(",");
3315                double left = Double.parseDouble(sector[0]);
3316                double right = Double.parseDouble(sector[1]);
3317
3318                while (left > 360) {
3319                    left -= 360;
3320                }
3321                while (right > 360) {
3322                    right -= 360;
3323                }
3324                while (left < 0) {
3325                    left += 360;
3326                }
3327                while (right < 0) {
3328                    right += 360;
3329                }
3330
3331                double orientation = 0;
3332                if (left > right) {
3333                    orientation = (left - 360 + right) / 2;
3334                } else {
3335                    orientation = (left + right) / 2;
3336                }
3337
3338                double dist = Double.parseDouble(sector[3]);
3339                double radius = dist * 1.1;
3340
3341                POINT2 pt0 = tg.LatLongs.get(0);
3342                Point2D ptPixels = converter.GeoToPixels(new Point2D.Double(pt0.x, pt0.y));
3343                POINT2 pt0F = new POINT2();
3344                pt0F.x = ptPixels.getX();
3345                pt0F.y = ptPixels.getY();
3346                pt0F.style = pt0.style;
3347
3348                POINT2 pt1 = mdlGeodesic.geodesic_coordinate(pt0, radius, orientation);
3349                ptPixels = converter.GeoToPixels(new Point2D.Double(pt1.x, pt1.y));
3350                POINT2 pt1F = new POINT2();
3351                pt1F.x = ptPixels.getX();
3352                pt1F.y = ptPixels.getY();
3353                pt1F.style = pt1.style;
3354
3355                dist = lineutility.CalcDistanceDouble(pt0F, pt1F);
3356                double base = 10;
3357                if (dist < 100) {
3358                    base = dist / 10;
3359                }
3360                if (base < 5) {
3361                    base = 5;
3362                }
3363                double basex2 = 2 * base;
3364                POINT2 ptTipF = lineutility.ExtendAlongLineDouble(pt0F, pt1F, dist + basex2);  //was 20
3365
3366                pt0 = pt0F;
3367                pt1 = ptTipF;
3368
3369                ArrayList<Double> AM = new ArrayList<>();
3370                String[] am = tg.get_AM().split(",");
3371
3372                for (String s : am) {
3373                    AM.add(Double.parseDouble(s));
3374                }
3375
3376                if (AM.size() < 2) {
3377                    if (Double.parseDouble(am[0]) != 0d) {
3378                        AM.add(0, 0d);
3379                    } else {
3380                        return;
3381                    }
3382                }
3383
3384                Point2D pt02d = new Point2D.Double(pt0.x, pt0.y);
3385                Point2D pt12d = new Point2D.Double(pt1.x, pt1.y);
3386                pt02d = converter.PixelsToGeo(pt02d);
3387                pt12d = converter.PixelsToGeo(pt12d);
3388                pt0.x = pt02d.getX();
3389                pt0.y = pt02d.getY();
3390                pt1.x = pt12d.getX();
3391                pt1.y = pt12d.getY();
3392                double az12 = mdlGeodesic.GetAzimuth(pt0, pt1);
3393
3394                Point2D pt22d = null;
3395
3396                radius = (AM.get(0) + AM.get(1)) / 2;
3397                POINT2 pt2 = mdlGeodesic.geodesic_coordinate(pt0, radius, az12);
3398                pt22d = new Point2D.Double(pt2.x, pt2.y);
3399                pt22d = converter.GeoToPixels(pt22d);
3400                pt2.x = pt22d.getX();
3401                pt2.y = pt22d.getY();
3402                AddAreaModifier(tg, tg.get_Name(), area, -1, pt2, pt2);
3403            }
3404        } catch (Exception exc) {
3405            ErrorLogger.LogException(_className, "addSectorModifiers",
3406                    new RendererException("Failed inside addSectorModifiers", exc));
3407        }
3408    }
3409
3410    /**
3411     * Called by the renderer after tg.Pixels has been filled with the
3412     * calculated points. The modifier path depends on points calculated by
3413     * CELineArray.
3414     *
3415     * @param tg
3416     */
3417    public static void AddModifiers2(TGLight tg, IPointConversion converter) {
3418        try {
3419            if (tg.Pixels == null || tg.Pixels.isEmpty()) {
3420                return;
3421            }
3422            switch (tg.get_LineType()) {
3423                case TacticalLines.CONVOY:
3424                case TacticalLines.HCONVOY:
3425                case TacticalLines.BREACH:
3426                case TacticalLines.BYPASS:
3427                case TacticalLines.CANALIZE:
3428                case TacticalLines.PENETRATE:
3429                case TacticalLines.CLEAR:
3430                case TacticalLines.DISRUPT:
3431                case TacticalLines.FIX:
3432                case TacticalLines.ISOLATE:
3433                case TacticalLines.OCCUPY:
3434                case TacticalLines.RETAIN:
3435                case TacticalLines.SECURE:
3436                case TacticalLines.CONTAIN:
3437                case TacticalLines.SEIZE:
3438                case TacticalLines.CORDONKNOCK:
3439                case TacticalLines.CORDONSEARCH:
3440                case TacticalLines.FOLLA:
3441                case TacticalLines.FOLSP:
3442                case TacticalLines.ACA_RECTANGULAR:
3443                case TacticalLines.ACA_CIRCULAR:
3444                case TacticalLines.RECTANGULAR:
3445                case TacticalLines.CUED_ACQUISITION:
3446                case TacticalLines.CIRCULAR:
3447                case TacticalLines.BDZ:
3448                case TacticalLines.FSA_CIRCULAR:
3449                case TacticalLines.NOTACK:
3450                case TacticalLines.ATI_CIRCULAR:
3451                case TacticalLines.CFFZ_CIRCULAR:
3452                case TacticalLines.SENSOR_CIRCULAR:
3453                case TacticalLines.CENSOR_CIRCULAR:
3454                case TacticalLines.DA_CIRCULAR:
3455                case TacticalLines.CFZ_CIRCULAR:
3456                case TacticalLines.ZOR_CIRCULAR:
3457                case TacticalLines.TBA_CIRCULAR:
3458                case TacticalLines.TVAR_CIRCULAR:
3459                case TacticalLines.FFA_CIRCULAR:
3460                case TacticalLines.NFA_CIRCULAR:
3461                case TacticalLines.RFA_CIRCULAR:
3462                case TacticalLines.KILLBOXBLUE_CIRCULAR:
3463                case TacticalLines.KILLBOXPURPLE_CIRCULAR:
3464                case TacticalLines.BLOCK:
3465                case TacticalLines.FFA_RECTANGULAR:
3466                case TacticalLines.NFA_RECTANGULAR:
3467                case TacticalLines.RFA_RECTANGULAR:
3468                case TacticalLines.KILLBOXBLUE_RECTANGULAR:
3469                case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
3470                case TacticalLines.FSA_RECTANGULAR:
3471                case TacticalLines.SHIP_AOI_RECTANGULAR:
3472                case TacticalLines.DEFENDED_AREA_RECTANGULAR:
3473                case TacticalLines.ATI_RECTANGULAR:
3474                case TacticalLines.CFFZ_RECTANGULAR:
3475                case TacticalLines.SENSOR_RECTANGULAR:
3476                case TacticalLines.CENSOR_RECTANGULAR:
3477                case TacticalLines.DA_RECTANGULAR:
3478                case TacticalLines.CFZ_RECTANGULAR:
3479                case TacticalLines.ZOR_RECTANGULAR:
3480                case TacticalLines.TBA_RECTANGULAR:
3481                case TacticalLines.TVAR_RECTANGULAR:
3482                case TacticalLines.PAA:
3483                case TacticalLines.PAA_RECTANGULAR:
3484                case TacticalLines.RECTANGULAR_TARGET:
3485                case TacticalLines.PAA_CIRCULAR:
3486                case TacticalLines.RANGE_FAN:
3487                case TacticalLines.RANGE_FAN_SECTOR:
3488                case TacticalLines.RADAR_SEARCH:
3489                case TacticalLines.SHIP_AOI_CIRCULAR:
3490                case TacticalLines.MFLANE:
3491                    break;
3492                default:
3493                    return;
3494            }
3495            //end section
3496            ArrayList<POINT2> origPoints = lineutility.getDeepCopy(tg.Pixels);
3497            int n = tg.Pixels.size();
3498            if (tg.modifiers == null) {
3499                tg.modifiers = new ArrayList();
3500            }
3501            Font font = tg.get_Font();
3502            POINT2 ptCenter = null;
3503            double csFactor = 1d;//this will be used for text spacing the 3d map (CommandCight)
3504            //String affiliation=tg.get_Affiliation();
3505            int linetype = tg.get_LineType();
3506            POINT2 pt0 = null, pt1 = null, pt2 = null, pt3 = null;
3507            int j = 0, k = 0;
3508            double dist = 0;
3509            String label = GetCenterLabel(tg);
3510            String[] X = null;
3511            int lastIndex = tg.Pixels.size() - 1;
3512            int nextToLastIndex = 0;
3513            if (tg.Pixels.size() > 1) {
3514                nextToLastIndex = tg.Pixels.size() - 2;
3515            }
3516            POINT2 ptLast = new POINT2(tg.Pixels.get(lastIndex));
3517            POINT2 ptNextToLast = null;
3518            if (tg.Pixels.size() > 1) {
3519                ptNextToLast = new POINT2(tg.Pixels.get(nextToLastIndex));
3520            }
3521            String WDash = ""; // Dash between W and W1 if they're not empty
3522            String TSpace = "", TDash = ""; // Space or dash between label and T modifier if T isn't empty
3523            if (tg.get_DTG() != null && tg.get_DTG1() != null && !tg.get_DTG().isEmpty() && !tg.get_DTG1().isEmpty()) {
3524                WDash = " - ";
3525            }
3526            if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
3527                TSpace = " ";
3528                TDash = " - ";
3529            }
3530
3531            POINT2 ptLeft = null, ptRight = null;
3532            BufferedImage bi = new BufferedImage(8, 8, BufferedImage.TYPE_INT_ARGB);
3533            Graphics2D g2d = bi.createGraphics();
3534            g2d.setFont(tg.get_Font());
3535            FontMetrics metrics = g2d.getFontMetrics();
3536            int stringWidth = 0, rfaLines = 0;
3537            pt0 = new POINT2(tg.Pixels.get(0));
3538            if (tg.Pixels.size() > 1) {
3539                pt1 = new POINT2(tg.Pixels.get(1));
3540            }
3541
3542            POINT2[] pts = null;
3543            // if the client is the 3d map (CS) then we want to shrink the spacing bnetween 
3544            // the lines of text
3545            if (tg.get_Client().equals("cpof3d")) {
3546                csFactor = 0.9d;
3547            }
3548
3549            shiftModifierPath(tg, pt0, pt1, ptLast, ptNextToLast);
3550            switch (linetype) {
3551                case TacticalLines.CONVOY:
3552                case TacticalLines.HCONVOY:
3553                    pt2 = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(3), 0);
3554                    pt3 = lineutility.MidPointDouble(tg.Pixels.get(1), tg.Pixels.get(2), 0);
3555                    AddIntegralAreaModifier(tg, tg.get_V(), aboveEndInside, 0, pt2, pt3, false);
3556                    AddIntegralAreaModifier(tg, tg.get_H(), aboveStartInside, 0, pt2, pt3, false);
3557                    addDTG(tg, aboveMiddle, 1.2 * csFactor, 2.2 * csFactor, pt2, pt3, metrics);
3558                    break;
3559                case TacticalLines.BREACH:
3560                case TacticalLines.BYPASS:
3561                case TacticalLines.CANALIZE:
3562                    pt0 = tg.Pixels.get(1);
3563                    pt1 = tg.Pixels.get(2);
3564                    //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3565                    AddIntegralAreaModifier(tg, label, aboveMiddlePerpendicular, -0.125 * csFactor, pt0, pt1, true);
3566                    break;
3567                case TacticalLines.PENETRATE:
3568                case TacticalLines.CLEAR:
3569                    pt0 = tg.Pixels.get(2);
3570                    pt1 = tg.Pixels.get(3);
3571                    //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3572                    AddIntegralAreaModifier(tg, label, aboveMiddle, -0.125 * csFactor, pt0, pt1, true);
3573                    break;
3574                case TacticalLines.DISRUPT:
3575                    pt0 = tg.Pixels.get(4);
3576                    pt1 = tg.Pixels.get(5);
3577                    //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3578                    AddIntegralAreaModifier(tg, label, aboveMiddle, -0.125 * csFactor, pt0, pt1, true);
3579                    break;
3580                case TacticalLines.FIX:
3581                    pt0 = tg.Pixels.get(0);
3582                    pt1 = tg.Pixels.get(1);
3583                    //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3584                    AddIntegralAreaModifier(tg, label, aboveMiddle, -0.125 * csFactor, pt0, pt1, true);
3585                    break;
3586                case TacticalLines.ISOLATE:
3587                case TacticalLines.OCCUPY:
3588                case TacticalLines.RETAIN:
3589                case TacticalLines.SECURE:
3590                    pt0 = tg.Pixels.get(13);
3591                    pt1 = tg.Pixels.get(14);
3592                    //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3593                    AddIntegralAreaModifier(tg, label, aboveMiddle, -0.125 * csFactor, pt0, pt1, true);
3594                    break;
3595                case TacticalLines.CONTAIN:
3596                    pt0 = tg.Pixels.get(13);
3597                    pt1 = tg.Pixels.get(14);
3598                    //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3599                    AddIntegralAreaModifier(tg, label, aboveMiddle, -0.125 * csFactor, pt0, pt1, true);
3600
3601                    // Contain always has "ENY" even if friendly (not N modifier)
3602                    for (j = 0; j < n; j++) {
3603                        if (tg.Pixels.get(j).style == 14) {
3604                            pt0 = tg.Pixels.get(j);
3605                            pt1 = tg.Pixels.get(j + 1);
3606                            AddIntegralAreaModifier(tg, "ENY", aboveMiddle, 0, pt0, pt1, true);
3607                            break;
3608                        }
3609                    }
3610                    break;
3611                case TacticalLines.SEIZE:
3612                    pt0 = tg.Pixels.get(26);
3613                    pt1 = tg.Pixels.get(27);
3614                    //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3615                    AddIntegralAreaModifier(tg, label, aboveMiddle, -0.125 * csFactor, pt0, pt1, true);
3616                    break;
3617                case TacticalLines.DEFENDED_AREA_RECTANGULAR:
3618                    ptLeft = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
3619                    ptRight = lineutility.MidPointDouble(tg.Pixels.get(2), tg.Pixels.get(3), 0);
3620                    AddIntegralAreaModifier(tg, label + TDash + tg.get_Name(), aboveMiddle, 0, ptLeft, ptRight, false);
3621                    break;
3622                case TacticalLines.SHIP_AOI_RECTANGULAR:
3623                    if (tg.Pixels.get(0).x > tg.Pixels.get(3).x) {
3624                        AddIntegralAreaModifier(tg, label, aboveMiddle, csFactor, tg.Pixels.get(0), tg.Pixels.get(3), false);
3625                    } else {
3626                        AddIntegralAreaModifier(tg, label, aboveMiddle, csFactor, tg.Pixels.get(1), tg.Pixels.get(2), false);
3627                    }
3628                    break;
3629                case TacticalLines.NOTACK:
3630                    ptCenter = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(tg.Pixels.size() / 2), 0);
3631                    AddIntegralAreaModifier(tg, label, area, -1, ptCenter, ptCenter, false);
3632                    addDTG(tg, area, csFactor, 2 * csFactor, ptCenter, ptCenter, metrics);
3633                 break;
3634                case TacticalLines.SHIP_AOI_CIRCULAR:
3635                    // Moved from AddModifiersGeo()
3636                    // AddModifiersGeo() called before getGeoEllipse(). Unable to use getMBR with single anchor point
3637
3638                    // Get variables from AddModifiersGeo
3639                    POINT2 lr = new POINT2(tg.Pixels.get(0));
3640                    POINT2 ll = new POINT2(tg.Pixels.get(0));
3641                    POINT2 ul = new POINT2(tg.Pixels.get(0));
3642                    POINT2 ur = new POINT2(tg.Pixels.get(0));
3643                    GetMBR(tg, ul, ur, lr, ll);
3644
3645                    AddIntegralAreaModifier(tg, label, aboveMiddle, csFactor, ll, lr, false);
3646                    break;
3647                case TacticalLines.MFLANE:
3648                    //pt0=tg.Pixels.get(7);
3649                    //pt1=tg.Pixels.get(5);
3650                    pt0 = tg.Pixels.get(4);
3651                    pt1 = tg.Pixels.get(2);
3652                    if (tg.Pixels.get(0).y < tg.Pixels.get(1).y) {
3653                        addDTG(tg, aboveMiddle, 0.5 * csFactor, 1.5 * csFactor, pt0, pt1, metrics);
3654                    } else {
3655                        addDTG(tg, aboveMiddle, -0.5 * csFactor, -1.5 * csFactor, pt0, pt1, metrics);
3656                    }
3657                    break;
3658                case TacticalLines.CORDONKNOCK:
3659                case TacticalLines.CORDONSEARCH:
3660                    pt0 = tg.Pixels.get(13);
3661                    pt1 = tg.Pixels.get(0);
3662                    stringWidth = metrics.stringWidth(label);
3663                    if (pt0.x < pt1.x) {
3664                        stringWidth = -stringWidth;
3665                    }
3666                    pt1 = lineutility.ExtendAlongLineDouble2(pt0, pt1, 0.75 * stringWidth);
3667                    AddIntegralAreaModifier(tg, label, aboveMiddle, 0, pt0, pt1, true);
3668                    break;
3669                case TacticalLines.FOLLA:
3670                    pt0 = tg.Pixels.get(0);
3671                    pt1 = lineutility.MidPointDouble(tg.Pixels.get(5), tg.Pixels.get(6), 0);
3672                    pt1 = lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3673                    AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, 0, pt0, pt1, true);
3674                    break;
3675                case TacticalLines.FOLSP:
3676                    pt0 = tg.Pixels.get(3);
3677                    pt1 = tg.Pixels.get(6);
3678                    pt1 = lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3679                    AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, 0, pt0, pt1, true);
3680                    break;
3681                case TacticalLines.ACA_RECTANGULAR:
3682                    ptLeft = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
3683                    ptRight = lineutility.MidPointDouble(tg.Pixels.get(2), tg.Pixels.get(3), 0);
3684                    AddModifier2(tg, label + TSpace + tg.get_Name(), aboveMiddle, -3 * csFactor, ptLeft, ptRight, false);
3685                    AddModifier2(tg, tg.get_T1(), aboveMiddle, -2 * csFactor, ptLeft, ptRight, false, "T1");
3686                    AddModifier2(tg, "MIN ALT: " + tg.get_X(), aboveMiddle, -1 * csFactor, ptLeft, ptRight, false, "H");
3687                    AddModifier2(tg, "MAX ALT: " + tg.get_X1(), aboveMiddle, 0, ptLeft, ptRight, false, "H1");
3688                    AddModifier2(tg, "Grids: " + tg.get_H(), aboveMiddle, 1 * csFactor, ptLeft, ptRight, false, "H2");
3689                    AddModifier2(tg, "EFF: " + tg.get_DTG() + WDash, aboveMiddle, 2 * csFactor, ptLeft, ptRight, false, "W");
3690                    AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 3 * csFactor, ptLeft, ptRight, false, "W1");
3691                    break;
3692                case TacticalLines.ACA_CIRCULAR:
3693                    ptCenter = lineutility.CalcCenterPointDouble2(tg.Pixels.toArray(), tg.Pixels.size());
3694                    AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), area, -3 * csFactor, ptCenter, ptCenter, false);
3695                    AddModifier2(tg, tg.get_T1(), area, -2 * csFactor, ptCenter, ptCenter, false, "T1");
3696                    AddIntegralAreaModifier(tg, "MIN ALT: " + tg.get_X(), area, -1 * csFactor, ptCenter, ptCenter, false, "H");
3697                    AddIntegralAreaModifier(tg, "MAX ALT: " + tg.get_X1(), area, 0, ptCenter, ptCenter, false, "H1");
3698                    AddIntegralAreaModifier(tg, "Grids: " + tg.get_H(), area, 1 * csFactor, ptCenter, ptCenter, false, "H2");
3699                    AddIntegralAreaModifier(tg, "EFF: " + tg.get_DTG() + WDash, area, 2 * csFactor, ptCenter, ptCenter, false, "W");
3700                    AddIntegralAreaModifier(tg, tg.get_DTG1(), area, 3 * csFactor, ptCenter, ptCenter, false, "W1");
3701                    break;
3702                case TacticalLines.FSA_CIRCULAR:
3703                case TacticalLines.ATI_CIRCULAR:
3704                case TacticalLines.CFFZ_CIRCULAR:
3705                case TacticalLines.SENSOR_CIRCULAR:
3706                case TacticalLines.CENSOR_CIRCULAR:
3707                case TacticalLines.DA_CIRCULAR:
3708                case TacticalLines.CFZ_CIRCULAR:
3709                case TacticalLines.ZOR_CIRCULAR:
3710                case TacticalLines.TBA_CIRCULAR:
3711                case TacticalLines.TVAR_CIRCULAR:
3712                case TacticalLines.KILLBOXBLUE_CIRCULAR:
3713                case TacticalLines.KILLBOXPURPLE_CIRCULAR:
3714                    ptCenter = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(tg.Pixels.size() / 2), 0);
3715                    AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, false);
3716                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 0.5 * csFactor, ptCenter, ptCenter, false);
3717                    AddOffsetModifier(tg, tg.get_DTG() + WDash, toEnd, -1 * csFactor, tg.Pixels.size() / 2, 0, 4, "left");
3718                    AddOffsetModifier(tg, tg.get_DTG1(), toEnd, 0, tg.Pixels.size() / 2, 0, 4, "left");
3719                    break;
3720                case TacticalLines.FFA_CIRCULAR:
3721                case TacticalLines.NFA_CIRCULAR:
3722                case TacticalLines.RFA_CIRCULAR:
3723                    rfaLines = getRFALines(tg);
3724                    ptCenter = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(51), 0);
3725                    switch (rfaLines) {
3726                        case 3: //2 valid modifiers and a label
3727                            AddIntegralAreaModifier(tg, label, area, -1 * csFactor, ptCenter, ptCenter, true);
3728                            AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, ptCenter, ptCenter, true);
3729                            addDTG(tg, area, 1 * csFactor, 2 * csFactor, ptCenter, ptCenter, metrics);
3730                            break;
3731                        case 2: //one valid modifier and a label
3732                            AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, true);
3733                            if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
3734                                AddIntegralAreaModifier(tg, tg.get_Name(), area, 0.5 * csFactor, ptCenter, ptCenter, true);
3735                            } else {
3736                                addDTG(tg, area, 0.5 * csFactor, 1.5 * csFactor, ptCenter, ptCenter, metrics);
3737                            }
3738                            break;
3739                        default:    //one label only
3740                            AddIntegralAreaModifier(tg, label, area, 0, ptCenter, ptCenter, true);
3741                            break;
3742                    }
3743                    break;
3744                case TacticalLines.BLOCK:
3745                    //for (j = 0; j < tg.Pixels.size(); j++) 
3746                    for (j = 0; j < n; j++) {
3747                        if (tg.Pixels.get(j).style == 14) {
3748                            AddIntegralModifier(tg, label, aboveMiddle, 0, j, j + 1);
3749                            break;
3750                        }
3751                    }
3752                    break;
3753                case TacticalLines.FFA_RECTANGULAR:
3754                case TacticalLines.NFA_RECTANGULAR:
3755                case TacticalLines.RFA_RECTANGULAR:
3756                    rfaLines = getRFALines(tg);
3757                    pt0 = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
3758                    pt1 = lineutility.MidPointDouble(tg.Pixels.get(2), tg.Pixels.get(3), 0);
3759                    switch (rfaLines) {
3760                        case 3: //two valid modifiers and one label
3761                            AddModifier2(tg, label, aboveMiddle, -1 * csFactor, pt0, pt1, false);
3762                            AddModifier2(tg, tg.get_Name(), aboveMiddle, 0, pt0, pt1, false);
3763                            addDTG(tg, aboveMiddle, 1 * csFactor, 2 * csFactor, pt0, pt1, metrics);
3764                            break;
3765                        case 2: //one valid modifier and one label
3766                            AddModifier2(tg, label, aboveMiddle, -0.5 * csFactor, pt0, pt1, false);
3767                            if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
3768                                AddModifier2(tg, tg.get_Name(), aboveMiddle, 0.5 * csFactor, pt0, pt1, false);
3769                            } else {
3770                              addDTG(tg, aboveMiddle, 0.5 * csFactor, 1.5 * csFactor, pt0, pt1, metrics);
3771                            }
3772                            break;
3773                        default:    //one label only
3774                            AddModifier2(tg, label, aboveMiddle, 0, pt0, pt1, false);
3775                            break;
3776                    }
3777                    break;
3778                case TacticalLines.KILLBOXBLUE_RECTANGULAR:
3779                case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
3780                case TacticalLines.FSA_RECTANGULAR:
3781                case TacticalLines.ATI_RECTANGULAR:
3782                case TacticalLines.CFFZ_RECTANGULAR:
3783                case TacticalLines.SENSOR_RECTANGULAR:
3784                case TacticalLines.CENSOR_RECTANGULAR:
3785                case TacticalLines.DA_RECTANGULAR:
3786                case TacticalLines.CFZ_RECTANGULAR:
3787                case TacticalLines.ZOR_RECTANGULAR:
3788                case TacticalLines.TBA_RECTANGULAR:
3789                case TacticalLines.TVAR_RECTANGULAR:
3790                    ptLeft = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
3791                    ptRight = lineutility.MidPointDouble(tg.Pixels.get(2), tg.Pixels.get(3), 0);
3792                    AddModifier2(tg, label, aboveMiddle, -0.5 * csFactor, ptLeft, ptRight, false);
3793                    AddModifier2(tg, tg.get_Name(), aboveMiddle, 0.5 * csFactor, ptLeft, ptRight, false);
3794                    pt0 = tg.Pixels.get(0);
3795                    pt1 = tg.Pixels.get(1);
3796                    pt2 = tg.Pixels.get(2);
3797                    pt3 = tg.Pixels.get(3);
3798                    if (tg.get_Client().equalsIgnoreCase("ge")) {
3799                        pt0.x -= font.getSize() / 2;
3800                        pt2.x -= font.getSize() / 2;
3801                    }
3802                    if (!tg.get_Client().equalsIgnoreCase("ge"))//added 2-27-12
3803                    {
3804                        clsUtility.shiftModifiersLeft(pt0, pt3, 12.5);
3805                        clsUtility.shiftModifiersLeft(pt1, pt2, 12.5);
3806                    }
3807                    if (ptLeft.x == ptRight.x) {
3808                        ptRight.x += 1;
3809                    }
3810                    if (ptLeft.x < ptRight.x) {
3811                        AddModifier(tg, tg.get_DTG() + WDash, toEnd, 0, pt0, pt3);//was 1,2 switched for CPOF
3812                        AddModifier(tg, tg.get_DTG1(), toEnd, 1 * csFactor, pt0, pt3);//was 1,2
3813                    } else {
3814                        AddModifier(tg, tg.get_DTG() + WDash, toEnd, 0, pt2, pt1);//was 3,0 //switched for CPOF
3815                        AddModifier(tg, tg.get_DTG1(), toEnd, 1 * csFactor, pt2, pt1);//was 3,0
3816                    }
3817
3818                    break;
3819                case TacticalLines.PAA_RECTANGULAR:
3820                    AddIntegralModifier(tg, label, aboveMiddlePerpendicular, 0, 0, 1, true);
3821                    AddIntegralModifier(tg, label, aboveMiddle, 0, 1, 2, true);
3822                    AddIntegralModifier(tg, label, aboveMiddlePerpendicular, 0, 2, 3, true);
3823                    AddIntegralModifier(tg, label, aboveMiddle, 0, 3, 0, true);
3824                    rfaLines = getRFALines(tg);
3825                    pt0 = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
3826                    pt1 = lineutility.MidPointDouble(tg.Pixels.get(2), tg.Pixels.get(3), 0);
3827                    switch (rfaLines) {
3828                        case 3: // two valid modifiers
3829                            AddModifier2(tg, tg.get_Name(), aboveMiddle, -0.5, pt0, pt1, false);
3830                            addDTG(tg, aboveMiddle, 0.5 * csFactor, 1.5 * csFactor, pt0, pt1, metrics);
3831                            break;
3832                        case 2: // one valid modifier
3833                            if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
3834                                AddModifier2(tg, tg.get_Name(), aboveMiddle, 0, pt0, pt1, false);
3835                            } else {
3836                                addDTG(tg, aboveMiddle, 0, csFactor, pt0, pt1, metrics);
3837                            }
3838                            break;
3839                        default:
3840                            break;
3841                    }
3842                    break;
3843                case TacticalLines.PAA_CIRCULAR:
3844                    for (int i = 0; i < 4; i++) {
3845                        AddIntegralModifier(tg, label, area, -0.5 * csFactor, n / 4 * i, n / 4 * i, false);
3846                    }
3847
3848                    rfaLines = getRFALines(tg);
3849                    ptCenter = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get((int) (n / 2.0 + 0.5)), 0);
3850                    switch (rfaLines) {
3851                        case 3: // two valid modifiers
3852                            AddIntegralAreaModifier(tg, tg.get_Name(), area, -0.5, ptCenter, ptCenter, false);
3853                            addDTG(tg, area, 0.5 * csFactor, 1.5 * csFactor, ptCenter, ptCenter, metrics);
3854                            break;
3855                        case 2: // one valid modifier
3856                            if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
3857                                AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, ptCenter, ptCenter, false);
3858                            } else {
3859                               addDTG(tg, area, 0, csFactor, ptCenter, ptCenter, metrics);
3860                            }
3861                            break;
3862                        default:
3863                            break;
3864                    }
3865                    break;
3866                case TacticalLines.RANGE_FAN:
3867                    if (tg.get_X() != null) {
3868                        X = tg.get_X().split(",");
3869                        for (j = 0; j < X.length; j++) {
3870                            if (tg.Pixels.size() > j * 102 + 25) {
3871                                pt0 = tg.Pixels.get(j * 102 + 25);
3872                                AddAreaModifier(tg, "ALT " + X[j], area, 0, pt0, pt0);
3873                            }
3874                        }
3875                    }
3876                    if(!tg.get_HideOptionalLabels())
3877                    {
3878                        String[] am = tg.get_AM().split(",");
3879                        for(j=0;j<am.length;j++)
3880                        {
3881                            if (tg.Pixels.size() > j * 102 + 25) {
3882                                pt0 = tg.Pixels.get(j * 102 + 25);
3883                                //AddAreaModifier(tg, "RG " + am[j], area, -1, pt0, pt0);
3884                                if(j==0)
3885                                    AddAreaModifier(tg, "MIN RG " + am[j], 3, -1, pt0, pt0);
3886                                else
3887                                    AddAreaModifier(tg, "MAX RG " + "(" + Integer.toString(j) + ") " + am[j], 3, -1, pt0, pt0);
3888                            }                            
3889                        }
3890                    }// end if set range fan text
3891                    break;
3892                case TacticalLines.RANGE_FAN_SECTOR:
3893                case TacticalLines.RADAR_SEARCH:
3894                    addSectorModifiers(tg, converter);
3895                    break;
3896                default:
3897                    break;
3898            }//end switch
3899            scaleModifiers(tg);
3900            tg.Pixels = origPoints;
3901            g2d.dispose();
3902            g2d = null;
3903        } catch (Exception exc) {
3904            ErrorLogger.LogException(_className, "AddModifiers2",
3905                    new RendererException("Failed inside AddModifiers2", exc));
3906        }
3907    }
3908
3909    /**
3910     * Displays the tg modifiers using a client Graphics2D, this is an option
3911     * provided to clients for displaying modifiers without using shapes
3912     *
3913     * @param tg the tactical graphic
3914     * @param g2d the graphics object for drawing
3915     * @deprecated
3916     */
3917    public static void DisplayModifiers(TGLight tg,
3918            Graphics2D g2d) {
3919        try {
3920            Font font = g2d.getFont();
3921            int j = 0;
3922            Modifier2 modifier = null;
3923            g2d.setBackground(Color.white);
3924            POINT2 pt = null;
3925            double theta = 0;
3926            int stringWidth = 0, stringHeight = 0;
3927            FontMetrics metrics = g2d.getFontMetrics();
3928            String s = "";
3929            int x = 0, y = 0;
3930            POINT2 pt1 = null, pt2 = null;
3931            int quadrant = -1;
3932            int n = tg.Pixels.size();
3933            //for (j = 0; j < tg.modifiers.size(); j++) 
3934            for (j = 0; j < n; j++) {
3935                modifier = (Modifier2) tg.modifiers.get(j);
3936                double lineFactor = modifier.lineFactor;
3937                s = modifier.text;
3938                double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
3939                pt = modifier.textPath[0];
3940                x1 = pt.x;
3941                y1 = pt.y;
3942                pt = modifier.textPath[1];
3943                x2 = pt.x;
3944                y2 = pt.y;
3945                theta = Math.atan2(y2 - y1, x2 - x1);
3946                POINT2 midPt;
3947                if (x1 > x2) {
3948                    theta -= Math.PI;
3949                }
3950                switch (modifier.type) {
3951                    case toEnd: //corresponds to LabelAndTextBeforeLineTG
3952                        g2d.rotate(theta, x1, y1);
3953                        stringWidth = metrics.stringWidth(s);
3954                        stringHeight = font.getSize();
3955                        if (x1 < x2 || (x1 == x2 && y1 > y2)) {
3956                            x = (int) x1 - stringWidth;
3957                            y = (int) y1 - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
3958                            g2d.setColor(tg.get_FontBackColor());
3959                            g2d.clearRect(x, y, stringWidth, stringHeight);
3960                            y = (int) y1 + (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
3961                            g2d.setColor(tg.get_TextColor());
3962                            g2d.drawString(s, x, y);
3963                        } else {
3964                            x = (int) x1;
3965                            y = (int) y1 - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
3966                            g2d.setColor(tg.get_FontBackColor());
3967                            g2d.clearRect(x, y, stringWidth, stringHeight);
3968                            y = (int) y1 + (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
3969                            g2d.setColor(tg.get_TextColor());
3970                            g2d.drawString(s, x, y);
3971                        }
3972                        break;
3973                    case aboveMiddle:
3974                        midPt = new POINT2((x1 + x2) / 2, (y1 + y2) / 2);
3975                        g2d.rotate(theta, midPt.x, midPt.y);
3976                        stringWidth = metrics.stringWidth(s);
3977                        stringHeight = font.getSize();
3978                        x = (int) midPt.x - stringWidth / 2;
3979                        y = (int) midPt.y - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
3980                        g2d.setColor(tg.get_FontBackColor());
3981                        g2d.clearRect(x, y, stringWidth, stringHeight);
3982                        y = (int) midPt.y + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
3983                        g2d.setColor(tg.get_TextColor());
3984                        g2d.drawString(s, x, y);
3985                        break;
3986                    case area:
3987                        g2d.rotate(0, x1, y1);
3988                        stringWidth = metrics.stringWidth(s);
3989                        stringHeight = font.getSize();
3990
3991                        x = (int) x1 - stringWidth / 2;
3992                        y = (int) y1 - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
3993                        g2d.setColor(tg.get_FontBackColor());
3994                        g2d.clearRect(x, y, stringWidth, stringHeight);
3995                        y = (int) y1 + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
3996                        g2d.setColor(tg.get_TextColor());
3997                        g2d.drawString(s, x, y);
3998                        break;
3999                    case screen:    //for SCREEN, GUARD, COVER
4000                        if (tg.Pixels.size() >= 14) {
4001                            pt1 = tg.Pixels.get(3);
4002                            pt2 = tg.Pixels.get(10);
4003                            quadrant = lineutility.GetQuadrantDouble(pt1, pt2);
4004                            theta = Math.atan2(pt2.y - pt1.y, pt2.x - pt1.x);
4005                            switch (quadrant) {
4006                                case 1:
4007                                    theta += Math.PI / 2;
4008                                    break;
4009                                case 2:
4010                                    theta -= Math.PI / 2;
4011                                    break;
4012                                case 3:
4013                                    theta -= Math.PI / 2;
4014                                    break;
4015                                case 4:
4016                                    theta += Math.PI / 2;
4017                                    break;
4018                                default:
4019                                    break;
4020                            }
4021
4022                            g2d.rotate(theta, x1, y1);
4023                            stringWidth = metrics.stringWidth(s);
4024                            stringHeight = font.getSize();
4025
4026                            x = (int) x1 - stringWidth / 2;
4027                            y = (int) y1 - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
4028                            g2d.setColor(tg.get_FontBackColor());
4029                            g2d.clearRect(x, y, stringWidth, stringHeight);
4030                            y = (int) y1 + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
4031                            g2d.setColor(tg.get_TextColor());
4032                            g2d.drawString(s, x, y);
4033                        } else {
4034                            stringWidth = metrics.stringWidth(s);
4035                            stringHeight = font.getSize();
4036                            x = (int) tg.Pixels.get(0).x;//(int) x1 - stringWidth / 2;
4037                            y = (int) tg.Pixels.get(0).y;//(int) y1 - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
4038                            g2d.setColor(tg.get_FontBackColor());
4039                            g2d.clearRect(x, y, stringWidth, stringHeight);
4040                            y = (int) y + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
4041                            g2d.setColor(tg.get_TextColor());
4042                            g2d.drawString(s, x, y);
4043                        }
4044                        break;
4045                    default:
4046                        break;
4047                }   //end switch
4048            }   //end for
4049        } catch (Exception exc) {
4050            ErrorLogger.LogException(_className, "DisplayModifiers",
4051                    new RendererException("Failed inside DisplayModifiers", exc));
4052        }
4053    }//end function
4054
4055    /**
4056     * Returns a Shape object for the text background for labels and modifiers
4057     *
4058     * @param tg the tactical graphic object
4059     * @param pt0 1st point of segment
4060     * @param pt1 last point of segment
4061     * @param stringWidth string width
4062     * @param stringHeight string height
4063     * @param lineFactor number of text lines above or below the segment
4064     * @param isTextFlipped true if text is flipped
4065     * @return the modifier shape
4066     */
4067    public static Shape2 BuildModifierShape(
4068            TGLight tg,
4069            POINT2 pt0,
4070            POINT2 pt1,
4071            int stringWidth,
4072            int stringHeight,
4073            double lineFactor,
4074            boolean isTextFlipped) {
4075        Shape2 modifierFill = null;
4076        try {
4077
4078            POINT2 ptTemp0 = new POINT2(pt0), ptTemp1 = new POINT2(pt1);
4079
4080            if (isTextFlipped) {
4081                lineFactor += 1;
4082            }
4083
4084            if (lineFactor < 0) //extend pt0,pt1 above the line
4085            {
4086                ptTemp0 = lineutility.ExtendDirectedLine(pt0, pt1, pt0, 2, -lineFactor * stringHeight);
4087                ptTemp1 = lineutility.ExtendDirectedLine(pt0, pt1, pt1, 2, -lineFactor * stringHeight);
4088            }
4089            if (lineFactor > 0) //extend pt0,pt1 below the line
4090            {
4091                ptTemp0 = lineutility.ExtendDirectedLine(pt0, pt1, pt0, 3, lineFactor * stringHeight);
4092                ptTemp1 = lineutility.ExtendDirectedLine(pt0, pt1, pt1, 3, lineFactor * stringHeight);
4093            }
4094            if (ptTemp0.y == ptTemp1.y) {
4095                ptTemp0.y += 1;
4096            }
4097
4098            POINT2 pt3 = null, pt4 = null, pt5 = null, pt6 = null, pt7 = null;
4099            pt3 = lineutility.ExtendAlongLineDouble(ptTemp0, ptTemp1, -stringWidth);
4100            pt4 = lineutility.ExtendDirectedLine(ptTemp1, ptTemp0, pt3, 0, stringHeight / 2);
4101            pt5 = lineutility.ExtendDirectedLine(ptTemp1, ptTemp0, pt3, 1, stringHeight / 2);
4102            pt6 = lineutility.ExtendDirectedLine(ptTemp1, ptTemp0, ptTemp0, 1, stringHeight / 2);
4103            pt7 = lineutility.ExtendDirectedLine(ptTemp1, ptTemp0, ptTemp0, 0, stringHeight / 2);
4104            modifierFill = new Shape2(Shape2.SHAPE_TYPE_MODIFIER_FILL);
4105
4106            modifierFill.moveTo(pt4);
4107            modifierFill.lineTo(pt5);
4108            modifierFill.lineTo(pt6);
4109            modifierFill.lineTo(pt7);
4110            modifierFill.lineTo(pt4);
4111        } catch (Exception exc) {
4112            ErrorLogger.LogException(_className, "BuildModifierShape",
4113                    new RendererException("Failed inside BuildModifierShape", exc));
4114        }
4115        return modifierFill;
4116    }
4117
4118    /**
4119     * For BOUNDARY and other line types which require breaks for the integral
4120     * text. Currently only boundary uses this
4121     *
4122     * @param tg
4123     * @param g2d the graphics object for drawing
4124     * @param shapes the shape array
4125     */
4126    public static void GetIntegralTextShapes(TGLight tg,
4127            Graphics2D g2d,
4128            ArrayList<Shape2> shapes) {
4129        try {
4130            if (tg.Pixels == null || shapes == null) {
4131                return;
4132            }
4133
4134            HashMap<Integer, Color> hmap = clsUtility.getMSRSegmentColors(tg);
4135            Color color = null;
4136
4137            Shape2 shape = null;
4138            Shape2 segShape = null;//diangostic 1-22-13
4139            g2d.setFont(tg.get_Font());
4140            int j = 0;
4141            String affiliation = null;
4142            FontMetrics metrics = g2d.getFontMetrics();
4143            String echelonSymbol = null;
4144            int stringWidthEchelonSymbol = 0;
4145            //boolean lineTooShort = false;
4146            POINT2 ptEchelonStart = null, ptEchelonEnd = null, midpt,
4147                    ptENY0Start = null, ptENY0End = null, ptENY1Start, ptENY1End, pt0 = null, pt1 = null;
4148            double dist = 0;
4149            BasicStroke stroke = null;
4150            switch (tg.get_LineType()) {
4151                case TacticalLines.BOUNDARY:
4152                    echelonSymbol = tg.get_EchelonSymbol();
4153                    //shapes = new ArrayList();
4154                    shape = new Shape2(Shape2.SHAPE_TYPE_POLYLINE);
4155                    shape.setLineColor(tg.get_LineColor());
4156                    shape.set_Style(tg.get_LineStyle());
4157                    stroke = clsUtility.getLineStroke(tg.get_LineThickness(), shape.get_Style(), tg.get_lineCap(), BasicStroke.JOIN_ROUND);
4158                    shape.setStroke(stroke);
4159                    if (echelonSymbol != null && !echelonSymbol.isEmpty()) {
4160                        stringWidthEchelonSymbol = metrics.stringWidth(echelonSymbol);
4161                    }
4162                    //diagnostic
4163                    if (hmap == null || hmap.isEmpty()) {
4164                        shape.moveTo(tg.Pixels.get(0));
4165                        for (j = 1; j < tg.Pixels.size(); j++) {
4166                            shape.lineTo(tg.Pixels.get(j));
4167                        }
4168                        shapes.add(shape);
4169                        break;
4170                    }
4171                    //end section
4172                    int n = tg.Pixels.size();
4173                    //for (j = 0; j < tg.Pixels.size() - 1; j++) 
4174                    for (j = 0; j < n - 1; j++) {
4175                        segShape = null;
4176                        if (hmap != null) {
4177                            if (hmap.containsKey(j)) {
4178                                color = (Color) hmap.get(j);
4179                                segShape = new Shape2(Shape2.SHAPE_TYPE_POLYLINE);
4180                                segShape.setLineColor(color);
4181                                segShape.set_Style(tg.get_LineStyle());
4182                                segShape.setStroke(stroke);
4183                            }
4184                        }
4185
4186                        pt0 = tg.Pixels.get(j);
4187                        pt1 = tg.Pixels.get(j + 1);
4188                        //lineTooShort = GetBoundarySegmentTooShort(tg, g2d, j);
4189                        if (segShape != null) {
4190                            segShape.moveTo(pt0);
4191                        } else {
4192                            shape.moveTo(pt0);
4193                        }
4194
4195                        //uncoment comment to remove line breaks for GE
4196                        //if (lineTooShort || tg.get_Client().equals("ge")) 
4197                        if (tg.get_Client().equals("ge") || GetBoundarySegmentTooShort(tg, g2d, j) == true) {
4198                            if (segShape != null) {
4199                                segShape.lineTo(pt1);
4200                                shapes.add(segShape);
4201                                continue;
4202                            } else {
4203                                shape.lineTo(pt1);
4204                                continue;
4205                            }
4206                        }
4207
4208                        midpt = lineutility.MidPointDouble(pt0, pt1, 0);
4209                        if (segShape != null) {
4210                            segShape.moveTo(pt0);
4211                        } else {
4212                            shape.moveTo(pt0);
4213                        }
4214
4215                        if (stringWidthEchelonSymbol > 0) {
4216                            midpt = lineutility.MidPointDouble(pt0, pt1, 0);
4217                            dist = lineutility.CalcDistanceDouble(pt0, midpt) - stringWidthEchelonSymbol / 1.5;
4218                            ptEchelonStart = lineutility.ExtendAlongLineDouble(pt0, pt1, dist);
4219                            dist = lineutility.CalcDistanceDouble(pt0, midpt) + stringWidthEchelonSymbol / 1.5;
4220                            ptEchelonEnd = lineutility.ExtendAlongLineDouble(pt0, pt1, dist);
4221                            if (segShape != null) {
4222                                segShape.lineTo(ptEchelonStart);
4223                                segShape.moveTo(ptEchelonEnd);
4224                            } else {
4225                                shape.lineTo(ptEchelonStart);
4226                                shape.moveTo(ptEchelonEnd);
4227                            }
4228                        }
4229                        if (segShape != null) {
4230                            segShape.lineTo(pt1);
4231                        } else {
4232                            shape.lineTo(pt1);
4233                        }
4234                        if (segShape != null) {
4235                            shapes.add(segShape);
4236                        }
4237                    }//end for
4238                    shapes.add(shape);
4239                    break;
4240                default:
4241                    break;
4242            }
4243        } catch (Exception exc) {
4244            ErrorLogger.LogException(_className, "GetIntegralTextShapes",
4245                    new RendererException("Failed inside GetIntegralTextShapes", exc));
4246        }
4247    }
4248
4249    private static int switchDirection(int direction) {
4250        int result = -1;
4251        switch (direction) {
4252            case 0:
4253                return 1;
4254            case 1:
4255                return 0;
4256            case 2:
4257                return 3;
4258            case 3:
4259                return 2;
4260        }
4261        return result;
4262    }
4263
4264    /**
4265     * Displays the modifiers to a Graphics2D from a BufferedImage
4266     *
4267     * @param tg the tactical graphic
4268     * @param g2d the Graphic for drawing
4269     * @param shapes the shape array
4270     * @param isTextFlipped true if text is flipped
4271     * @param converter to convert between geographic and pixel coordinates
4272     */
4273    public static void DisplayModifiers2(TGLight tg,
4274            Graphics2D g2d,
4275            ArrayList<Shape2> shapes,
4276            boolean isTextFlipped,
4277            IPointConversion converter) {
4278        try {
4279            if (shapes == null) {
4280                return;
4281            }
4282
4283            if (tg.modifiers == null || tg.modifiers.isEmpty()) {
4284                return;
4285            }
4286            Font font = null;
4287            int j = 0;
4288            Modifier2 modifier = null;
4289            Color fontBackColor = tg.get_FontBackColor();
4290            double theta = 0;
4291            double stringWidth = 0, stringHeight = 0;
4292            String s = "";
4293            Bitmap image = null;
4294            int x = 0, y = 0;
4295            POINT2 pt0 = null, pt1 = null, pt2 = null, pt3 = null;
4296            int quadrant = -1;
4297            Shape2 shape2 = null;
4298            long lineType = tg.get_LineType();
4299            font = tg.get_Font();    //might have to change this
4300            if (font == null) {
4301                font = g2d.getFont();
4302            }
4303            if (font.getSize() == 0) {
4304                return;
4305            }
4306            g2d.setFont(font);
4307            FontMetrics metrics = g2d.getFontMetrics();
4308            //we need a background color
4309            if (fontBackColor != null) {
4310                g2d.setBackground(fontBackColor);
4311            } else {
4312                g2d.setBackground(Color.white);
4313            }
4314
4315            int direction = -1;
4316            Point glyphPosition = null;
4317            for (j = 0; j < tg.modifiers.size(); j++) {
4318                modifier = (Modifier2) tg.modifiers.get(j);
4319
4320                double lineFactor = modifier.lineFactor;
4321
4322                if (isTextFlipped) {
4323                    lineFactor = -lineFactor;
4324                }
4325
4326                s = modifier.text;
4327                if (s == null || s.equals("")) {
4328
4329                    image = modifier.image;
4330                    if (image == null) {
4331                        continue;
4332                    }
4333                }
4334                stringWidth = s != null ? (double) metrics.stringWidth(s) + 1 : image.getWidth() + 1;
4335                stringHeight = s != null ? (double) font.getSize() : image.getHeight();
4336
4337                double x1 = 0, y1 = 0, x2 = 0, y2 = 0, dist = 0;
4338                pt0 = modifier.textPath[0];
4339                x1 = Math.round(pt0.x);
4340                y1 = Math.round(pt0.y);
4341                pt1 = modifier.textPath[1];
4342                x2 = Math.round(pt1.x);
4343                y2 = Math.round(pt1.y);
4344                theta = Math.atan2(y2 - y1, x2 - x1);
4345                POINT2 midPt;
4346                if (x1 > x2) {
4347                    theta -= Math.PI;
4348                }
4349                pt0 = new POINT2(x1, y1);
4350                pt1 = new POINT2(x2, y2);
4351                midPt = new POINT2((x1 + x2) / 2, (y1 + y2) / 2);
4352                Point2D modifierPosition = null;  //use this if using justify
4353                int justify = ShapeInfo.justify_left;
4354                switch (modifier.type) {
4355                    case aboveEnd: // On line
4356                    case toEnd: // Next to line
4357                        if (x1 == x2) {
4358                            x2 += 1;
4359                        }
4360
4361                        if (lineFactor >= 0) {
4362                            direction = 2;
4363                        } else {
4364                            direction = 3;
4365                        }
4366
4367                        if (lineType == TacticalLines.LC || tg.get_Client().equalsIgnoreCase("ge")) {
4368                            direction = switchDirection(direction);
4369                        }
4370
4371                        if ((modifier.type == toEnd && x1 < x2) || (modifier.type == aboveEnd && x2 < x1)) {
4372                            justify = ShapeInfo.justify_right;
4373                        } else {
4374                            justify = ShapeInfo.justify_left;
4375                        }
4376
4377                        pt3 = lineutility.ExtendDirectedLine(pt1, pt0, pt0, direction, lineFactor * stringHeight);
4378
4379                        glyphPosition = new Point((int) pt3.x, (int) pt3.y);
4380                        modifierPosition = new Point2D.Double(pt3.x, pt3.y);
4381                        break;
4382                    case aboveStartInside:
4383                        pt3 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
4384
4385                        glyphPosition = new Point((int) pt3.x, (int) pt3.y);
4386                        modifierPosition = new Point2D.Double((int) pt3.x, pt3.y);
4387                        break;
4388                    case aboveEndInside:
4389                        pt3 = lineutility.ExtendAlongLineDouble(pt1, pt0, stringWidth);
4390
4391                        glyphPosition = new Point((int) pt3.x, (int) pt3.y);
4392                        modifierPosition = new Point2D.Double((int) pt3.x, pt3.y);
4393                        break;
4394                    case aboveMiddle:
4395                    case aboveMiddlePerpendicular:
4396                        pt2 = midPt;
4397                        if (tg.get_Client().equals("2D")) {
4398                            lineFactor += 0.5;
4399                        }
4400
4401                        if (lineFactor >= 0) {
4402                            pt3 = lineutility.ExtendDirectedLine(pt0, pt2, pt2, 3, Math.abs((lineFactor) * stringHeight));
4403                            midPt = lineutility.ExtendDirectedLine(pt0, midPt, midPt, 3, Math.abs((lineFactor) * stringHeight));
4404                        } else {
4405                            pt3 = lineutility.ExtendDirectedLine(pt0, pt2, pt2, 2, Math.abs((lineFactor) * stringHeight));
4406                            midPt = lineutility.ExtendDirectedLine(pt0, midPt, midPt, 2, Math.abs((lineFactor) * stringHeight));
4407                        }
4408                        //pt3=lineutility.ExtendDirectedLine(pt0, pt2, pt2, 2, lineFactor*stringHeight);
4409                        if (x1 == x2 && y1 > y2) {
4410                            pt3 = lineutility.ExtendDirectedLine(pt0, pt2, pt2, 1, Math.abs((lineFactor) * stringHeight));
4411                            midPt = lineutility.ExtendDirectedLine(pt0, midPt, midPt, 1, Math.abs((lineFactor) * stringHeight));
4412                        }
4413                        if (x1 == x2 && y1 < y2) {
4414                            pt3 = lineutility.ExtendDirectedLine(pt0, pt2, pt2, 0, Math.abs((lineFactor) * stringHeight));
4415                            midPt = lineutility.ExtendDirectedLine(pt0, midPt, midPt, 0, Math.abs((lineFactor) * stringHeight));
4416                        }
4417
4418                        glyphPosition = new Point((int) pt3.x, (int) pt3.y);
4419                        justify = ShapeInfo.justify_center;
4420                        modifierPosition = new Point2D.Double(midPt.x, midPt.y);
4421
4422                        if(modifier.type == aboveMiddlePerpendicular) {
4423                            // Need to negate the original rotation
4424                            if (x1 > x2) {
4425                                theta += Math.PI;
4426                            }
4427                            // Adjust the label rotation based on the y values
4428                            if (y1 > y2) {
4429                                theta += Math.PI;
4430                            }
4431                            // Rotate by 90 degrees. This is how we rotate the label perpendicular to the line
4432                            theta -= Math.PI / 2;
4433                        }
4434                        break;
4435                    case area:
4436                        theta = 0;
4437
4438                        //y = (int) y1 + (int) (stringHeight / 2) + (int) (1.25 * lineFactor * stringHeight);
4439                        y = (int) y1 + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
4440                        x = image != null ? (int) (x1 - stringWidth / 3) : (int) x1;
4441
4442                        glyphPosition = new Point(x, y);
4443                        justify = ShapeInfo.justify_center;
4444                        modifierPosition = new Point2D.Double(x, y);
4445                        break;
4446                    case areaImage:
4447                        glyphPosition = new Point((int)x1, (int)y1);
4448                        justify = ShapeInfo.justify_center;
4449                        modifierPosition = new Point2D.Double((int)x1, (int)y1);
4450                        break;
4451                    case screen:    //for SCREEN, GUARD, COVER, not currently used
4452                        if (tg.Pixels.size() >= 14) {
4453                            pt1 = tg.Pixels.get(3);
4454                            pt2 = tg.Pixels.get(10);
4455                            quadrant = lineutility.GetQuadrantDouble(pt1, pt2);
4456                            theta = Math.atan2(pt2.y - pt1.y, pt2.x - pt1.x);
4457                            if (Math.abs(theta) < Math.PI / 8) {
4458                                if (theta < 0) {
4459                                    theta -= Math.PI / 2;
4460                                } else {
4461                                    theta += Math.PI / 2;
4462                                }
4463                            }
4464                            switch (quadrant) {
4465                                case 1:
4466                                    theta += Math.PI / 2;
4467                                    break;
4468                                case 2:
4469                                    theta -= Math.PI / 2;
4470                                    break;
4471                                case 3:
4472                                    theta -= Math.PI / 2;
4473                                    break;
4474                                case 4:
4475                                    theta += Math.PI / 2;
4476                                    break;
4477                                default:
4478                                    break;
4479                            }
4480
4481                            x = (int) x1 - (int) stringWidth / 2;
4482                            y = (int) y1 - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
4483                            y = (int) y1 + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
4484                        } else {
4485                            theta = 0;
4486                            x = (int) tg.Pixels.get(0).x;
4487                            y = (int) tg.Pixels.get(0).y;
4488                            x = (int) x - (int) stringWidth / 2;
4489                            y = (int) y - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
4490                            y = (int) y + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
4491                        }
4492
4493                        glyphPosition = new Point(x, y);
4494                        //glyphPosition=new Point2D.Double(x,y);
4495                        break;
4496                    default:
4497                        break;
4498                }   //end switch
4499
4500                shape2 = new Shape2(Shape2.SHAPE_TYPE_MODIFIER_FILL);
4501
4502                shape2.setStroke(new BasicStroke(0, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3));
4503
4504                if (tg.get_TextColor() != null) {
4505                    shape2.setFillColor(tg.get_TextColor());
4506                } else if (tg.get_LineColor() != null) {
4507                    shape2.setFillColor(tg.get_LineColor());
4508                }
4509                if (tg.get_LineColor() != null) {
4510                    shape2.setLineColor(tg.get_LineColor());
4511                }
4512                //only GE uses the converter, generic uses the affine transform and draws at 0,0
4513                if (converter != null) {
4514                    shape2.setGlyphPosition(glyphPosition);
4515                } else {
4516                    shape2.setGlyphPosition(new Point2D.Double(0, 0));
4517                }
4518                //shape2.setGlyphPosition(new Point(0,0));
4519                //added two settings for use by GE
4520                if(s != null && !s.equals("")) {
4521                    shape2.setModifierString(s);
4522                    TextLayout tl = new TextLayout(s, font, g2d.getFontMetrics().getFontRenderContext());
4523                    shape2.setTextLayout(tl);
4524                    shape2.setTextJustify(justify);
4525                } else if (image != null) {
4526                    shape2.setModifierImage(image);
4527                }
4528                //shape2.setModifierStringPosition(glyphPosition);//M. Deutch 7-6-11
4529                shape2.setModifierAngle(theta * 180 / Math.PI);
4530                shape2.setModifierPosition(modifierPosition);
4531
4532                if (shape2 != null) {
4533                    shapes.add(shape2);
4534                }
4535
4536            }   //end for
4537        } //end try
4538        catch (Exception exc) {
4539            ErrorLogger.LogException(_className, "DisplayModifiers2",
4540                    exc);
4541        }
4542    }//end function
4543
4544    /**
4545     * Builds a shape object to wrap text
4546     *
4547     * @param g2d the Graphic object for drawing
4548     * @param str text to wrap
4549     * @param font the draw font
4550     * @param tx the drawing transform, text rotation and translation
4551     * @return
4552     */
4553    public static Shape getTextShape(Graphics2D g2d,
4554                                     String str,
4555                                     Font font,
4556                                     AffineTransform tx) {
4557        TextLayout tl = null;
4558        FontRenderContext frc = null;
4559        try {
4560            frc = g2d.getFontRenderContext();
4561            tl = new TextLayout(str, font, frc);
4562        } catch (Exception exc) {
4563            ErrorLogger.LogException(_className, "getTextShape",
4564                    new RendererException("Failed inside getTextShape", exc));
4565        }
4566        return tl.getOutline(tx);
4567    }
4568
4569    /**
4570     * Creates text outline as a shape
4571     *
4572     * @param originalText the original text
4573     * @return text shape
4574     */
4575    public static Shape2 createTextOutline(Shape2 originalText) {
4576        Shape2 siOutline = null;
4577        try {
4578            Shape outline = originalText.getShape();
4579
4580            siOutline = new Shape2(Shape2.SHAPE_TYPE_MODIFIER_FILL);
4581            siOutline.setShape(outline);
4582
4583            if (originalText.getFillColor().getRed() == 255
4584                    && originalText.getFillColor().getGreen() == 255
4585                    && originalText.getFillColor().getBlue() == 255) {
4586                siOutline.setLineColor(Color.BLACK);
4587            } else {
4588                siOutline.setLineColor(Color.WHITE);
4589            }
4590
4591            int width = RendererSettings.getInstance().getTextOutlineWidth();
4592
4593            siOutline.setStroke(new BasicStroke(width, BasicStroke.CAP_ROUND,
4594                    BasicStroke.JOIN_ROUND, 3));
4595
4596        } catch (Exception exc) {
4597            ErrorLogger.LogException(_className, "createTextOutline",
4598                    new RendererException("Failed inside createTextOutline", exc));
4599        }
4600        return siOutline;
4601    }
4602
4603    /**
4604     * Channels don't return points in tg.Pixels. For Channels modifiers we only
4605     * need to collect the points, don't need internal arrays, and can calculate
4606     * on which segments the modifiers lie.
4607     *
4608     * @param shape
4609     * @return
4610     */
4611    private static ArrayList<POINT2> getShapePoints(Shape shape) {
4612        try {
4613            ArrayList<Point2D> ptsPoly = new ArrayList();
4614            Point2D ptPoly = null;
4615            double[] coords = new double[6];
4616            int zeros = 0;
4617            for (PathIterator i = shape.getPathIterator(null); !i.isDone(); i.next()) {
4618                int type = i.currentSegment(coords);
4619                if (type == 0 && zeros == 2) {
4620                    break;
4621                }
4622                switch (type) {
4623                    case PathIterator.SEG_MOVETO:
4624                        ptPoly = new Point2D.Double(coords[0], coords[1]);
4625                        ptsPoly.add(ptPoly);
4626                        zeros++;
4627                        break;
4628                    case PathIterator.SEG_LINETO:
4629                        ptPoly = new Point2D.Double(coords[0], coords[1]);
4630                        ptsPoly.add(ptPoly);
4631                        break;
4632                    case PathIterator.SEG_QUADTO: //quadTo was never used
4633                        break;
4634                    case PathIterator.SEG_CUBICTO:  //curveTo was used for some METOC's
4635                        break;
4636                    case PathIterator.SEG_CLOSE:    //closePath was never used
4637                        break;
4638                }
4639            }
4640            if (ptsPoly.size() > 0) {
4641                ArrayList<POINT2> pts = null;
4642                pts = new ArrayList();
4643                for (int j = 0; j < ptsPoly.size(); j++) {
4644                    Point2D pt2d = ptsPoly.get(j);
4645                    POINT2 pt = new POINT2(pt2d.getX(), pt2d.getY());
4646                    pts.add(pt);
4647                }
4648                return pts;
4649            }
4650        } catch (Exception exc) {
4651            ErrorLogger.LogException(_className, "getshapePoints",
4652                    new RendererException("Failed inside getShapePoints", exc));
4653        }
4654        return null;
4655    }
4656
4657    private static Bitmap getImageModifier(TGLight tg) {
4658        String symbolID = tg.get_SymbolId();
4659        ImageInfo symbol = null;
4660        Map<String,String> mods = new HashMap<>();
4661        Map<String,String> sa = new HashMap<>();
4662        sa.put(MilStdAttributes.PixelSize, String.valueOf(tg.get_IconSize()));
4663        int contaminationCode = EntityCode.getSymbolForContaminationArea(SymbolID.getEntityCode(symbolID));
4664        int modifier1Code = SymbolID.getModifier1(symbolID);
4665        int lineType = GetLinetypeFromString(symbolID);
4666        if (contaminationCode > 0) {
4667            sa.put(MilStdAttributes.OutlineSymbol, "true");
4668            sa.put(MilStdAttributes.FillColor, RendererUtilities.colorToHexString(tg.get_FillColor(), true));
4669            sa.put(MilStdAttributes.LineColor, RendererUtilities.colorToHexString(tg.get_LineColor(), true));
4670            String contaminationSP = SymbolID.setEntityCode(symbolID, contaminationCode);
4671            contaminationSP = SymbolID.setHQTFD(contaminationSP, 0); // Remove dummy modifier if necessary
4672            symbol = SinglePointRenderer.getInstance().RenderSP(contaminationSP, mods, sa);
4673        } else if (lineType == TacticalLines.DEPICT || lineType == TacticalLines.MINED || lineType == TacticalLines.FENCED || lineType == TacticalLines.MINE_LINE) {
4674            if (modifier1Code < 13 || modifier1Code > 50) {
4675                // Invalid mine type
4676                modifier1Code = 13;//unspecified mine (default value if not specified as per MilStd 2525)
4677                symbolID = SymbolID.setModifier1(symbolID, modifier1Code);
4678            }
4679            if (tg.get_KeepUnitRation()) {
4680                sa.put(MilStdAttributes.PixelSize, String.valueOf((int) (tg.get_IconSize() * 1.5)));
4681            }
4682            sa.put(MilStdAttributes.OutlineSymbol, "true");
4683            symbol = SinglePointRenderer.getInstance().RenderModifier(symbolID, sa);
4684        } else if (lineType == TacticalLines.LAA && modifier1Code > 0) {
4685            sa.put(MilStdAttributes.OutlineSymbol, "true");
4686            sa.put(MilStdAttributes.FillColor, RendererUtilities.colorToHexString(tg.get_FillColor(), true));
4687            sa.put(MilStdAttributes.LineColor, RendererUtilities.colorToHexString(tg.get_LineColor(), true));
4688            if (tg.get_KeepUnitRation()) {
4689                sa.put(MilStdAttributes.PixelSize, String.valueOf((int) (tg.get_IconSize() * 1.5)));
4690            }
4691            symbol = SinglePointRenderer.getInstance().RenderModifier(symbolID, sa);
4692        } else if (lineType == TacticalLines.ANCHORAGE_LINE || lineType == TacticalLines.ANCHORAGE_AREA) {
4693            sa.put(MilStdAttributes.OutlineSymbol, "false");
4694            String anchorPoint = SymbolID.setEntityCode(symbolID, EntityCode.EntityCode_AnchoragePoint);
4695            symbol = SinglePointRenderer.getInstance().RenderSP(anchorPoint, mods, sa);
4696        }
4697
4698        if (symbol != null)
4699            return symbol.getImage();
4700        else
4701            return null;
4702    }
4703}