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