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