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, 0 * 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                }
2379            }
2380
2381            //modifiers aren't grouped or the symbol doesn't leverage grouped modifiers
2382            if(!modifiersGrouped) {
2383                switch (linetype) {
2384                    case TacticalLines.PL:
2385                        AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), toEnd, T1LineFactor, pt0, pt1, false);
2386                        AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), toEnd, T1LineFactor, ptLast, ptNextToLast, false);
2387                        break;
2388                    case TacticalLines.DECISION_LINE:
2389                        if (tg.get_AS() == null || !tg.get_AS().isEmpty())
2390                            tg.set_AS(GENCLookup.getInstance().get3CharCode(SymbolID.getCountryCode(tg.get_SymbolId())));
2391                        Bitmap bi = getImageModifier(tg);
2392
2393                        POINT2 ptDP1 = lineutility.ExtendLine2Double(pt1, pt0, bi.getWidth() / 2, 0);
2394                        POINT2 ptDP2 = lineutility.ExtendLine2Double(ptNextToLast, ptLast, bi.getWidth() / 2, 0);
2395
2396                        AddIntegralAreaModifier(tg, bi, toEnd, 0, pt0, ptDP1, false);
2397                        AddIntegralAreaModifier(tg, bi, toEnd, 0, ptLast, ptDP2, false);
2398                        break;
2399                    case TacticalLines.BS_LINE:
2400                    case TacticalLines.BBS_LINE:
2401                        if (tg.get_T1() == null || tg.get_T1().isEmpty()) {
2402                            AddIntegralAreaModifier(tg, tg.get_Name(), toEnd, T1LineFactor, pt0, pt1, false);
2403                            AddIntegralAreaModifier(tg, tg.get_Name(), toEnd, T1LineFactor, ptLast, ptNextToLast, false);
2404                        } else {
2405                            if (tg.get_T1().equalsIgnoreCase("1")) {
2406                                for (j = 0; j < tg.Pixels.size() - 1; j++) {
2407                                    AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, 0, tg.Pixels.get(j), tg.Pixels.get(j + 1), false);
2408                                }
2409                            } else if (tg.get_T1().equalsIgnoreCase("2")) {
2410                                AddIntegralAreaModifier(tg, tg.get_Name(), toEnd, T1LineFactor, pt0, pt1, false);
2411                                AddIntegralAreaModifier(tg, tg.get_Name(), toEnd, T1LineFactor, ptLast, ptNextToLast, false);
2412                            } else if (tg.get_T1().equalsIgnoreCase("3")) {
2413                                //either end of the polyline
2414                                dist = lineutility.CalcDistanceDouble(pt0, pt1);
2415                                stringWidth = metrics.stringWidth(tg.get_Name());
2416                                stringWidth /= 2;
2417                                pt2 = lineutility.ExtendAlongLineDouble2(pt1, pt0, dist + stringWidth);
2418                                AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, pt2, pt2, false);
2419                                dist = lineutility.CalcDistanceDouble(ptNextToLast, ptLast);
2420                                pt2 = lineutility.ExtendAlongLineDouble2(ptNextToLast, ptLast, dist + stringWidth);
2421                                AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, pt2, pt2, false);
2422                                //the intermediate points
2423                                for (j = 1; j < tg.Pixels.size() - 1; j++) {
2424                                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, tg.Pixels.get(j), tg.Pixels.get(j), false);
2425                                }
2426                            } else //t1 is set inadvertantly or for other graphics
2427                            {
2428                                AddIntegralAreaModifier(tg, tg.get_Name(), toEnd, T1LineFactor, pt0, pt1, false);
2429                                AddIntegralAreaModifier(tg, tg.get_Name(), toEnd, T1LineFactor, ptLast, ptNextToLast, false);
2430                            }
2431                        }
2432                        break;
2433                    case TacticalLines.BS_AREA:
2434                    case TacticalLines.BBS_AREA:
2435                        AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, ptCenter, ptCenter, false);
2436                        break;
2437                    case TacticalLines.FEBA:
2438                        AddIntegralAreaModifier(tg, label, toEnd, 0, pt0, pt1, false);
2439                        AddIntegralAreaModifier(tg, label, toEnd, 0, ptLast, ptNextToLast, false);
2440                        break;
2441                    // T before label
2442                    case TacticalLines.FSCL:
2443                        pt0 = tg.Pixels.get(0);
2444                        pt1 = tg.Pixels.get(1);
2445                        pt2 = tg.Pixels.get(tg.Pixels.size() - 1);
2446                        pt3 = tg.Pixels.get(tg.Pixels.size() - 2);
2447                        dist = lineutility.CalcDistanceDouble(pt0, pt1);
2448                        dist2 = lineutility.CalcDistanceDouble(pt2, pt3);
2449                        stringWidth = (int) ((double) metrics.stringWidth(tg.get_Name() + " " + label));
2450                        stringWidth2 = (int) ((double) metrics.stringWidth(tg.get_DTG()));
2451                        if (stringWidth2 > stringWidth) {
2452                            stringWidth = stringWidth2;
2453                        }
2454
2455                        if (tg.Pixels.size() == 2) //one segment
2456                        {
2457                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2458                            AddModifier2(tg, tg.get_Name() + " " + label, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2459                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2460                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2461                            if (dist > 3.5 * stringWidth)//was 28stringwidth+5
2462                            {
2463                                pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
2464                                pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
2465                                pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2466                                AddModifier2(tg, tg.get_Name() + " " + label, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2467                                AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2468                                AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2469                            }
2470                        } else //more than one semgent
2471                        {
2472                            double dist3 = lineutility.CalcDistanceDouble(pt0, pt2);
2473                            if (dist > stringWidth + 5 || dist >= dist2 || dist3 > stringWidth + 5) {
2474                                pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2475                                AddModifier2(tg, tg.get_Name() + " " + label, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2476                                AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2477                                AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2478                            }
2479                            if (dist2 > stringWidth + 5 || dist2 > dist || dist3 > stringWidth + 5) {
2480                                pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
2481                                pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
2482                                pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2483                                AddModifier2(tg, tg.get_Name() + " " + label, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2484                                AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2485                                AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2486                            }
2487                        }
2488                        break;
2489                    // T after label
2490                    case TacticalLines.ICL:
2491                    case TacticalLines.NFL:
2492                    case TacticalLines.BCL_REVD:
2493                    case TacticalLines.RFL:
2494                    case TacticalLines.BCL:
2495                        pt0 = tg.Pixels.get(0);
2496                        pt1 = tg.Pixels.get(1);
2497                        pt2 = tg.Pixels.get(tg.Pixels.size() - 1);
2498                        pt3 = tg.Pixels.get(tg.Pixels.size() - 2);
2499                        dist = lineutility.CalcDistanceDouble(pt0, pt1);
2500                        dist2 = lineutility.CalcDistanceDouble(pt2, pt3);
2501                        String TMod = ""; // Don't add parenthesis if T modifier is empty
2502
2503
2504                        int version = SymbolID.getVersion(tg.get_SymbolId());
2505                        if (version < SymbolID.Version_2525E) {
2506                            TMod = tg.get_Name();
2507                            stringWidth = (int) ((double) metrics.stringWidth(TMod + " " + label));
2508                        } else if (version == SymbolID.Version_2525E || version == SymbolID.Version_2525Ech1) {
2509                            if (linetype == TacticalLines.BCL) {
2510                                if (tg.get_Name() != null && !tg.get_Name().isEmpty())
2511                                    TMod = " (" + tg.get_Name() + ")";
2512                                stringWidth = (int) ((double) metrics.stringWidth(label + TMod));
2513                            } else {
2514                                TMod = tg.get_Name();
2515                                stringWidth = (int) ((double) metrics.stringWidth(tg.get_Name() + " " + label));
2516                            }
2517                        } else if ((version == SymbolID.Version_APP6Ech2)) {
2518                            if (tg.get_Name() != null && !tg.get_Name().isEmpty())
2519                                TMod += " " + tg.get_Name();
2520                            if (tg.get_AS() != null && !tg.get_AS().isEmpty())
2521                                TMod += " (" + tg.get_AS() + ")";
2522                            stringWidth = (int) ((double) metrics.stringWidth(label + TMod));
2523
2524                        }
2525
2526                        stringWidth2 = (int) ((double) metrics.stringWidth(tg.get_DTG()));
2527                        if (stringWidth2 > stringWidth) {
2528                            stringWidth = stringWidth2;
2529                        }
2530
2531                        if (tg.Pixels.size() == 2) //one segment
2532                        {
2533                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2534                            AddModifier2(tg, label + TSpace + TMod, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2535                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2536                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2537                            if (dist > 3.5 * stringWidth)//was 28stringwidth+5
2538                            {
2539                                pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
2540                                pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
2541                                pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2542                                AddModifier2(tg, label + TSpace + TMod, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2543                                AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2544                                AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2545                            }
2546                        } else //more than one semgent
2547                        {
2548                            double dist3 = lineutility.CalcDistanceDouble(pt0, pt2);
2549                            if (dist > stringWidth + 5 || dist >= dist2 || dist3 > stringWidth + 5) {
2550                                pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2551                                AddModifier2(tg, label + TSpace + TMod, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2552                                AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2553                                AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2554                            }
2555                            if (dist2 > stringWidth + 5 || dist2 > dist || dist3 > stringWidth + 5) {
2556                                pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
2557                                pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
2558                                pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
2559                                AddModifier2(tg, label + TSpace + TMod, aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
2560                                AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
2561                                AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
2562                            }
2563                        }
2564                        break;
2565                    case TacticalLines.DIRATKSPT:
2566                    case TacticalLines.DIRATKAIR:
2567                    case TacticalLines.DIRATKGND:
2568                        midPt = lineutility.MidPointDouble(pt0, pt1, 0);
2569                        //midPt=lineutility.MidPointDouble(pt0, midPt, 0);
2570                        AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, 0, pt0, midPt, false);
2571                        addDTG(tg, aboveMiddle, csFactor, 2 * csFactor, pt0, pt1, metrics);
2572                        break;
2573                    case TacticalLines.SPT:
2574                    case TacticalLines.FRONTAL_ATTACK:
2575                    case TacticalLines.TURNING_MOVEMENT:
2576                    case TacticalLines.MOVEMENT_TO_CONTACT:
2577                    case TacticalLines.AIRAOA:
2578                    case TacticalLines.AAAAA:
2579                    case TacticalLines.MAIN:
2580                        if (tg.Pixels.size() == 3) //one segment
2581                        {
2582                            midPt = lineutility.MidPointDouble(pt0, pt1, 0);
2583                            AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, aboveMiddle, 0, midPt, midPt, false);
2584                            AddIntegralAreaModifier(tg, tg.get_DTG1(), aboveMiddle, csFactor, midPt, midPt, false);
2585                            AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, 2 * csFactor, midPt, midPt, false);
2586
2587                        } else if (tg.Pixels.size() == 4) //2 segments
2588                        {
2589                            midPt = lineutility.MidPointDouble(pt1, pt2, 0);
2590                            AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, aboveMiddle, 0, midPt, midPt, false);
2591                            AddIntegralAreaModifier(tg, tg.get_DTG1(), aboveMiddle, csFactor, midPt, midPt, false);
2592                            AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, 2 * csFactor, midPt, midPt, false);
2593                        } else // 3 or more segments
2594                        {
2595                            midPt = lineutility.MidPointDouble(pt1, pt2, 0);
2596                            AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, aboveMiddle, -csFactor / 2, midPt, midPt, false);
2597                            AddIntegralAreaModifier(tg, tg.get_DTG1(), aboveMiddle, csFactor / 2, midPt, midPt, false);
2598                            midPt = lineutility.MidPointDouble(pt2, pt3, 0);
2599                            AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, -csFactor / 2, midPt, midPt, false);
2600                        }
2601                        break;
2602                    case TacticalLines.LL:
2603                    case TacticalLines.LOD:
2604                    case TacticalLines.LDLC:
2605                    case TacticalLines.PLD:
2606                    case TacticalLines.RELEASE:
2607                    case TacticalLines.HOL:
2608                    case TacticalLines.BHL:
2609                    case TacticalLines.FCL:
2610                    case TacticalLines.HOLD:
2611                    case TacticalLines.BRDGHD:
2612                    case TacticalLines.HOLD_GE:
2613                    case TacticalLines.BRDGHD_GE:
2614                    case TacticalLines.LOA:
2615                    case TacticalLines.IFF_OFF:
2616                    case TacticalLines.IFF_ON:
2617                        AddIntegralAreaModifier(tg, label, aboveEnd, -csFactor, pt0, pt1, false);
2618                        AddIntegralAreaModifier(tg, label, aboveEnd, -csFactor, ptLast, ptNextToLast, false);
2619                        break;
2620                    case TacticalLines.EWL:
2621                        AddIntegralAreaModifier(tg, label, aboveEnd, -csFactor, pt0, pt1, false);
2622                        AddIntegralAreaModifier(tg, label, aboveEnd, -csFactor, ptLast, ptNextToLast, false);
2623                        tg.set_EchelonSymbol("");
2624                        if (clipRect != null) {
2625                            AddBoundaryModifiers(tg, g2d, clipRect);
2626                        } else {
2627                            AddBoundaryModifiers(tg, g2d, clipArray);
2628                        }
2629                        break;
2630                    case TacticalLines.AIRFIELD:
2631                        ur = new POINT2();
2632                        ul = new POINT2();
2633                        ll = new POINT2();
2634                        lr = new POINT2();
2635                        Modifier2.GetMBR(tg, ul, ur, lr, ll);
2636                        stringWidth = metrics.stringWidth(tg.get_H());
2637                        pt0.x = ur.x + stringWidth / 2 + 1;
2638                        //pt0.x=ptUr.x+1;
2639                        //pt0.y=(ptUr.y+ptLr.y)/2-metrics.getFont().getSize()
2640                        pt0.y = (ur.y + lr.y) / 2 - font.getSize();
2641                        AddIntegralAreaModifier(tg, tg.get_H(), area, csFactor, pt0, pt0, false);
2642                        break;
2643                    case TacticalLines.LAUNCH_AREA:
2644                    case TacticalLines.DEFENDED_AREA_CIRCULAR:
2645                        AddIntegralAreaModifier(tg, label + TDash + tg.get_Name(), area, 0, ptCenter, ptCenter, false);
2646                        break;
2647                    case TacticalLines.JTAA:
2648                    case TacticalLines.SAA:
2649                    case TacticalLines.SGAA:
2650                        addNModifier(tg);
2651                        AddIntegralAreaModifier(tg, label + TDash + tg.get_Name(), area, 0, ptCenter, ptCenter, false);
2652                        addDTG(tg, area, csFactor, 2 * csFactor, ptCenter, ptCenter, metrics);
2653                        break;
2654                    case TacticalLines.FORT:
2655                    case TacticalLines.ZONE:
2656                        AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, ptCenter, ptCenter, false);
2657                        break;
2658                    case TacticalLines.BDZ:
2659                        AddIntegralAreaModifier(tg, label, area, 0, pt0, pt0, false);
2660                        break;
2661                    case TacticalLines.ASSAULT:
2662                    case TacticalLines.ATKPOS:
2663                    case TacticalLines.OBJ:
2664                    case TacticalLines.NAI:
2665                    case TacticalLines.TAI:
2666                    case TacticalLines.BASE_CAMP_REVD:
2667                    case TacticalLines.GUERILLA_BASE_REVD:
2668                    case TacticalLines.ASSY:
2669                    case TacticalLines.EA:
2670                    case TacticalLines.DZ:
2671                    case TacticalLines.EZ:
2672                    case TacticalLines.LZ:
2673                    case TacticalLines.PZ:
2674                    case TacticalLines.AO:
2675                        AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), area, 0, ptCenter, ptCenter, false);
2676                        break;
2677                    case TacticalLines.BASE_CAMP:
2678                    case TacticalLines.GUERILLA_BASE:
2679                        AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), area, -1 * csFactor, ptCenter, ptCenter, false);
2680                        AddModifier(tg, tg.get_H(), area, 0, ptCenter, ptCenter);
2681                        addDTG(tg, area, 1 * csFactor, 2 * csFactor, ptCenter, ptCenter, metrics);
2682                        addNModifier(tg);
2683                        addModifierBottomSegment(tg, tg.get_EchelonSymbol());
2684                        break;
2685                    case TacticalLines.GENERIC_AREA:
2686                        AddIntegralAreaModifier(tg, tg.get_H() + " " + tg.get_Name(), area, -0.5 * csFactor, ptCenter, ptCenter, false);
2687                        addDTG(tg, area, 0.5 * csFactor, 1.5 * csFactor, ptCenter, ptCenter, metrics);
2688                        addNModifier(tg);
2689                        break;
2690                    case TacticalLines.AIRHEAD:
2691                        GetMBR(tg, ul, ur, lr, ll);
2692                        AddIntegralAreaModifier(tg, label, aboveMiddle, csFactor, ll, lr, false);
2693                        break;
2694                    case TacticalLines.AC:
2695                    case TacticalLines.LLTR:
2696                    case TacticalLines.MRR:
2697                    case TacticalLines.SL:
2698                    case TacticalLines.TC:
2699                    case TacticalLines.SAAFR:
2700                    case TacticalLines.SC:
2701                        AddIntegralModifier(tg, "Name: " + tg.get_Name(), aboveMiddle, -7 * csFactor, middleSegment, middleSegment + 1, false);
2702                        AddIntegralModifier(tg, "Width: " + removeDecimal(tg.get_AM()), aboveMiddle, -6 * csFactor, middleSegment, middleSegment + 1, false);
2703                        AddIntegralModifier(tg, "Min Alt: " + tg.get_X(), aboveMiddle, -5 * csFactor, middleSegment, middleSegment + 1, false);
2704                        AddIntegralModifier(tg, "Max Alt: " + tg.get_X1(), aboveMiddle, -4 * csFactor, middleSegment, middleSegment + 1, false);
2705                        AddIntegralModifier(tg, "DTG Start: " + tg.get_DTG(), aboveMiddle, -3 * csFactor, middleSegment, middleSegment + 1, false);
2706                        AddIntegralModifier(tg, "DTG End: " + tg.get_DTG1(), aboveMiddle, -2 * csFactor, middleSegment, middleSegment + 1, false);
2707                        AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, 0, middleSegment, middleSegment + 1, false);
2708                        break;
2709                    case TacticalLines.BEARING_J:
2710                    case TacticalLines.BEARING_RDF:
2711                    case TacticalLines.BEARING:
2712                    case TacticalLines.ELECTRO:
2713                    case TacticalLines.BEARING_EW:
2714                    case TacticalLines.ACOUSTIC:
2715                    case TacticalLines.ACOUSTIC_AMB:
2716                    case TacticalLines.TORPEDO:
2717                    case TacticalLines.OPTICAL:
2718                        midPt = lineutility.MidPointDouble(pt0, pt1, 0);
2719                        AddIntegralAreaModifier(tg, label, aboveMiddle, 0, midPt, midPt, true);
2720                        pt3 = lineutility.ExtendDirectedLine(pt0, pt1, pt1, 3, font.getSize() / 2.0);
2721                        AddIntegralAreaModifier(tg, tg.get_H(), aboveMiddle, 1, pt3, pt3, true);
2722                        break;
2723                    case TacticalLines.ACA:
2724                        AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), area, -3 * csFactor, ptCenter, ptCenter, false);
2725                        AddIntegralAreaModifier(tg, tg.get_T1(), area, -2 * csFactor, ptCenter, ptCenter, false);
2726                        AddIntegralAreaModifier(tg, "MIN ALT: " + tg.get_X(), area, -1 * csFactor, ptCenter, ptCenter, false, "H");
2727                        AddIntegralAreaModifier(tg, "MAX ALT: " + tg.get_X1(), area, 0, ptCenter, ptCenter, false, "H1");
2728                        AddIntegralAreaModifier(tg, "GRID " + tg.get_Location(), area, 1 * csFactor, ptCenter, ptCenter, false, "H2");
2729                        AddModifier2(tg, "EFF " + tg.get_DTG() + WDash, area, 2 * csFactor, ptCenter, ptCenter, false, "W");
2730                        AddModifier2(tg, tg.get_DTG1(), area, 3 * csFactor, ptCenter, ptCenter, false, "W1");
2731                        break;
2732                    case TacticalLines.MFP:
2733                        pt0 = tg.Pixels.get(middleSegment);
2734                        pt1 = tg.Pixels.get(middleSegment + 1);
2735                        AddIntegralModifier(tg, label, aboveMiddle, 0, middleSegment, middleSegment + 1, true);
2736                        AddIntegralModifier(tg, tg.get_DTG() + WDash, aboveEnd, 1 * csFactor, 0, 1, false);
2737                        AddIntegralModifier(tg, tg.get_DTG1(), aboveEnd, 2 * csFactor, 0, 1, false);
2738                        break;
2739                    case TacticalLines.LINTGT:
2740                        AddIntegralModifier(tg, ap, aboveMiddle, -0.7 * csFactor, middleSegment, middleSegment + 1, false);
2741                        break;
2742                    case TacticalLines.LINTGTS:
2743                        AddIntegralModifier(tg, ap, aboveMiddle, -0.7 * csFactor, middleSegment, middleSegment + 1, false);
2744                        AddIntegralModifier(tg, label, aboveMiddle, 0.7 * csFactor, middleSegment, middleSegment + 1, false);
2745                        break;
2746                    case TacticalLines.FPF:
2747                        AddIntegralModifier(tg, ap, aboveMiddle, -0.7 * csFactor, 0, 1, false);
2748                        AddIntegralModifier(tg, label, aboveMiddle, .7 * csFactor, 0, 1, false);
2749                        AddIntegralModifier(tg, tg.get_T1(), aboveMiddle, 1.7 * csFactor, 0, 1, false);
2750                        AddIntegralModifier(tg, v, aboveMiddle, 2.7 * csFactor, 0, 1, false);
2751                        break;
2752                    case TacticalLines.AT:
2753                        AddIntegralAreaModifier(tg, ap, area, 0, ptCenter, ptCenter, false);
2754                        break;
2755                    case TacticalLines.RECTANGULAR:
2756                    case TacticalLines.CIRCULAR:
2757                        AddIntegralAreaModifier(tg, ap, area, 0, pt0, pt0, false);
2758                        break;
2759                    case TacticalLines.PBS_CIRCLE:
2760                    case TacticalLines.PBS_ELLIPSE:
2761                    case TacticalLines.PBS_RECTANGLE:
2762                    case TacticalLines.BBS_POINT:
2763                        AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, pt0, pt0, false);
2764                        break;
2765                    case TacticalLines.RECTANGULAR_TARGET:
2766                        stringWidth = metrics.stringWidth(tg.get_Name());
2767                        POINT2 offsetCenterPoint = new POINT2(ptCenter.x + ((double) stringWidth) / 2.0, ptCenter.y);
2768                        AddIntegralAreaModifier(tg, tg.get_Name(), area, -1 * csFactor, offsetCenterPoint, offsetCenterPoint, false);
2769                        break;
2770                    case TacticalLines.SMOKE:
2771                        AddIntegralAreaModifier(tg, ap, area, -csFactor, ptCenter, ptCenter, false);
2772                        AddIntegralAreaModifier(tg, label, area, 0, ptCenter, ptCenter, false);
2773                        addDTG(tg, area, 1 * csFactor, 2 * csFactor, ptCenter, ptCenter, metrics);
2774                        break;
2775                    case TacticalLines.LINE:
2776                        AddIntegralModifier(tg, tg.get_Name(), aboveMiddle, csFactor, middleSegment, middleSegment + 1, false);
2777                        break;
2778                    case TacticalLines.MINED:
2779                        if (tg.isHostile()) {
2780                            pt1 = lineutility.MidPointDouble(pt0, pt1, 0);
2781                            AddIntegralAreaModifier(tg, tg.get_N(), aboveMiddle, 0, pt0, pt1, true);
2782                            if (middleSegment != 0) {
2783                                pt0 = tg.Pixels.get(middleSegment);
2784                                pt1 = tg.Pixels.get(middleSegment + 1);
2785                                pt1 = lineutility.MidPointDouble(pt0, pt1, 0);
2786                                AddIntegralAreaModifier(tg, tg.get_N(), aboveMiddle, 0, pt0, pt1, true);
2787                            }
2788                        }
2789                        GetMBR(tg, ul, ur, lr, ll);
2790                        AddIntegralAreaModifier(tg, tg.get_H(), aboveMiddle, -1.5 * csFactor, ul, ur, false);
2791                        AddIntegralAreaModifier(tg, tg.get_DTG(), aboveMiddle, 1.5 * csFactor, ll, lr, false);
2792                        addModifierOnLine("M", tg);
2793                        AddIntegralAreaModifier(tg, getImageModifier(tg), areaImage, 0, ptCenter, ptCenter, false);
2794                        break;
2795                    case TacticalLines.FENCED:
2796                        if (tg.isHostile()) {
2797                            pt1 = lineutility.MidPointDouble(pt0, pt1, 0);
2798                            AddIntegralAreaModifier(tg, tg.get_N(), aboveMiddle, 0, pt0, pt1, true);
2799                            if (middleSegment != 0) {
2800                                pt0 = tg.Pixels.get(middleSegment);
2801                                pt1 = tg.Pixels.get(middleSegment + 1);
2802                                pt1 = lineutility.MidPointDouble(pt0, pt1, 0);
2803                                AddIntegralAreaModifier(tg, tg.get_N(), aboveMiddle, 0, pt0, pt1, true);
2804                            }
2805                        }
2806                        addModifierOnLine("M", tg);
2807                        AddIntegralAreaModifier(tg, getImageModifier(tg), areaImage, 0, ptCenter, ptCenter, false);
2808                        break;
2809                    case TacticalLines.ASLTXING:
2810                        if (tg.Pixels.get(1).y > tg.Pixels.get(0).y) {
2811                            pt0 = tg.Pixels.get(1);
2812                            pt1 = tg.Pixels.get(3);
2813                            pt2 = tg.Pixels.get(0);
2814                            pt3 = tg.Pixels.get(2);
2815                        } else {
2816                            pt0 = tg.Pixels.get(0);
2817                            pt1 = tg.Pixels.get(2);
2818                            pt2 = tg.Pixels.get(1);
2819                            pt3 = tg.Pixels.get(3);
2820                        }
2821                        pt2 = lineutility.ExtendAlongLineDouble2(pt0, pt2, -20);
2822                        pt3 = lineutility.ExtendAlongLineDouble2(pt1, pt3, -20);
2823                        addDTG(tg, aboveMiddle, 0, csFactor, pt2, pt3, metrics);
2824                        break;
2825                    case TacticalLines.SERIES:
2826                    case TacticalLines.DRCL:
2827                        addModifierTopSegment(tg, tg.get_Name());
2828                        break;
2829                    case TacticalLines.STRIKWARN:
2830                        AddIntegralModifier(tg, "1", aboveMiddle, 0, index, index + 1, true);
2831                        AddIntegralModifier(tg, "2", aboveMiddle, 0, size / 2, size / 2 + 1, true);
2832                        break;
2833                    case TacticalLines.SCREEN:
2834                    case TacticalLines.COVER:
2835                    case TacticalLines.GUARD:
2836                        if (tg.Pixels.size() == 4) {
2837                            pt1 = new POINT2(tg.Pixels.get(1));
2838                            pt2 = new POINT2(tg.Pixels.get(2));
2839                            AddIntegralAreaModifier(tg, label, area, 0, pt1, pt1, true);
2840                            AddIntegralAreaModifier(tg, label, area, 0, pt2, pt2, true);
2841                        } else {
2842                            stringHeight = (int) (0.5 * (double) font.getSize());
2843                            dAngle0 = Math.atan2(tg.Pixels.get(0).y - tg.Pixels.get(1).y, tg.Pixels.get(0).x - tg.Pixels.get(1).x);
2844                            dAngle1 = Math.atan2(tg.Pixels.get(0).y - tg.Pixels.get(2).y, tg.Pixels.get(0).x - tg.Pixels.get(2).x);
2845                            pt0 = new POINT2(tg.Pixels.get(0));
2846                            pt0.x -= 30 * Math.cos(dAngle0);
2847                            pt0.y -= 30 * Math.sin(dAngle0) + stringHeight;
2848                            pt1 = new POINT2(tg.Pixels.get(0));
2849                            pt1.x -= 30 * Math.cos(dAngle1);
2850                            pt1.y -= 30 * Math.sin(dAngle1) + stringHeight;
2851                            AddIntegralAreaModifier(tg, label, area, 0, pt0, pt0, true);
2852                            AddIntegralAreaModifier(tg, label, area, 0, pt1, pt1, true);
2853                        }
2854                        break;
2855                    case TacticalLines.MSR_ONEWAY:
2856                    case TacticalLines.ASR_ONEWAY:
2857                    case TacticalLines.TRAFFIC_ROUTE_ONEWAY:
2858                    case TacticalLines.MSR_TWOWAY:
2859                    case TacticalLines.ASR_TWOWAY:
2860                    case TacticalLines.MSR_ALT:
2861                    case TacticalLines.ASR_ALT:
2862                    case TacticalLines.TRAFFIC_ROUTE_ALT:
2863                        stringWidth = (int) (1.5 * (double) metrics.stringWidth(label + TSpace + tg.get_Name()));
2864                        double arrowOffset = 10 * DPIScaleFactor;
2865                        if (linetype == TacticalLines.MSR_TWOWAY || linetype == TacticalLines.ASR_TWOWAY)
2866                            arrowOffset = 25 * DPIScaleFactor;
2867                        boolean isAlt = linetype == TacticalLines.MSR_ALT || linetype == TacticalLines.ASR_ALT || linetype == TacticalLines.TRAFFIC_ROUTE_ALT;
2868                        if (isAlt) {
2869                            stringWidth2 = (int) (1.5 * (double) metrics.stringWidth("ALT"));
2870                            if (stringWidth2 > stringWidth) {
2871                                stringWidth = stringWidth2;
2872                            }
2873                        }
2874
2875                        foundSegment = false;
2876                        //acevedo - 11/30/2017 - adding option to render only 2 labels.
2877                        if (RendererSettings.getInstance().getTwoLabelOnly() == false) {
2878                            for (j = 0; j < tg.Pixels.size() - 1; j++) {
2879                                pt0 = tg.Pixels.get(j);
2880                                pt1 = tg.Pixels.get(j + 1);
2881                                dist = lineutility.CalcDistanceDouble(pt0, pt1);
2882                                int arrowSide = arraysupport.SupplyRouteArrowSide(pt0, pt1);
2883                                if (dist < stringWidth) {
2884                                    continue;
2885                                } else {
2886                                    if (arrowSide == 1 || arrowSide == 2) {
2887                                        // Shift points to account for arrow shift with DPI
2888                                        pt0 = lineutility.ExtendDirectedLine(pt1, pt0, pt0, arrowSide, arrowOffset);
2889                                        pt1 = lineutility.ExtendDirectedLine(pt1, pt0, pt1, arrowSide, arrowOffset);
2890                                        AddModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -1.7 * csFactor, pt0, pt1);
2891                                        if (isAlt)
2892                                            AddModifier(tg, "ALT", aboveMiddle, 0, pt0, pt1);
2893                                    } else {
2894                                        AddModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1);
2895                                        if (isAlt) {
2896                                            pt0 = lineutility.ExtendDirectedLine(pt1, pt0, pt0, arrowSide, arrowOffset);
2897                                            pt1 = lineutility.ExtendDirectedLine(pt1, pt0, pt1, arrowSide, arrowOffset);
2898                                            AddModifier(tg, "ALT", aboveMiddle, 0, pt0, pt1);
2899                                        }
2900                                    }
2901                                    foundSegment = true;
2902                                }
2903                            }
2904                            if (foundSegment == false) {
2905                                pt0 = tg.Pixels.get(middleSegment);
2906                                pt1 = tg.Pixels.get(middleSegment + 1);
2907                                int arrowSide = arraysupport.SupplyRouteArrowSide(pt0, pt1);
2908                                if (arrowSide == 1 || arrowSide == 2) {
2909                                    // Shift points to account for arrow shift with DPI
2910                                    pt0 = lineutility.ExtendDirectedLine(pt1, pt0, pt0, arrowSide, arrowOffset);
2911                                    pt1 = lineutility.ExtendDirectedLine(pt1, pt0, pt1, arrowSide, arrowOffset);
2912                                    AddModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -1.7 * csFactor, pt0, pt1);
2913                                    if (isAlt)
2914                                        AddModifier(tg, "ALT", aboveMiddle, 0, pt0, pt1);
2915                                } else {
2916                                    AddModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1);
2917                                    if (isAlt) {
2918                                        pt0 = lineutility.ExtendDirectedLine(pt1, pt0, pt0, arrowSide, arrowOffset);
2919                                        pt1 = lineutility.ExtendDirectedLine(pt1, pt0, pt1, arrowSide, arrowOffset);
2920                                        AddModifier(tg, "ALT", aboveMiddle, 0, pt0, pt1);
2921                                    }
2922                                }
2923                            }
2924                        } else {
2925                            // 2 labels one to the north and the other to the south of graphic.
2926                            northestPtIndex = 0;
2927                            northestPt = tg.Pixels.get(northestPtIndex);
2928                            southestPtIndex = 0;
2929                            southestPt = tg.Pixels.get(southestPtIndex);
2930
2931                            for (j = 0; j < tg.Pixels.size() - 1; j++) {
2932                                pt0 = tg.Pixels.get(j);
2933                                if (pt0.y >= northestPt.y) {
2934                                    northestPt = pt0;
2935                                    northestPtIndex = j;
2936                                }
2937                                if (pt0.y <= southestPt.y) {
2938                                    southestPt = pt0;
2939                                    southestPtIndex = j;
2940                                }
2941                            }
2942
2943                            AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -1.7 * csFactor, northestPtIndex, northestPtIndex + 1, false);
2944                            if (isAlt)
2945                                AddIntegralModifier(tg, "ALT", aboveMiddle, -0.7 * csFactor, northestPtIndex, northestPtIndex + 1, false);
2946
2947                            if (northestPtIndex != southestPtIndex) {
2948                                AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -1.7 * csFactor, southestPtIndex, southestPtIndex + 1, false);
2949                                if (isAlt)
2950                                    AddIntegralModifier(tg, "ALT", aboveMiddle, -0.7 * csFactor, southestPtIndex, southestPtIndex + 1, false);
2951                            }
2952                        }//else
2953                        break;
2954                    case TacticalLines.DHA_REVD:
2955                        AddIntegralAreaModifier(tg, "DETAINEE", area, -1.5 * csFactor, ptCenter, ptCenter, false);
2956                        AddIntegralAreaModifier(tg, "HOLDING", area, -0.5 * csFactor, ptCenter, ptCenter, false);
2957                        AddIntegralAreaModifier(tg, "AREA", area, 0.5 * csFactor, ptCenter, ptCenter, false);
2958                        AddIntegralAreaModifier(tg, tg.get_Name(), area, 1.5 * csFactor, ptCenter, ptCenter, false);
2959                        break;
2960                    case TacticalLines.EPW:
2961                        AddIntegralAreaModifier(tg, "EPW", area, -1.5 * csFactor, ptCenter, ptCenter, false);
2962                        AddIntegralAreaModifier(tg, "HOLDING", area, -0.5 * csFactor, ptCenter, ptCenter, false);
2963                        AddIntegralAreaModifier(tg, "AREA", area, 0.5 * csFactor, ptCenter, ptCenter, false);
2964                        AddIntegralAreaModifier(tg, tg.get_Name(), area, 1.5 * csFactor, ptCenter, ptCenter, false);
2965                        break;
2966                    case TacticalLines.UXO:
2967                        addModifierOnLine("UXO", tg, true);
2968                        break;
2969                    case TacticalLines.GENERAL:
2970                        addNModifier(tg);
2971                        break;
2972                    case TacticalLines.DHA:
2973                    case TacticalLines.KILL_ZONE:
2974                    case TacticalLines.FARP:
2975                        AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, false);
2976                        AddIntegralAreaModifier(tg, tg.get_Name(), area, 0.5 * csFactor, ptCenter, ptCenter, false);
2977                        break;
2978                    case TacticalLines.BSA:
2979                    case TacticalLines.DSA:
2980                    case TacticalLines.CSA:
2981                    case TacticalLines.RSA:
2982                        AddIntegralAreaModifier(tg, label, area, 0, ptCenter, ptCenter, false);
2983                        break;
2984                    case TacticalLines.RHA:
2985                        AddIntegralAreaModifier(tg, "REFUGEE", area, -1.5 * csFactor, ptCenter, ptCenter, false);
2986                        AddIntegralAreaModifier(tg, "HOLDING", area, -0.5 * csFactor, ptCenter, ptCenter, false);
2987                        AddIntegralAreaModifier(tg, "AREA", area, 0.5 * csFactor, ptCenter, ptCenter, false);
2988                        AddIntegralAreaModifier(tg, tg.get_Name(), area, 1.5 * csFactor, ptCenter, ptCenter, false);
2989                        break;
2990                    case TacticalLines.MSR:
2991                    case TacticalLines.ASR:
2992                    case TacticalLines.TRAFFIC_ROUTE:
2993                        //AddIntegralModifier(tg, label + tg.get_Name(), aboveMiddle, -1*csFactor, middleSegment, middleSegment + 1,false);
2994                        foundSegment = false;
2995                        //acevedo - 11/30/2017 - adding option to render only 2 labels.
2996                        if (RendererSettings.getInstance().getTwoLabelOnly() == false) {
2997                            for (j = 0; j < tg.Pixels.size() - 1; j++) {
2998                                pt0 = tg.Pixels.get(j);
2999                                pt1 = tg.Pixels.get(j + 1);
3000                                stringWidth = (int) (1.5 * (double) metrics.stringWidth(label + TSpace + tg.get_Name()));
3001                                dist = lineutility.CalcDistanceDouble(pt0, pt1);
3002                                if (dist < stringWidth) {
3003                                    continue;
3004                                } else {
3005                                    AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -1 * csFactor, j, j + 1, false);
3006                                    foundSegment = true;
3007                                }
3008                            }
3009                            if (foundSegment == false) {
3010                                AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, -1 * csFactor, middleSegment, middleSegment + 1, false);
3011                            }
3012                        } else {
3013                            // 2 labels one to the north and the other to the south of graphic.
3014                            for (j = 0; j < tg.Pixels.size(); j++) {
3015                                pt0 = tg.Pixels.get(j);
3016
3017                                if (northestPt == null) {
3018                                    northestPt = pt0;
3019                                    northestPtIndex = j;
3020                                }
3021                                if (southestPt == null) {
3022                                    southestPt = pt0;
3023                                    southestPtIndex = j;
3024                                }
3025                                if (pt0.y >= northestPt.y) {
3026                                    northestPt = pt0;
3027                                    northestPtIndex = j;
3028                                }
3029
3030                                if (pt0.y <= southestPt.y) {
3031                                    southestPt = pt0;
3032                                    southestPtIndex = j;
3033                                }
3034                            }//for
3035                            middleSegment = northestPtIndex;
3036                            middleSegment2 = southestPtIndex;
3037
3038                            if (middleSegment == tg.Pixels.size() - 1) {
3039                                middleSegment -= 1;
3040                            }
3041                            if (middleSegment2 == tg.Pixels.size() - 1) {
3042                                middleSegment2 -= 1;
3043                            }
3044                            if (middleSegment == middleSegment2) {
3045                                middleSegment2 -= 1;
3046                            }
3047
3048                            // if (middleSegment != middleSegment2) {
3049                            AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, 0, middleSegment, middleSegment + 1, false);
3050                            //}
3051                            AddIntegralModifier(tg, label + TSpace + tg.get_Name(), aboveMiddle, 0, middleSegment2, middleSegment2 + 1, false);
3052
3053                        }//else
3054                        break;
3055                    case TacticalLines.TRIP:
3056                        foundSegment = false;
3057                        stringWidth = (int) (1.5 * (double) metrics.stringWidth(label));
3058                        for (j = 0; j < tg.Pixels.size() - 1; j++) {
3059                            pt0 = tg.Pixels.get(j);
3060                            pt1 = tg.Pixels.get(j + 1);
3061                            midPt = lineutility.MidPointDouble(pt0, pt1, 0);
3062                            dist = lineutility.CalcDistanceDouble(pt0, pt1);
3063                            if (dist > stringWidth) {
3064                                AddModifier2(tg, label, aboveMiddle, -0.7 * csFactor, midPt, midPt, false);
3065                                foundSegment = true;
3066                            }
3067                        }
3068                        if (!foundSegment) {
3069                            midPt = lineutility.MidPointDouble(tg.Pixels.get(middleSegment), tg.Pixels.get(middleSegment + 1), 0);
3070                            AddModifier2(tg, label, aboveMiddle, -0.7 * csFactor, midPt, midPt, false);
3071                        }
3072                        break;
3073                    case TacticalLines.GAP:
3074                        if (tg.Pixels.get(1).y > tg.Pixels.get(0).y) {
3075                            pt0 = tg.Pixels.get(1);
3076                            pt1 = tg.Pixels.get(3);
3077                            pt2 = tg.Pixels.get(0);
3078                            pt3 = tg.Pixels.get(2);
3079                        } else {
3080                            pt0 = tg.Pixels.get(0);
3081                            pt1 = tg.Pixels.get(2);
3082                            pt2 = tg.Pixels.get(1);
3083                            pt3 = tg.Pixels.get(3);
3084                        }
3085                        pt2 = lineutility.ExtendAlongLineDouble2(pt0, pt2, -20);
3086                        pt3 = lineutility.ExtendAlongLineDouble2(pt1, pt3, -20);
3087                        AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, 0, pt0, pt1, false);
3088                        addDTG(tg, aboveMiddle, 0, csFactor, pt2, pt3, metrics);
3089                        break;
3090                    case TacticalLines.BIO:
3091                    case TacticalLines.BIOT:
3092                    case TacticalLines.CHEM:
3093                    case TacticalLines.CHEMT:
3094                    case TacticalLines.NUC:
3095                    case TacticalLines.RAD:
3096                    case TacticalLines.RADT:
3097                        AddIntegralAreaModifier(tg, getImageModifier(tg), areaImage, 0, ptCenter, ptCenter, false);
3098                        break;
3099                    case TacticalLines.ANCHORAGE_LINE:
3100                        AddIntegralAreaModifier(tg, getImageModifier(tg), aboveMiddle, -0.15 * csFactor, tg.Pixels.get(middleSegment), tg.Pixels.get(middleSegment + 1), false);
3101                        break;
3102                    case TacticalLines.ANCHORAGE_AREA:
3103                        // Add anchor on segment with lowest midpoint
3104                        y = pt0.y + pt1.y;
3105                        index = 0;
3106                        for (j = 1; j < size - 1; j++) {
3107                            if (y < tg.Pixels.get(j).y + tg.Pixels.get(j + 1).y) {
3108                                index = j;
3109                                y = tg.Pixels.get(index).y + tg.Pixels.get(index + 1).y;
3110                            }
3111                        }
3112                        AddIntegralAreaModifier(tg, getImageModifier(tg), aboveMiddle, -0.25 * csFactor, tg.Pixels.get(index), tg.Pixels.get(index + 1), false);
3113                        break;
3114                    case TacticalLines.MINE_LINE:
3115                        AddIntegralAreaModifier(tg, getImageModifier(tg), aboveMiddle, -0.2 * csFactor, tg.Pixels.get(middleSegment), tg.Pixels.get(middleSegment + 1), false);
3116                        if (tg.isHostile()) {
3117                            AddIntegralAreaModifier(tg, tg.get_N(), toEnd, 0.0, pt0, pt1, false);
3118                            AddIntegralAreaModifier(tg, tg.get_N(), toEnd, 0.0, ptLast, ptNextToLast, false);
3119                        }
3120                        break;
3121                    case TacticalLines.DEPICT:
3122                        GetMBR(tg, ul, ur, lr, ll);
3123                        addNModifier(tg);
3124                        AddIntegralAreaModifier(tg, getImageModifier(tg), areaImage, 0, ptCenter, ptCenter, false);
3125                        break;
3126                    case TacticalLines.FFA:
3127                    case TacticalLines.RFA:
3128                    case TacticalLines.NFA:
3129                        AddIntegralAreaModifier(tg, label, area, -1 * csFactor, ptCenter, ptCenter, false);
3130                        AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, ptCenter, ptCenter, false);
3131                        addDTG(tg, area, 1 * csFactor, 2 * csFactor, ptCenter, ptCenter, metrics);
3132                        break;
3133                    case TacticalLines.PAA:
3134                        addModifierOnLine("PAA", tg);
3135                        AddIntegralAreaModifier(tg, tg.get_Name(), area, -0.5 * csFactor, ptCenter, ptCenter, false);
3136                        addDTG(tg, area, 0.5 * csFactor, 1.5 * csFactor, ptCenter, ptCenter, metrics);
3137                        break;
3138                    case TacticalLines.FSA:
3139                        AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), area, -0.5 * csFactor, ptCenter, ptCenter, false);
3140                        addDTG(tg, area, 0.5 * csFactor, 1.5 * csFactor, ptCenter, ptCenter, metrics);
3141                        break;
3142                    case TacticalLines.ATI:
3143                    case TacticalLines.CFFZ:
3144                    case TacticalLines.CFZ:
3145                    case TacticalLines.TBA:
3146                    case TacticalLines.TVAR:
3147                    case TacticalLines.ZOR:
3148                    case TacticalLines.DA:
3149                    case TacticalLines.SENSOR:
3150                    case TacticalLines.CENSOR:
3151                    case TacticalLines.KILLBOXBLUE:
3152                    case TacticalLines.KILLBOXPURPLE:
3153                        AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, false);
3154                        AddIntegralAreaModifier(tg, tg.get_Name(), area, 0.5 * csFactor, ptCenter, ptCenter, false);
3155
3156                        //DTG label top left point of BBOX
3157                        /*GetMBR(tg, ul, ur, lr, ll);
3158                        POINT2 ptLeft = ul;
3159                        POINT2 ptRight = ur;
3160                        //if (tg.get_Client().equalsIgnoreCase("ge")) {
3161                        //    ptLeft.x -= font.getSize() / 2;
3162                        //    ptRight.x -= font.getSize() / 2;}
3163
3164                        AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, toEnd, 0.5 * csFactor, ptLeft, ptRight, false, "W");
3165                        AddIntegralAreaModifier(tg, tg.get_DTG1(), toEnd, 1.5 * csFactor, ptLeft, ptRight, false, "W1");//*/
3166
3167                        //DTG Labels at highest left-of-center point
3168                        //Could be more expensive than the bbox approximation above if there are a lot of points.
3169                        POINT2 highest = getHighestPointLeftOfCenter(tg.Pixels,ptCenter);
3170                        AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, toEnd, 0.5 * csFactor, highest, new POINT2(highest.x + 0.001, highest.y), false, "W");
3171                        AddIntegralAreaModifier(tg, tg.get_DTG1(), toEnd, 1.5 * csFactor, highest, new POINT2(highest.x + 0.001, highest.y), false, "W1");
3172                        break;
3173                    case TacticalLines.BATTLE:
3174                    case TacticalLines.STRONG:
3175                        AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, ptCenter, ptCenter, false);
3176                        addModifierBottomSegment(tg, tg.get_EchelonSymbol());
3177                        break;
3178                    case TacticalLines.PNO:
3179                        AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), area, 0, ptCenter, ptCenter, false);
3180                        addModifierBottomSegment(tg, tg.get_EchelonSymbol());
3181                        addNModifier(tg);
3182                        break;
3183                    case TacticalLines.WFZ_REVD:
3184                        AddIntegralAreaModifier(tg, label, area, -1.5 * csFactor, ptCenter, ptCenter, true);
3185                        AddIntegralAreaModifier(tg, tg.get_Name(), area, -0.5 * csFactor, ptCenter, ptCenter, true);
3186                        AddIntegralAreaModifier(tg, "TIME FROM: " + tg.get_DTG(), area, 0.5 * csFactor, ptCenter, ptCenter, true, "W");
3187                        AddIntegralAreaModifier(tg, "TIME TO: " + tg.get_DTG1(), area, 1.5 * csFactor, ptCenter, ptCenter, true, "W1");
3188                        break;
3189                    case TacticalLines.WFZ:
3190                        AddIntegralAreaModifier(tg, label, area, -2.5 * csFactor, ptCenter, ptCenter, true);
3191                        AddIntegralAreaModifier(tg, tg.get_Name(), area, -1.5 * csFactor, ptCenter, ptCenter, true);
3192                        AddIntegralAreaModifier(tg, "TIME FROM: " + tg.get_DTG(), area, -0.5 * csFactor, ptCenter, ptCenter, true, "W");
3193                        AddIntegralAreaModifier(tg, "TIME TO: " + tg.get_DTG1(), area, 0.5 * csFactor, ptCenter, ptCenter, true, "W1");
3194                        AddIntegralAreaModifier(tg, "MIN ALT: " + tg.get_X(), area, 1.5 * csFactor, ptCenter, ptCenter, false, "H");
3195                        AddIntegralAreaModifier(tg, "MAX ALT: " + tg.get_X1(), area, 2.5, ptCenter, ptCenter, false, "H1");
3196                        break;
3197                    case TacticalLines.OBSFAREA:
3198                        AddIntegralAreaModifier(tg, label, area, -1.5 * csFactor, ptCenter, ptCenter, false);
3199                        AddIntegralAreaModifier(tg, tg.get_Name(), area, -0.5 * csFactor, ptCenter, ptCenter, false);
3200                        AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, area, 0.5 * csFactor, ptCenter, ptCenter, false, "W");
3201                        AddIntegralAreaModifier(tg, tg.get_DTG1(), area, 1.5 * csFactor, ptCenter, ptCenter, false, "W1");
3202                        break;
3203                    case TacticalLines.OBSAREA:
3204                        AddIntegralAreaModifier(tg, tg.get_Name(), area, -1 * csFactor, ptCenter, ptCenter, true);
3205                        AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, area, 0, ptCenter, ptCenter, true, "W");
3206                        AddIntegralAreaModifier(tg, tg.get_DTG1(), area, 1 * csFactor, ptCenter, ptCenter, true, "W1");
3207                        break;
3208                    case TacticalLines.ROZ:
3209                    case TacticalLines.AARROZ:
3210                    case TacticalLines.UAROZ:
3211                    case TacticalLines.WEZ:
3212                    case TacticalLines.FEZ:
3213                    case TacticalLines.JEZ:
3214                    case TacticalLines.FAADZ:
3215                    case TacticalLines.HIDACZ:
3216                    case TacticalLines.MEZ:
3217                    case TacticalLines.LOMEZ:
3218                    case TacticalLines.HIMEZ:
3219                        AddIntegralAreaModifier(tg, label, area, -2.5, ptCenter, ptCenter, false, "");
3220                        AddIntegralAreaModifier(tg, tg.get_Name(), area, -1.5, ptCenter, ptCenter, false, "T");
3221                        AddIntegralAreaModifier(tg, "MIN ALT: " + tg.get_X(), area, -0.5, ptCenter, ptCenter, false, "H");
3222                        AddIntegralAreaModifier(tg, "MAX ALT: " + tg.get_X1(), area, 0.5, ptCenter, ptCenter, false, "H1");
3223                        AddIntegralAreaModifier(tg, "TIME FROM: " + tg.get_DTG(), area, 1.5, ptCenter, ptCenter, false, "W");
3224                        AddIntegralAreaModifier(tg, "TIME TO: " + tg.get_DTG1(), area, 2.5, ptCenter, ptCenter, false, "W1");
3225                        break;
3226                    case TacticalLines.ENCIRCLE:
3227                        if (tg.isHostile()) {
3228                            AddIntegralModifier(tg, tg.get_N(), aboveMiddle, 0, 0, 1, true);
3229                            AddIntegralModifier(tg, tg.get_N(), aboveMiddle, 0, middleSegment, middleSegment + 1, true);
3230                        }
3231                        break;
3232                    case TacticalLines.LAA:
3233                        AddIntegralAreaModifier(tg, getImageModifier(tg), areaImage, 0, ptCenter, ptCenter, false);
3234                        AddIntegralAreaModifier(tg, label, area, -1 * csFactor, ptCenter, ptCenter, false);
3235                        break;
3236                    case TacticalLines.BOUNDARY:
3237                        if (clipRect != null) {
3238                            AddBoundaryModifiers(tg, g2d, clipRect);
3239                        } else {
3240                            AddBoundaryModifiers(tg, g2d, clipArray);
3241                        }
3242                        break;
3243                    case TacticalLines.CFL:
3244                        stringWidth = (int) ((double) metrics.stringWidth(label + TSpace + tg.get_Name()));
3245                        stringWidth2 = (int) ((double) metrics.stringWidth(tg.get_DTG() + WDash + tg.get_DTG1()));
3246                        if (stringWidth2 > stringWidth) {
3247                            stringWidth = stringWidth2;
3248                        }
3249                        pt0 = new POINT2(tg.Pixels.get(middleSegment));
3250                        pt1 = new POINT2(tg.Pixels.get(middleSegment + 1));
3251                        getPixelsMiddleSegment(tg, stringWidth, pt0, pt1);
3252                        AddModifier2(tg, label + TSpace + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
3253                        addDTG(tg, aboveMiddle, 0.7 * csFactor, 1.7 * csFactor, pt0, pt1, metrics);
3254                        break;
3255                    case TacticalLines.FLOT:
3256                        if (tg.get_H().equals("1")) {
3257                            label = "LC";
3258                        } else if (tg.get_H().equals("2")) {
3259                            label = "";
3260                        }
3261                        AddIntegralAreaModifier(tg, label, toEnd, 0, pt0, pt1, false);
3262                        AddIntegralAreaModifier(tg, label, toEnd, 0, ptLast, ptNextToLast, false);
3263
3264                        if (tg.isHostile()) {
3265                            AddIntegralAreaModifier(tg, tg.get_N(), toEnd, -1 * csFactor, pt0, pt1, false);
3266                            AddIntegralAreaModifier(tg, tg.get_N(), toEnd, -1 * csFactor, ptLast, ptNextToLast, false);
3267                        }
3268                        break;
3269                    case TacticalLines.LC:
3270                        double shiftFactor = 1d;
3271                        if (shiftLines) {
3272                            shiftFactor = 0.5d;
3273                        }
3274                        if (tg.isHostile()) {
3275                            if (pt0.x < pt1.x) {
3276                                TLineFactor = -shiftFactor;//was -1
3277                            } else {
3278                                TLineFactor = shiftFactor;//was 1
3279                            }
3280                            AddIntegralAreaModifier(tg, tg.get_N(), toEnd, TLineFactor, pt0, pt1, false);
3281                            if (ptNextToLast.x < ptLast.x) {
3282                                TLineFactor = -shiftFactor;//was -1
3283                            } else {
3284                                TLineFactor = shiftFactor;//was 1
3285                            }
3286                            AddIntegralAreaModifier(tg, tg.get_N(), toEnd, TLineFactor, ptLast, ptNextToLast, false);
3287                        }
3288                        AddIntegralAreaModifier(tg, label, toEnd, 0, pt0, pt1, false);
3289                        AddIntegralAreaModifier(tg, label, toEnd, 0, ptLast, ptNextToLast, false);
3290                        break;
3291                    case TacticalLines.CATK:
3292                        AddIntegralModifier(tg, label, aboveMiddle, 0, 1, 0, false);
3293                        break;
3294                    case TacticalLines.CATKBYFIRE:
3295                        stringWidth = (int) (1.5 * (double) metrics.stringWidth(label));
3296                        pt2 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
3297                        AddModifier2(tg, label, aboveMiddle, 0, pt1, pt2, false);
3298                        break;
3299                    case TacticalLines.IL:
3300                        AddIntegralModifier(tg, tg.get_Name(), aboveMiddle, 0, 1, 0, false);
3301                        break;
3302                    case TacticalLines.RETIRE:
3303                    case TacticalLines.PURSUIT:
3304                    case TacticalLines.WITHDRAW:
3305                    case TacticalLines.DISENGAGE:
3306                    case TacticalLines.WDRAWUP:
3307                    case TacticalLines.FPOL:
3308                    case TacticalLines.RPOL:
3309                    case TacticalLines.DEMONSTRATE:
3310                        AddIntegralModifier(tg, label, aboveMiddle, 0, 0, 1, true);
3311                        break;
3312                    case TacticalLines.RIP:
3313                    case TacticalLines.BOMB:
3314                    case TacticalLines.TGMF:
3315                        AddIntegralAreaModifier(tg, label, area, 0, ptCenter, ptCenter, true);
3316                        break;
3317                    case TacticalLines.MSDZ:
3318                        AddIntegralAreaModifier(tg, "1", area, 0, pt1, pt1, true);
3319                        AddIntegralAreaModifier(tg, "2", area, 0, pt2, pt2, true);
3320                        AddIntegralAreaModifier(tg, "3", area, 0, pt3, pt3, true);
3321                        break;
3322                    case TacticalLines.DELAY:
3323                        AddIntegralModifier(tg, tg.get_DTG(), aboveMiddle, -1 * csFactor, 0, 1, false);
3324                        AddIntegralModifier(tg, label, aboveMiddle, 0, 0, 1, true);
3325                        break;
3326                    case TacticalLines.GENERIC_LINE:
3327                        pt0 = tg.Pixels.get(0);
3328                        pt1 = tg.Pixels.get(1);
3329                        pt2 = tg.Pixels.get(tg.Pixels.size() - 1);
3330                        pt3 = tg.Pixels.get(tg.Pixels.size() - 2);
3331                        dist = lineutility.CalcDistanceDouble(pt0, pt1);
3332                        dist2 = lineutility.CalcDistanceDouble(pt2, pt3);
3333                        stringWidth = (int) ((double) metrics.stringWidth(tg.get_H() + " " + tg.get_Name()));
3334                        stringWidth2 = (int) ((double) metrics.stringWidth(tg.get_DTG()));
3335                        if (stringWidth2 > stringWidth) {
3336                            stringWidth = stringWidth2;
3337                        }
3338
3339                        if (tg.Pixels.size() == 2) //one segment
3340                        {
3341                            pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
3342                            AddModifier2(tg, tg.get_H() + " " + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
3343                            AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
3344                            AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
3345                            if (dist > 3.5 * stringWidth)//was 28stringwidth+5
3346                            {
3347                                pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
3348                                pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
3349                                pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
3350                                AddModifier2(tg, tg.get_H() + " " + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
3351                                AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
3352                                AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
3353                            }
3354                        } else //more than one semgent
3355                        {
3356                            double dist3 = lineutility.CalcDistanceDouble(pt0, pt2);
3357                            if (dist > stringWidth + 5 || dist >= dist2 || dist3 > stringWidth + 5) {
3358                                pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
3359                                AddModifier2(tg, tg.get_H() + " " + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
3360                                AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
3361                                AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
3362                            }
3363                            if (dist2 > stringWidth + 5 || dist2 > dist || dist3 > stringWidth + 5) {
3364                                pt0 = tg.Pixels.get(tg.Pixels.size() - 1);
3365                                pt1 = tg.Pixels.get(tg.Pixels.size() - 2);
3366                                pt1 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
3367                                AddModifier2(tg, tg.get_H() + " " + tg.get_Name(), aboveMiddle, -0.7 * csFactor, pt0, pt1, false);
3368                                AddModifier2(tg, tg.get_DTG() + WDash, aboveMiddle, 0.7 * csFactor, pt0, pt1, false);
3369                                AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 1.7 * csFactor, pt0, pt1, false);
3370                            }
3371                        }
3372                        break;
3373                    default:
3374                        break;
3375                }
3376            }
3377            scaleModifiers(tg);
3378            tg.Pixels = origPoints;
3379        } catch (Exception exc) {
3380            ErrorLogger.LogException(_className, "AddModifiersGeo",
3381                    new RendererException("Failed inside AddModifiersGeo", exc));
3382        }
3383
3384    }
3385
3386    /**
3387     * RFA, NFA, FFA need these for line spacing
3388     *
3389     * @param tg
3390     * @return
3391     */
3392    private static int getRFALines(TGLight tg) {
3393        int lines = 1;
3394        try {
3395            if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
3396                lines++;
3397            }
3398            if (tg.get_DTG() != null && !tg.get_DTG().isEmpty()) {
3399                lines++;
3400            } else if (tg.get_DTG1() != null && !tg.get_DTG1().isEmpty()) {
3401                lines++;
3402            }
3403        } catch (Exception exc) {
3404            ErrorLogger.LogException(_className, "AddModifiers",
3405                    new RendererException("Failed inside AddModifiers", exc));
3406        }
3407        return lines;
3408    }
3409
3410    /**
3411     * Added sector range fan modifiers based using the calculated orientation
3412     * indicator points
3413     *
3414     * @param tg
3415     * @param converter
3416     * @return
3417     */
3418    private static void addSectorModifiers(TGLight tg, IPointConversion converter) {
3419        try {
3420            if (tg.get_LineType() == TacticalLines.RANGE_FAN_SECTOR) {
3421                ArrayList<Double> AM = new ArrayList<>();
3422                ArrayList<Double> AN = new ArrayList<>();
3423                //get the number of sectors
3424                String X = tg.get_X();
3425                String[] altitudes = null;
3426                String[] am = tg.get_AM().split(",");
3427                String[] an = tg.get_AN().split(",");
3428                int numSectors = an.length / 2;
3429                //there must be at least one sector
3430                if (numSectors < 1) {
3431                    return;
3432                }
3433                if (!X.isEmpty()) {
3434                    altitudes = X.split(",");
3435                }
3436                try {
3437                    for (String s : am) {
3438                        AM.add(Double.parseDouble(s));
3439                    }
3440                    for (String s : an) {
3441                        AN.add(Double.parseDouble(s));
3442                    }
3443                } catch (NumberFormatException e) {
3444                    return;
3445                }
3446                if (numSectors + 1 > AM.size()) {
3447                    if (Double.parseDouble(am[0]) != 0d) {
3448                        AM.add(0, 0d);
3449                    }
3450                }
3451
3452                int n = tg.Pixels.size();
3453                //pt0 and pt1 are points for the location indicator
3454                POINT2 pt0 = tg.Pixels.get(n - 5);
3455                POINT2 pt1 = tg.Pixels.get(n - 4);
3456                Point2D pt02d = new Point2D.Double(pt0.x, pt0.y);
3457                Point2D pt12d = new Point2D.Double(pt1.x, pt1.y);
3458                pt02d = converter.PixelsToGeo(pt02d);
3459                pt12d = converter.PixelsToGeo(pt12d);
3460                pt0.x = pt02d.getX();
3461                pt0.y = pt02d.getY();
3462                pt1.x = pt12d.getX();
3463                pt1.y = pt12d.getY();
3464                //azimuth of the orientation indicator
3465                double az12 = mdlGeodesic.GetAzimuth(pt0, pt1);
3466
3467                POINT2 pt2 = null;
3468                ArrayList<POINT2> locModifier = new ArrayList();
3469                //diagnostic
3470                POINT2 ptLeft = null, ptRight = null;
3471                ArrayList<POINT2> locAZModifier = new ArrayList();
3472                //end section
3473                Point2D pt22d = null;
3474                double radius = 0;
3475                for (int k = 0; k < numSectors; k++) {
3476                    if (AM.size() < k + 2) {
3477                        break;
3478                    }
3479                    radius = (AM.get(k) + AM.get(k + 1)) / 2;
3480                    pt2 = mdlGeodesic.geodesic_coordinate(pt0, radius, az12);
3481                    //need locModifier in geo pixels
3482                    pt22d = new Point2D.Double(pt2.x, pt2.y);
3483                    pt22d = converter.GeoToPixels(pt22d);
3484                    pt2.x = pt22d.getX();
3485                    pt2.y = pt22d.getY();
3486                    locModifier.add(pt2);
3487                    //diagnostic
3488                    if (tg.get_HideOptionalLabels())
3489                        continue;
3490                    ptLeft = mdlGeodesic.geodesic_coordinate(pt0, radius, AN.get(2 * k));
3491                    //need ptLeft in geo pixels
3492                    pt22d = new Point2D.Double(ptLeft.x, ptLeft.y);
3493                    pt22d = converter.GeoToPixels(pt22d);
3494                    ptLeft.x = pt22d.getX();
3495                    ptLeft.y = pt22d.getY();
3496                    ptRight = mdlGeodesic.geodesic_coordinate(pt0, radius, AN.get(2 * k + 1));
3497                    //need ptRight in geo pixels
3498                    pt22d = new Point2D.Double(ptRight.x, ptRight.y);
3499                    pt22d = converter.GeoToPixels(pt22d);
3500                    ptRight.x = pt22d.getX();
3501                    ptRight.y = pt22d.getY();
3502                    locAZModifier.add(ptLeft);
3503                    locAZModifier.add(ptRight);
3504                    //end section
3505                }
3506                if (altitudes != null) {
3507                    for (int k = 0; k < altitudes.length; k++) {
3508                        if (k >= locModifier.size()) {
3509                            break;
3510                        }
3511                        pt0 = locModifier.get(k);
3512                        AddAreaModifier(tg, "ALT " + altitudes[k], area, 0, pt0, pt0);
3513                    }
3514                }
3515
3516                if (!tg.get_HideOptionalLabels()) {
3517                    for (int k = 0; k < numSectors; k++) {
3518                        pt0 = locModifier.get(k);
3519                        AddAreaModifier(tg, "RG " + removeDecimal(AM.get(k + 1)), area, -1, pt0, pt0);
3520                        ptLeft = locAZModifier.get(2 * k);
3521                        ptRight = locAZModifier.get(2 * k + 1);
3522                        AddAreaModifier(tg, removeDecimal(an[2 * k]), area, 0, ptLeft, ptLeft);
3523                        AddAreaModifier(tg, removeDecimal(an[2 * k + 1]), area, 0, ptRight, ptRight);
3524                    }
3525                }
3526            } else if (tg.get_LineType() == TacticalLines.RADAR_SEARCH) {
3527                // Copies functionality from RANGE_FAN_SECTOR with one sector and different modifiers
3528                String strLeftRightMinMax = tg.get_LRMM();
3529                String[] sector = strLeftRightMinMax.split(",");
3530                double left = Double.parseDouble(sector[0]);
3531                double right = Double.parseDouble(sector[1]);
3532
3533                while (left > 360) {
3534                    left -= 360;
3535                }
3536                while (right > 360) {
3537                    right -= 360;
3538                }
3539                while (left < 0) {
3540                    left += 360;
3541                }
3542                while (right < 0) {
3543                    right += 360;
3544                }
3545
3546                double orientation = 0;
3547                if (left > right) {
3548                    orientation = (left - 360 + right) / 2;
3549                } else {
3550                    orientation = (left + right) / 2;
3551                }
3552
3553                double dist = Double.parseDouble(sector[3]);
3554                double radius = dist * 1.1;
3555
3556                POINT2 pt0 = tg.LatLongs.get(0);
3557                Point2D ptPixels = converter.GeoToPixels(new Point2D.Double(pt0.x, pt0.y));
3558                POINT2 pt0F = new POINT2();
3559                pt0F.x = ptPixels.getX();
3560                pt0F.y = ptPixels.getY();
3561                pt0F.style = pt0.style;
3562
3563                POINT2 pt1 = mdlGeodesic.geodesic_coordinate(pt0, radius, orientation);
3564                ptPixels = converter.GeoToPixels(new Point2D.Double(pt1.x, pt1.y));
3565                POINT2 pt1F = new POINT2();
3566                pt1F.x = ptPixels.getX();
3567                pt1F.y = ptPixels.getY();
3568                pt1F.style = pt1.style;
3569
3570                dist = lineutility.CalcDistanceDouble(pt0F, pt1F);
3571                double base = 10;
3572                if (dist < 100) {
3573                    base = dist / 10;
3574                }
3575                if (base < 5) {
3576                    base = 5;
3577                }
3578                double basex2 = 2 * base;
3579                POINT2 ptTipF = lineutility.ExtendAlongLineDouble(pt0F, pt1F, dist + basex2);  //was 20
3580
3581                pt0 = pt0F;
3582                pt1 = ptTipF;
3583
3584                ArrayList<Double> AM = new ArrayList<>();
3585                String[] am = tg.get_AM().split(",");
3586
3587                for (String s : am) {
3588                    AM.add(Double.parseDouble(s));
3589                }
3590
3591                if (AM.size() < 2) {
3592                    if (Double.parseDouble(am[0]) != 0d) {
3593                        AM.add(0, 0d);
3594                    } else {
3595                        return;
3596                    }
3597                }
3598
3599                Point2D pt02d = new Point2D.Double(pt0.x, pt0.y);
3600                Point2D pt12d = new Point2D.Double(pt1.x, pt1.y);
3601                pt02d = converter.PixelsToGeo(pt02d);
3602                pt12d = converter.PixelsToGeo(pt12d);
3603                pt0.x = pt02d.getX();
3604                pt0.y = pt02d.getY();
3605                pt1.x = pt12d.getX();
3606                pt1.y = pt12d.getY();
3607                double az12 = mdlGeodesic.GetAzimuth(pt0, pt1);
3608
3609                Point2D pt22d = null;
3610
3611                radius = (AM.get(0) + AM.get(1)) / 2;
3612                POINT2 pt2 = mdlGeodesic.geodesic_coordinate(pt0, radius, az12);
3613                pt22d = new Point2D.Double(pt2.x, pt2.y);
3614                pt22d = converter.GeoToPixels(pt22d);
3615                pt2.x = pt22d.getX();
3616                pt2.y = pt22d.getY();
3617                AddAreaModifier(tg, tg.get_Name(), area, -1, pt2, pt2);
3618            }
3619        } catch (Exception exc) {
3620            ErrorLogger.LogException(_className, "addSectorModifiers",
3621                    new RendererException("Failed inside addSectorModifiers", exc));
3622        }
3623    }
3624
3625    /**
3626     * Called by the renderer after tg.Pixels has been filled with the
3627     * calculated points. The modifier path depends on points calculated by
3628     * CELineArray.
3629     *
3630     * @param tg
3631     */
3632    public static void AddModifiers2(TGLight tg, IPointConversion converter) {
3633        try {
3634            if (tg.Pixels == null || tg.Pixels.isEmpty()) {
3635                return;
3636            }
3637            switch (tg.get_LineType()) {
3638                case TacticalLines.BS_RECTANGLE:
3639                case TacticalLines.BBS_RECTANGLE:
3640                case TacticalLines.CONVOY:
3641                case TacticalLines.HCONVOY:
3642                case TacticalLines.BREACH:
3643                case TacticalLines.BYPASS:
3644                case TacticalLines.CANALIZE:
3645                case TacticalLines.PENETRATE:
3646                case TacticalLines.CLEAR:
3647                case TacticalLines.DISRUPT:
3648                case TacticalLines.FIX:
3649                case TacticalLines.ISOLATE:
3650                case TacticalLines.OCCUPY:
3651                case TacticalLines.RETAIN:
3652                case TacticalLines.SECURE:
3653                case TacticalLines.CONTROL:
3654                case TacticalLines.LOCATE:
3655                case TacticalLines.AREA_DEFENSE:
3656                case TacticalLines.CONTAIN:
3657                case TacticalLines.SEIZE:
3658                case TacticalLines.CAPTURE:
3659                case TacticalLines.EVACUATE:
3660                case TacticalLines.TURN:
3661                case TacticalLines.CORDONKNOCK:
3662                case TacticalLines.CORDONSEARCH:
3663                case TacticalLines.DENY:
3664                case TacticalLines.ESCORT:
3665                case TacticalLines.EXFILTRATION:
3666                case TacticalLines.INFILTRATION:
3667                case TacticalLines.FOLLA:
3668                case TacticalLines.FOLSP:
3669                case TacticalLines.ACA_RECTANGULAR:
3670                case TacticalLines.ACA_CIRCULAR:
3671                case TacticalLines.RECTANGULAR:
3672                case TacticalLines.CUED_ACQUISITION:
3673                case TacticalLines.CIRCULAR:
3674                case TacticalLines.BDZ:
3675                case TacticalLines.BBS_POINT:
3676                case TacticalLines.FSA_CIRCULAR:
3677                case TacticalLines.NOTACK:
3678                case TacticalLines.ATI_CIRCULAR:
3679                case TacticalLines.CFFZ_CIRCULAR:
3680                case TacticalLines.SENSOR_CIRCULAR:
3681                case TacticalLines.CENSOR_CIRCULAR:
3682                case TacticalLines.DA_CIRCULAR:
3683                case TacticalLines.CFZ_CIRCULAR:
3684                case TacticalLines.ZOR_CIRCULAR:
3685                case TacticalLines.TBA_CIRCULAR:
3686                case TacticalLines.TVAR_CIRCULAR:
3687                case TacticalLines.FFA_CIRCULAR:
3688                case TacticalLines.NFA_CIRCULAR:
3689                case TacticalLines.RFA_CIRCULAR:
3690                case TacticalLines.KILLBOXBLUE_CIRCULAR:
3691                case TacticalLines.KILLBOXPURPLE_CIRCULAR:
3692                case TacticalLines.BLOCK:
3693                case TacticalLines.FFA_RECTANGULAR:
3694                case TacticalLines.NFA_RECTANGULAR:
3695                case TacticalLines.RFA_RECTANGULAR:
3696                case TacticalLines.KILLBOXBLUE_RECTANGULAR:
3697                case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
3698                case TacticalLines.FSA_RECTANGULAR:
3699                case TacticalLines.SHIP_AOI_RECTANGULAR:
3700                case TacticalLines.DEFENDED_AREA_RECTANGULAR:
3701                case TacticalLines.ATI_RECTANGULAR:
3702                case TacticalLines.CFFZ_RECTANGULAR:
3703                case TacticalLines.SENSOR_RECTANGULAR:
3704                case TacticalLines.CENSOR_RECTANGULAR:
3705                case TacticalLines.DA_RECTANGULAR:
3706                case TacticalLines.CFZ_RECTANGULAR:
3707                case TacticalLines.ZOR_RECTANGULAR:
3708                case TacticalLines.TBA_RECTANGULAR:
3709                case TacticalLines.TVAR_RECTANGULAR:
3710                case TacticalLines.PAA:
3711                case TacticalLines.PAA_RECTANGULAR:
3712                case TacticalLines.RECTANGULAR_TARGET:
3713                case TacticalLines.PAA_CIRCULAR:
3714                case TacticalLines.RANGE_FAN:
3715                case TacticalLines.RANGE_FAN_SECTOR:
3716                case TacticalLines.RADAR_SEARCH:
3717                case TacticalLines.SHIP_AOI_CIRCULAR:
3718                case TacticalLines.MFLANE:
3719                case TacticalLines.ENVELOPMENT:
3720                case TacticalLines.MOBILE_DEFENSE:
3721                    break;
3722                default:
3723                    return;
3724            }
3725            //end section
3726            ArrayList<POINT2> origPoints = lineutility.getDeepCopy(tg.Pixels);
3727            int n = tg.Pixels.size();
3728            if (tg.modifiers == null) {
3729                tg.modifiers = new ArrayList();
3730            }
3731            Font font = tg.get_Font();
3732            POINT2 ptCenter = null;
3733            double csFactor = 1d;//this will be used for text spacing the 3d map (CommandCight)
3734            //String affiliation=tg.get_Affiliation();
3735            int linetype = tg.get_LineType();
3736            POINT2 pt0 = null, pt1 = null, pt2 = null, pt3 = null;
3737            int j = 0, k = 0;
3738            double dist = 0;
3739            String label = GetCenterLabel(tg);
3740            String[] X = null;
3741            int lastIndex = tg.Pixels.size() - 1;
3742            int nextToLastIndex = 0;
3743            if (tg.Pixels.size() > 1) {
3744                nextToLastIndex = tg.Pixels.size() - 2;
3745            }
3746            POINT2 ptLast = new POINT2(tg.Pixels.get(lastIndex));
3747            POINT2 ptNextToLast = null;
3748            if (tg.Pixels.size() > 1) {
3749                ptNextToLast = new POINT2(tg.Pixels.get(nextToLastIndex));
3750            }
3751            String WDash = ""; // Dash between W and W1 if they're not empty
3752            String TSpace = "", TDash = ""; // Space or dash between label and T modifier if T isn't empty
3753            if (tg.get_DTG() != null && tg.get_DTG1() != null && !tg.get_DTG().isEmpty() && !tg.get_DTG1().isEmpty()) {
3754                WDash = " - ";
3755            }
3756            if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
3757                TSpace = " ";
3758                TDash = " - ";
3759            }
3760
3761            POINT2 ptLeft = null, ptRight = null;
3762            BufferedImage bi = new BufferedImage(8, 8, BufferedImage.TYPE_INT_ARGB);
3763            Graphics2D g2d = bi.createGraphics();
3764            g2d.setFont(tg.get_Font());
3765            FontMetrics metrics = g2d.getFontMetrics();
3766            int stringWidth = 0, rfaLines = 0;
3767            pt0 = new POINT2(tg.Pixels.get(0));
3768            if (tg.Pixels.size() > 1) {
3769                pt1 = new POINT2(tg.Pixels.get(1));
3770            }
3771
3772            POINT2[] pts = null;
3773            // if the client is the 3d map (CS) then we want to shrink the spacing between
3774            // the lines of text
3775            if (tg.get_Client().equals("cpof3d")) {
3776                csFactor = 0.9d;
3777            }
3778
3779            shiftModifierPath(tg, pt0, pt1, ptLast, ptNextToLast);
3780
3781            boolean modifiersGrouped = false;
3782            if(RendererSettings.getInstance().getGroupModifiers())
3783            {
3784                switch (linetype) {
3785                    case TacticalLines.PAA_RECTANGULAR:
3786                    case TacticalLines.PAA_CIRCULAR:
3787                        //too complex to duplicate, simpler to group labels in next switch statement
3788                        break;
3789                    case TacticalLines.ACA_RECTANGULAR:
3790                    case TacticalLines.ACA_CIRCULAR:
3791                        ptCenter = lineutility.CalcCenterPointDouble2(tg.Pixels.toArray(), tg.Pixels.size());
3792                        AddIntegralAreaModifier(tg, buildAreaGroupString(tg, label), area, 0, ptCenter, ptCenter, false);
3793                        modifiersGrouped = true;
3794                        break;
3795                    case TacticalLines.FSA_CIRCULAR:
3796                    case TacticalLines.ATI_CIRCULAR:
3797                    case TacticalLines.CFFZ_CIRCULAR:
3798                    case TacticalLines.SENSOR_CIRCULAR:
3799                    case TacticalLines.CENSOR_CIRCULAR:
3800                    case TacticalLines.DA_CIRCULAR:
3801                    case TacticalLines.CFZ_CIRCULAR:
3802                    case TacticalLines.ZOR_CIRCULAR:
3803                    case TacticalLines.TBA_CIRCULAR:
3804                    case TacticalLines.TVAR_CIRCULAR:
3805                    case TacticalLines.KILLBOXBLUE_CIRCULAR:
3806                    case TacticalLines.KILLBOXPURPLE_CIRCULAR:
3807                        ptCenter = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(tg.Pixels.size() / 2), 0);
3808                        //center labels
3809                        AddIntegralAreaModifier(tg, buildAreaGroupString(tg, label), area, 0, ptCenter, ptCenter, false);
3810                        //DTG on top left
3811                        POINT2 dtgCircularPosition = tg.Pixels.get(Math.round(tg.Pixels.size() * 0.5625f));
3812                        AddIntegralAreaModifier(tg, buildAreaGroupDTGString(tg), toEnd, -2 * csFactor, dtgCircularPosition, new POINT2(dtgCircularPosition.x + 0.001, dtgCircularPosition.y, 0), false);
3813                        modifiersGrouped = true;
3814                        break;
3815                    case TacticalLines.FFA_CIRCULAR:
3816                    case TacticalLines.NFA_CIRCULAR:
3817                    case TacticalLines.RFA_CIRCULAR:
3818                        ptCenter = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(51), 0);
3819                        AddIntegralAreaModifier(tg, buildAreaGroupString(tg, label), area, 0, ptCenter, ptCenter, false);
3820                        modifiersGrouped = true;
3821                        break;
3822                    case TacticalLines.FFA_RECTANGULAR:
3823                    case TacticalLines.NFA_RECTANGULAR:
3824                    case TacticalLines.RFA_RECTANGULAR:
3825                        pt0 = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
3826                        pt1 = lineutility.MidPointDouble(tg.Pixels.get(2), tg.Pixels.get(3), 0);
3827                        ptCenter = lineutility.MidPointDouble(pt0, pt1, 0);
3828                        AddIntegralAreaModifier(tg, buildAreaGroupString(tg, label), area, 0, ptCenter, ptCenter, false);
3829                        modifiersGrouped = true;
3830                        break;
3831                    case TacticalLines.KILLBOXBLUE_RECTANGULAR:
3832                    case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
3833                    case TacticalLines.FSA_RECTANGULAR:
3834                    case TacticalLines.ATI_RECTANGULAR:
3835                    case TacticalLines.CFFZ_RECTANGULAR:
3836                    case TacticalLines.SENSOR_RECTANGULAR:
3837                    case TacticalLines.CENSOR_RECTANGULAR:
3838                    case TacticalLines.DA_RECTANGULAR:
3839                    case TacticalLines.CFZ_RECTANGULAR:
3840                    case TacticalLines.ZOR_RECTANGULAR:
3841                    case TacticalLines.TBA_RECTANGULAR:
3842                    case TacticalLines.TVAR_RECTANGULAR:
3843                        ptLeft = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
3844                        ptRight = lineutility.MidPointDouble(tg.Pixels.get(2), tg.Pixels.get(3), 0);
3845                        ptCenter = lineutility.MidPointDouble(ptLeft, ptRight, 0);
3846                        AddIntegralAreaModifier(tg, buildAreaGroupString(tg,label), area, 0 * csFactor, ptCenter, ptCenter, false);
3847
3848                        //highest point left of center
3849                        POINT2 dtgPosition = getHighestPointLeftOfCenter(tg.Pixels,ptCenter);
3850
3851                        AddIntegralAreaModifier(tg, buildAreaGroupDTGString(tg), toEnd, 0 * csFactor, dtgPosition, new POINT2(dtgPosition.x + 0.001, dtgPosition.y, 0), false);
3852                        modifiersGrouped = true;
3853                        break;
3854                }
3855            }
3856
3857            if(!modifiersGrouped) {
3858                switch (linetype) {
3859                    case TacticalLines.BS_RECTANGLE:
3860                    case TacticalLines.BBS_RECTANGLE:
3861                        pts = new POINT2[4];
3862                        for (j = 0; j < 4; j++) {
3863                            pts[j] = tg.Pixels.get(j);
3864                        }
3865                        ptCenter = lineutility.CalcCenterPointDouble2(pts, 4);
3866                        AddIntegralAreaModifier(tg, tg.get_Name(), area, -0.125 * csFactor, ptCenter, ptCenter, false);
3867                        break;
3868                    case TacticalLines.CONVOY:
3869                    case TacticalLines.HCONVOY:
3870                        pt2 = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(3), 0);
3871                        pt3 = lineutility.MidPointDouble(tg.Pixels.get(1), tg.Pixels.get(2), 0);
3872                        AddIntegralAreaModifier(tg, tg.get_V(), aboveEndInside, 0, pt2, pt3, false);
3873                        AddIntegralAreaModifier(tg, tg.get_H(), aboveStartInside, 0, pt2, pt3, false);
3874                        addDTG(tg, aboveMiddle, 1.2 * csFactor, 2.2 * csFactor, pt2, pt3, metrics);
3875                        break;
3876                    case TacticalLines.BREACH:
3877                    case TacticalLines.BYPASS:
3878                    case TacticalLines.CANALIZE:
3879                        pt0 = tg.Pixels.get(1);
3880                        pt1 = tg.Pixels.get(2);
3881                        //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3882                        AddIntegralAreaModifier(tg, label, aboveMiddlePerpendicular, -0.125 * csFactor, pt0, pt1, true);
3883                        break;
3884                    case TacticalLines.PENETRATE:
3885                    case TacticalLines.CLEAR:
3886                        pt0 = tg.Pixels.get(2);
3887                        pt1 = tg.Pixels.get(3);
3888                        //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3889                        AddIntegralAreaModifier(tg, label, aboveMiddle, -0.125 * csFactor, pt0, pt1, true);
3890                        break;
3891                    case TacticalLines.DISRUPT:
3892                        pt0 = tg.Pixels.get(4);
3893                        pt1 = tg.Pixels.get(5);
3894                        //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3895                        AddIntegralAreaModifier(tg, label, aboveMiddle, -0.125 * csFactor, pt0, pt1, true);
3896                        break;
3897                    case TacticalLines.FIX:
3898                        pt0 = tg.Pixels.get(0);
3899                        pt1 = tg.Pixels.get(1);
3900                        //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3901                        AddIntegralAreaModifier(tg, label, aboveMiddle, -0.125 * csFactor, pt0, pt1, true);
3902                        break;
3903                    case TacticalLines.ISOLATE:
3904                    case TacticalLines.OCCUPY:
3905                    case TacticalLines.RETAIN:
3906                    case TacticalLines.SECURE:
3907                    case TacticalLines.CONTROL:
3908                    case TacticalLines.LOCATE:
3909                    case TacticalLines.AREA_DEFENSE:
3910                        pt0 = tg.Pixels.get(13);
3911                        pt1 = tg.Pixels.get(14);
3912                        //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3913                        ptCenter = lineutility.MidPointDouble(pt0, pt1, 0);
3914                        AddIntegralAreaModifier(tg, label, aboveMiddle, -0.125 * csFactor, ptCenter, ptCenter, true);
3915                        break;
3916                    case TacticalLines.CONTAIN:
3917                        pt0 = tg.Pixels.get(13);
3918                        pt1 = tg.Pixels.get(14);
3919                        //ptCenter = lineutility.MidPointDouble(pt0, pt1, 0);//maybe figure out which points are center line to apply an angle
3920                        ptCenter = tg.Pixels.get(13);//always rightside up at mid-point of curve
3921                        //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3922                        AddIntegralAreaModifier(tg, label, aboveMiddle, -0.125 * csFactor, ptCenter, ptCenter, true);
3923
3924                        // Contain always has "ENY" even if friendly (not N modifier)
3925                        for (j = 0; j < n; j++) {
3926                            if (tg.Pixels.get(j).style == 14) {
3927                                pt0 = tg.Pixels.get(j);
3928                                pt1 = tg.Pixels.get(j + 1);
3929                                AddIntegralAreaModifier(tg, "ENY", aboveMiddle, 0, pt0, pt1, true);
3930                                break;
3931                            }
3932                        }
3933                        break;
3934                    case TacticalLines.TURN:
3935                        pt0 = tg.Pixels.get(12);
3936                        pt1 = tg.Pixels.get(13);
3937                        ptCenter = lineutility.MidPointDouble(pt0, pt1, 0);
3938                        AddIntegralAreaModifier(tg, label, area, -0.125 * csFactor, ptCenter, ptCenter, true);
3939                        break;
3940                    case TacticalLines.SEIZE:
3941                    case TacticalLines.CAPTURE:
3942                    case TacticalLines.EVACUATE:
3943                        pt0 = tg.Pixels.get(26);
3944                        pt1 = tg.Pixels.get(27);
3945                        //pt1=lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
3946                        ptCenter = lineutility.MidPointDouble(pt0, pt1, 0);
3947                        AddIntegralAreaModifier(tg, label, aboveMiddle, -0.125 * csFactor, ptCenter, ptCenter, true);
3948                        break;
3949                    case TacticalLines.DEFENDED_AREA_RECTANGULAR:
3950                        ptLeft = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
3951                        ptRight = lineutility.MidPointDouble(tg.Pixels.get(2), tg.Pixels.get(3), 0);
3952                        AddIntegralAreaModifier(tg, label + TDash + tg.get_Name(), aboveMiddle, 0, ptLeft, ptRight, false);
3953                        break;
3954                    case TacticalLines.SHIP_AOI_RECTANGULAR:
3955                        if (tg.Pixels.get(0).x > tg.Pixels.get(3).x) {
3956                            AddIntegralAreaModifier(tg, label, aboveMiddle, 0.6 * csFactor, tg.Pixels.get(0), tg.Pixels.get(3), false);
3957                        } else {
3958                            AddIntegralAreaModifier(tg, label, aboveMiddle, 0.6 * csFactor, tg.Pixels.get(1), tg.Pixels.get(2), false);
3959                        }
3960                        break;
3961                    case TacticalLines.NOTACK:
3962                        ptCenter = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(tg.Pixels.size() / 2), 0);
3963                        AddIntegralAreaModifier(tg, label, area, -1, ptCenter, ptCenter, false);
3964                        addDTG(tg, area, csFactor, 2 * csFactor, ptCenter, ptCenter, metrics);
3965                        break;
3966                    case TacticalLines.SHIP_AOI_CIRCULAR:
3967                        // Moved from AddModifiersGeo()
3968                        // AddModifiersGeo() called before getGeoEllipse(). Unable to use getMBR with single anchor point
3969
3970                        // Get variables from AddModifiersGeo
3971                        POINT2 lr = new POINT2(tg.Pixels.get(0));
3972                        POINT2 ll = new POINT2(tg.Pixels.get(0));
3973                        POINT2 ul = new POINT2(tg.Pixels.get(0));
3974                        POINT2 ur = new POINT2(tg.Pixels.get(0));
3975                        GetMBR(tg, ul, ur, lr, ll);
3976
3977                        AddIntegralAreaModifier(tg, label, aboveMiddle, csFactor, ll, lr, false);
3978                        break;
3979                    case TacticalLines.MFLANE:
3980                        //pt0=tg.Pixels.get(7);
3981                        //pt1=tg.Pixels.get(5);
3982                        pt0 = tg.Pixels.get(4);
3983                        pt1 = tg.Pixels.get(2);
3984                        if (tg.Pixels.get(0).y < tg.Pixels.get(1).y) {
3985                            addDTG(tg, aboveMiddle, 0.5 * csFactor, 1.5 * csFactor, pt0, pt1, metrics);
3986                        } else {
3987                            addDTG(tg, aboveMiddle, -0.5 * csFactor, -1.5 * csFactor, pt0, pt1, metrics);
3988                        }
3989                        break;
3990                    case TacticalLines.CORDONKNOCK:
3991                    case TacticalLines.CORDONSEARCH:
3992                    case TacticalLines.DENY:
3993                        pt0 = tg.Pixels.get(13);
3994                        pt1 = tg.Pixels.get(0);
3995                        stringWidth = metrics.stringWidth(label);
3996                        if (pt0.x < pt1.x) {
3997                            stringWidth = -stringWidth;
3998                        }
3999                        pt1 = lineutility.ExtendAlongLineDouble2(pt0, pt1, 0.75 * stringWidth);
4000                        ptCenter = lineutility.MidPointDouble(pt0, pt1, 0);
4001                        AddIntegralAreaModifier(tg, label, aboveMiddle, 0, ptCenter, ptCenter, true);
4002                        break;
4003                    case TacticalLines.ESCORT:
4004                        if (tg.Pixels.size() == 6) {
4005                            if (tg.Pixels.get(2).x == tg.Pixels.get(3).x &&
4006                                    tg.Pixels.get(2).y == tg.Pixels.get(3).y) {
4007                                //No Room for E labels
4008                                break;//?
4009                            }
4010                            AddIntegralAreaModifier(tg, label, toEnd, 0, tg.Pixels.get(2), tg.Pixels.get(1), true);
4011                            AddIntegralAreaModifier(tg, label, toEnd, 0, tg.Pixels.get(3), tg.Pixels.get(4), true);
4012                        }
4013                        break;
4014                    case TacticalLines.EXFILTRATION:
4015                    case TacticalLines.INFILTRATION:
4016                        AddIntegralAreaModifier(tg, label, aboveMiddle, 0, pt0, pt1, true);
4017                        break;
4018                    case TacticalLines.FOLLA:
4019                        pt0 = tg.Pixels.get(0);
4020                        pt1 = lineutility.MidPointDouble(tg.Pixels.get(5), tg.Pixels.get(6), 0);
4021                        pt1 = lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
4022                        AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, 0, pt0, pt1, true);
4023                        break;
4024                    case TacticalLines.FOLSP:
4025                        pt0 = tg.Pixels.get(3);
4026                        pt1 = tg.Pixels.get(6);
4027                        pt1 = lineutility.ExtendAlongLineDouble(pt1, pt0, -10);
4028                        AddIntegralAreaModifier(tg, tg.get_Name(), aboveMiddle, 0, pt0, pt1, true);
4029                        break;
4030                /*case TacticalLines.ACA_RECTANGULAR:
4031                    ptLeft = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
4032                    ptRight = lineutility.MidPointDouble(tg.Pixels.get(2), tg.Pixels.get(3), 0);
4033                    AddModifier2(tg, label + TSpace + tg.get_Name(), aboveMiddle, -3 * csFactor, ptLeft, ptRight, false);
4034                    AddModifier2(tg, tg.get_T1(), aboveMiddle, -2 * csFactor, ptLeft, ptRight, false, "T1");
4035                    AddModifier2(tg, "MIN ALT: " + tg.get_X(), aboveMiddle, -1 * csFactor, ptLeft, ptRight, false, "H");
4036                    AddModifier2(tg, "MAX ALT: " + tg.get_X1(), aboveMiddle, 0, ptLeft, ptRight, false, "H1");
4037                    AddModifier2(tg, "GRID " + tg.get_Location(), aboveMiddle, 1 * csFactor, ptLeft, ptRight, false, "H2");
4038                    AddModifier2(tg, "EFF " + tg.get_DTG() + WDash, aboveMiddle, 2 * csFactor, ptLeft, ptRight, false, "W");
4039                    AddModifier2(tg, tg.get_DTG1(), aboveMiddle, 3 * csFactor, ptLeft, ptRight, false, "W1");
4040                    break;//*/
4041                    case TacticalLines.ACA_RECTANGULAR://text always right-side-up like circular
4042                    case TacticalLines.ACA_CIRCULAR:
4043                        ptCenter = lineutility.CalcCenterPointDouble2(tg.Pixels.toArray(), tg.Pixels.size());
4044                        AddIntegralAreaModifier(tg, label + TSpace + tg.get_Name(), area, -3 * csFactor, ptCenter, ptCenter, false);
4045                        AddModifier2(tg, tg.get_T1(), area, -2 * csFactor, ptCenter, ptCenter, false, "T1");
4046                        AddIntegralAreaModifier(tg, "MIN ALT: " + tg.get_X(), area, -1 * csFactor, ptCenter, ptCenter, false, "H");
4047                        AddIntegralAreaModifier(tg, "MAX ALT: " + tg.get_X1(), area, 0, ptCenter, ptCenter, false, "H1");
4048                        AddIntegralAreaModifier(tg, "GRID " + tg.get_Location(), area, 1 * csFactor, ptCenter, ptCenter, false, "H2");
4049                        AddIntegralAreaModifier(tg, "EFF " + tg.get_DTG() + WDash, area, 2 * csFactor, ptCenter, ptCenter, false, "W");
4050                        AddIntegralAreaModifier(tg, tg.get_DTG1(), area, 3 * csFactor, ptCenter, ptCenter, false, "W1");
4051                        break;
4052                    case TacticalLines.FSA_CIRCULAR:
4053                    case TacticalLines.ATI_CIRCULAR:
4054                    case TacticalLines.CFFZ_CIRCULAR:
4055                    case TacticalLines.SENSOR_CIRCULAR:
4056                    case TacticalLines.CENSOR_CIRCULAR:
4057                    case TacticalLines.DA_CIRCULAR:
4058                    case TacticalLines.CFZ_CIRCULAR:
4059                    case TacticalLines.ZOR_CIRCULAR:
4060                    case TacticalLines.TBA_CIRCULAR:
4061                    case TacticalLines.TVAR_CIRCULAR:
4062                    case TacticalLines.KILLBOXBLUE_CIRCULAR:
4063                    case TacticalLines.KILLBOXPURPLE_CIRCULAR:
4064                        ptCenter = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(tg.Pixels.size() / 2), 0);
4065                        AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, false);
4066                        AddIntegralAreaModifier(tg, tg.get_Name(), area, 0.5 * csFactor, ptCenter, ptCenter, false);
4067
4068                        //labels left of circle
4069                        //AddOffsetModifier(tg, tg.get_DTG() + WDash, toEnd, -1 * csFactor, tg.Pixels.size() / 2, 0, 4, "left");
4070                        //AddOffsetModifier(tg, tg.get_DTG1(), toEnd, 0, tg.Pixels.size() / 2, 0, 4, "left");
4071
4072                        //labels top left of circle
4073                        POINT2 dtgCircularPosition = tg.Pixels.get(Math.round(tg.Pixels.size() * 0.5625f));
4074                        AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, toEnd, -2 * csFactor, dtgCircularPosition, new POINT2(dtgCircularPosition.x + 0.001, dtgCircularPosition.y, 0), false);
4075                        AddIntegralAreaModifier(tg, tg.get_DTG1(), toEnd, -1 * csFactor, dtgCircularPosition, new POINT2(dtgCircularPosition.x + 0.001, dtgCircularPosition.y, 0), false);
4076                        break;
4077                    case TacticalLines.FFA_CIRCULAR:
4078                    case TacticalLines.NFA_CIRCULAR:
4079                    case TacticalLines.RFA_CIRCULAR:
4080                        rfaLines = getRFALines(tg);
4081                        ptCenter = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(51), 0);
4082                        switch (rfaLines) {
4083                            case 3: //2 valid modifiers and a label
4084                                AddIntegralAreaModifier(tg, label, area, -1 * csFactor, ptCenter, ptCenter, false);
4085                                AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, ptCenter, ptCenter, false);
4086                                addDTG(tg, area, 1 * csFactor, 2 * csFactor, ptCenter, ptCenter, metrics);
4087                                break;
4088                            case 2: //one valid modifier and a label
4089                                AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, false);
4090                                if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
4091                                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 0.5 * csFactor, ptCenter, ptCenter, false);
4092                                } else {
4093                                    addDTG(tg, area, 0.5 * csFactor, 1.5 * csFactor, ptCenter, ptCenter, metrics);
4094                                }
4095                                break;
4096                            default:    //one label only
4097                                AddIntegralAreaModifier(tg, label, area, 0, ptCenter, ptCenter, false);
4098                                break;
4099                        }
4100                        break;
4101                    case TacticalLines.BLOCK:
4102                        //for (j = 0; j < tg.Pixels.size(); j++)
4103                        for (j = 0; j < n; j++) {
4104                            if (tg.Pixels.get(j).style == 14) {
4105                                AddIntegralModifier(tg, label, aboveMiddle, 0, j, j + 1);
4106                                break;
4107                            }
4108                        }
4109                        break;
4110                    case TacticalLines.FFA_RECTANGULAR:
4111                    case TacticalLines.NFA_RECTANGULAR:
4112                    case TacticalLines.RFA_RECTANGULAR:
4113                        rfaLines = getRFALines(tg);
4114                        pt0 = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
4115                        pt1 = lineutility.MidPointDouble(tg.Pixels.get(2), tg.Pixels.get(3), 0);
4116                        ptCenter = lineutility.MidPointDouble(pt0, pt1, 0);
4117                        switch (rfaLines) {
4118                            case 3: //2 valid modifiers and a label
4119                                AddIntegralAreaModifier(tg, label, area, -1 * csFactor, ptCenter, ptCenter, false);
4120                                AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, ptCenter, ptCenter, false);
4121                                addDTG(tg, area, 1 * csFactor, 2 * csFactor, ptCenter, ptCenter, metrics);
4122                                break;
4123                            case 2: //one valid modifier and a label
4124                                AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, false);
4125                                if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
4126                                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 0.5 * csFactor, ptCenter, ptCenter, false);
4127                                } else {
4128                                    addDTG(tg, area, 0.5 * csFactor, 1.5 * csFactor, ptCenter, ptCenter, metrics);
4129                                }
4130                                break;
4131                            default:    //one label only
4132                                AddIntegralAreaModifier(tg, label, area, 0, ptCenter, ptCenter, false);
4133                                break;
4134                        }
4135                        break;
4136                    case TacticalLines.KILLBOXBLUE_RECTANGULAR:
4137                    case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
4138                    case TacticalLines.FSA_RECTANGULAR:
4139                    case TacticalLines.ATI_RECTANGULAR:
4140                    case TacticalLines.CFFZ_RECTANGULAR:
4141                    case TacticalLines.SENSOR_RECTANGULAR:
4142                    case TacticalLines.CENSOR_RECTANGULAR:
4143                    case TacticalLines.DA_RECTANGULAR:
4144                    case TacticalLines.CFZ_RECTANGULAR:
4145                    case TacticalLines.ZOR_RECTANGULAR:
4146                    case TacticalLines.TBA_RECTANGULAR:
4147                    case TacticalLines.TVAR_RECTANGULAR:
4148
4149                        ptLeft = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
4150                        ptRight = lineutility.MidPointDouble(tg.Pixels.get(2), tg.Pixels.get(3), 0);
4151                        ptCenter = lineutility.MidPointDouble(ptLeft, ptRight, 0);
4152
4153                        //labels upright
4154                        AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, false);
4155                        AddIntegralAreaModifier(tg, tg.get_Name(), area, 0.5 * csFactor, ptCenter, ptCenter, false);
4156
4157                        //labels on angle
4158                        //AddModifier2(tg, label, aboveMiddle, -0.5 * csFactor, ptLeft, ptRight, false);
4159                        //AddModifier2(tg, tg.get_Name(), aboveMiddle, 0.5 * csFactor, ptLeft, ptRight, false);
4160
4161                        /*//DTG modifiers top left following angle
4162                        pt0 = tg.Pixels.get(0);
4163                        pt1 = tg.Pixels.get(1);
4164                        pt2 = tg.Pixels.get(2);
4165                        pt3 = tg.Pixels.get(3);
4166
4167                        if (tg.get_Client().equalsIgnoreCase("ge")) {
4168                            pt0.x -= font.getSize() / 2;
4169                            pt2.x -= font.getSize() / 2;
4170                        }
4171                        if (!tg.get_Client().equalsIgnoreCase("ge"))//added 2-27-12
4172                        {
4173                            clsUtility.shiftModifiersLeft(pt0, pt3, 12.5);
4174                            clsUtility.shiftModifiersLeft(pt1, pt2, 12.5);
4175                        }
4176
4177                        if (ptLeft.x == ptRight.x) {
4178                            ptRight.x += 1;
4179                        }
4180                        if (ptLeft.x < ptRight.x) {
4181                            AddModifier(tg, tg.get_DTG() + WDash, toEnd, 0, pt0, pt3);//was 1,2 switched for CPOF
4182                            AddModifier(tg, tg.get_DTG1(), toEnd, 1 * csFactor, pt0, pt3);//was 1,2
4183                        } else {
4184                            AddModifier(tg, tg.get_DTG() + WDash, toEnd, 0, pt2, pt1);//was 3,0 //switched for CPOF
4185                            AddModifier(tg, tg.get_DTG1(), toEnd, 1 * csFactor, pt2, pt1);//was 3,0
4186                        }//*/
4187
4188                        //highest point left of center
4189                        POINT2 dtgPosition = getHighestPointLeftOfCenter(tg.Pixels, ptCenter);
4190
4191                        //DTG at north west-ish point and right-side-up
4192                        AddIntegralAreaModifier(tg, tg.get_DTG() + WDash, toEnd, 0.5 * csFactor, dtgPosition, new POINT2(dtgPosition.x + 0.001, dtgPosition.y, 0), false);
4193                        AddIntegralAreaModifier(tg, tg.get_DTG1() + " ", toEnd, 1.5 * csFactor, dtgPosition, new POINT2(dtgPosition.x + 0.001, dtgPosition.y, 0), false);
4194
4195                        //DTG at first point and angled with rectangle
4196                        //AddOffsetModifier(tg, tg.get_DTG() + WDash, toEnd, -1 * csFactor, tg.Pixels.size() / 2, 0, 4, "left");
4197                        //AddOffsetModifier(tg, tg.get_DTG1(), toEnd, 0, tg.Pixels.size() / 2, 0, 4, "left");
4198
4199                        break;//*/
4200                    case TacticalLines.PAA_RECTANGULAR:
4201
4202                        ptCenter = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
4203                        AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, true);//label level
4204                        //AddIntegralModifier(tg, label, aboveMiddlePerpendicular, 0, 0, 1, true);//label angled with line
4205                        ptCenter = lineutility.MidPointDouble(tg.Pixels.get(1), tg.Pixels.get(2), 0);
4206                        AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, true);
4207                        //AddIntegralModifier(tg, label, aboveMiddle, 0, 1, 2, true);
4208                        ptCenter = lineutility.MidPointDouble(tg.Pixels.get(2), tg.Pixels.get(3), 0);
4209                        AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, true);
4210                        //AddIntegralModifier(tg, label, aboveMiddlePerpendicular, 0, 2, 3, true);
4211                        ptCenter = lineutility.MidPointDouble(tg.Pixels.get(3), tg.Pixels.get(0), 0);
4212                        AddIntegralAreaModifier(tg, label, area, -0.5 * csFactor, ptCenter, ptCenter, true);
4213                        //AddIntegralModifier(tg, label, aboveMiddle, 0, 3, 0, true);
4214                        rfaLines = getRFALines(tg);
4215                        pt0 = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get(1), 0);
4216                        pt1 = lineutility.MidPointDouble(tg.Pixels.get(2), tg.Pixels.get(3), 0);
4217                        ptCenter = lineutility.MidPointDouble(pt0, pt1, 0);
4218                        switch (rfaLines) {
4219                            case 3: // two valid modifiers
4220                                if (!RendererSettings.getInstance().getGroupModifiers()) {
4221                                    AddIntegralAreaModifier(tg, tg.get_Name(), area, -1.5, ptCenter, ptCenter, false);
4222                                    addDTG(tg, area, 0.5 * csFactor, 1.5 * csFactor, ptCenter, ptCenter, metrics);
4223                                } else {
4224                                    String modifier = buildAreaGroupString(tg, label);
4225                                    AddIntegralAreaModifier(tg, modifier, area, 0, ptCenter, ptCenter, false);
4226                                }
4227                                break;
4228                            case 2: // one valid modifier
4229                                if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
4230                                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, ptCenter, ptCenter, false);
4231                                } else {
4232                                    addDTG(tg, area, 0, csFactor, ptCenter, ptCenter, metrics);
4233                                }
4234                                break;
4235                            default:
4236                                break;
4237                        }
4238                        break;
4239                    case TacticalLines.PAA_CIRCULAR:
4240                        for (int i = 0; i < 4; i++) {
4241                            AddIntegralModifier(tg, label, area, -0.5 * csFactor, n / 4 * i, n / 4 * i, false);
4242                        }
4243
4244                        rfaLines = getRFALines(tg);
4245                        ptCenter = lineutility.MidPointDouble(tg.Pixels.get(0), tg.Pixels.get((int) (n / 2.0 + 0.5)), 0);
4246                        switch (rfaLines) {
4247                            case 3: // two valid modifiers
4248                                if (!RendererSettings.getInstance().getGroupModifiers()) {
4249                                    AddIntegralAreaModifier(tg, tg.get_Name(), area, -1.5, ptCenter, ptCenter, false);
4250                                    addDTG(tg, area, 0.5 * csFactor, 1.5 * csFactor, ptCenter, ptCenter, metrics);
4251                                } else {
4252                                    String modifier = buildAreaGroupString(tg, label);
4253                                    AddIntegralAreaModifier(tg, modifier, area, 0, ptCenter, ptCenter, false);
4254                                }
4255                                break;
4256                            case 2: // one valid modifier
4257                                if (tg.get_Name() != null && !tg.get_Name().isEmpty()) {
4258                                    AddIntegralAreaModifier(tg, tg.get_Name(), area, 0, ptCenter, ptCenter, false);
4259                                } else {
4260                                    addDTG(tg, area, 0, csFactor, ptCenter, ptCenter, metrics);
4261                                }
4262                                break;
4263                            default:
4264                                break;
4265                        }
4266                        break;
4267                    case TacticalLines.RANGE_FAN:
4268                        if (tg.get_X() != null) {
4269                            X = tg.get_X().split(",");
4270                            for (j = 0; j < X.length; j++) {
4271                                if (tg.Pixels.size() > j * 102 + 25) {
4272                                    pt0 = tg.Pixels.get(j * 102 + 25);
4273                                    AddAreaModifier(tg, "ALT " + X[j], area, 0, pt0, pt0);
4274                                }
4275                            }
4276                        }
4277                        if (!tg.get_HideOptionalLabels()) {
4278                            String[] am = tg.get_AM().split(",");
4279                            for (j = 0; j < am.length; j++) {
4280                                if (tg.Pixels.size() > j * 102 + 25) {
4281                                    pt0 = tg.Pixels.get(j * 102 + 25);
4282                                    //AddAreaModifier(tg, "RG " + am[j], area, -1, pt0, pt0);
4283                                    if (j == 0)
4284                                        AddAreaModifier(tg, "MIN RG " + removeDecimal(am[j]), 3, -1, pt0, pt0);
4285                                    else
4286                                        AddAreaModifier(tg, "MAX RG " + "(" + Integer.toString(j) + ") " + removeDecimal(am[j]), 3, -1, pt0, pt0);
4287                                }
4288                            }
4289                        }// end if set range fan text
4290                        break;
4291                    case TacticalLines.RANGE_FAN_SECTOR:
4292                    case TacticalLines.RADAR_SEARCH:
4293                        addSectorModifiers(tg, converter);
4294                        break;
4295                    case TacticalLines.ENVELOPMENT:
4296                        AddIntegralModifier(tg, label, aboveMiddle, 0, 0, 1, true);
4297                        break;
4298                    case TacticalLines.MOBILE_DEFENSE:
4299                        AddIntegralModifier(tg, label, area, 0, 16, 16, true);
4300                        break;
4301                    default:
4302                        break;
4303                }//end switch
4304            }
4305            scaleModifiers(tg);
4306            tg.Pixels = origPoints;
4307            g2d.dispose();
4308            g2d = null;
4309        } catch (Exception exc) {
4310            ErrorLogger.LogException(_className, "AddModifiers2",
4311                    new RendererException("Failed inside AddModifiers2", exc));
4312        }
4313    }
4314
4315    /**
4316     * Calculates the highest point that is left-of-center given symbol center point and points
4317     * @param pixels Points that make the symbol
4318     * @param ptCenter Represents the center of the symbol
4319     * @return Point that is the highest point, left-of-center
4320     */
4321    private static POINT2 getHighestPointLeftOfCenter(ArrayList<POINT2> pixels, POINT2 ptCenter)
4322    {
4323        //highest point left of center
4324        POINT2 highest = pixels.get(0);
4325        boolean validPointFound = false;
4326        for (POINT2 p : pixels) //loop through points
4327        {
4328            if(p.x <= ptCenter.x)//we only care about points left of center
4329            {
4330                if(!validPointFound)//find initial left-of-center point
4331                {
4332                    highest = p;//set initial value
4333                    validPointFound = true;
4334                }
4335                else if(p.y < highest.y)//see if this point is higher than the current point
4336                    highest = p;//set new highest, left-of-center point
4337            }
4338        }
4339        return highest;
4340    }
4341    private static String buildAreaGroupString(TGLight tg, String label)
4342    {
4343        int linetype = tg.get_LineType();
4344        String modifier = "";
4345        switch (linetype)
4346        {
4347            case TacticalLines.PAA:
4348            case TacticalLines.PAA_CIRCULAR:
4349            case TacticalLines.PAA_RECTANGULAR:
4350                modifier = tg.get_Name();
4351                if(tg.get_DTG() != null && !tg.get_DTG().isEmpty())
4352                    modifier += "\n" + tg.get_DTG() + " -";
4353                if(tg.get_DTG1() != null && !tg.get_DTG1().isEmpty())
4354                    modifier += "\n" + tg.get_DTG1();
4355                break;
4356            case TacticalLines.ACA:
4357            case TacticalLines.ACA_CIRCULAR:
4358            case TacticalLines.ACA_RECTANGULAR:
4359                modifier += label + " " + tg.get_Name();
4360                modifier += "\n" + tg.get_T1();
4361                modifier += "\n" + "MIN ALT: " + tg.get_X();
4362                modifier += "\n" + "MAX ALT: " + tg.get_X1();
4363                modifier += "\n" + "GRID: " + tg.get_Location();
4364                modifier += "\n" + "EFF: " + tg.get_DTG() + " -";
4365                modifier += "\n" + tg.get_DTG1();
4366                break;
4367            case TacticalLines.FFA:
4368            case TacticalLines.NFA:
4369            case TacticalLines.RFA:
4370            case TacticalLines.FFA_RECTANGULAR:
4371            case TacticalLines.NFA_RECTANGULAR:
4372            case TacticalLines.RFA_RECTANGULAR:
4373            case TacticalLines.FFA_CIRCULAR:
4374            case TacticalLines.NFA_CIRCULAR:
4375            case TacticalLines.RFA_CIRCULAR:
4376                modifier = label + "\n" + tg.get_Name();
4377                if(tg.get_DTG() != null && !tg.get_DTG().isEmpty())
4378                    modifier += "\n" + tg.get_DTG() + " -";
4379                if(tg.get_DTG1() != null && !tg.get_DTG1().isEmpty())
4380                    modifier += "\n" + tg.get_DTG1();
4381                break;
4382            case TacticalLines.FSA:
4383                modifier = label + " " + tg.get_Name();
4384                if(tg.get_DTG() != null && !tg.get_DTG().isEmpty())
4385                    modifier += "\n" + tg.get_DTG() + " -";
4386                if(tg.get_DTG1() != null && !tg.get_DTG1().isEmpty())
4387                    modifier += "\n" + tg.get_DTG1();
4388                break;
4389            case TacticalLines.ATI:
4390            case TacticalLines.CFFZ:
4391            case TacticalLines.CFZ:
4392            case TacticalLines.TBA:
4393            case TacticalLines.TVAR:
4394            case TacticalLines.ZOR:
4395            case TacticalLines.DA:
4396            case TacticalLines.SENSOR:
4397            case TacticalLines.CENSOR:
4398            case TacticalLines.KILLBOXBLUE:
4399            case TacticalLines.KILLBOXPURPLE:
4400            case TacticalLines.KILLBOXBLUE_RECTANGULAR:
4401            case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
4402            case TacticalLines.FSA_RECTANGULAR:
4403            case TacticalLines.ATI_RECTANGULAR:
4404            case TacticalLines.CFFZ_RECTANGULAR:
4405            case TacticalLines.SENSOR_RECTANGULAR:
4406            case TacticalLines.CENSOR_RECTANGULAR:
4407            case TacticalLines.DA_RECTANGULAR:
4408            case TacticalLines.CFZ_RECTANGULAR:
4409            case TacticalLines.ZOR_RECTANGULAR:
4410            case TacticalLines.TBA_RECTANGULAR:
4411            case TacticalLines.TVAR_RECTANGULAR:
4412            case TacticalLines.FSA_CIRCULAR:
4413            case TacticalLines.ATI_CIRCULAR:
4414            case TacticalLines.CFFZ_CIRCULAR:
4415            case TacticalLines.SENSOR_CIRCULAR:
4416            case TacticalLines.CENSOR_CIRCULAR:
4417            case TacticalLines.DA_CIRCULAR:
4418            case TacticalLines.CFZ_CIRCULAR:
4419            case TacticalLines.ZOR_CIRCULAR:
4420            case TacticalLines.TBA_CIRCULAR:
4421            case TacticalLines.TVAR_CIRCULAR:
4422            case TacticalLines.KILLBOXBLUE_CIRCULAR:
4423            case TacticalLines.KILLBOXPURPLE_CIRCULAR:
4424                modifier = label + "\n" + tg.get_Name();
4425                break;
4426            case TacticalLines.WFZ_REVD:
4427            case TacticalLines.OBSFAREA:
4428                modifier = label;
4429                modifier += "\n" + tg.get_Name();
4430                modifier += "\n" + "TIME FROM: " + tg.get_DTG();
4431                modifier += "\n" + "TIME TO: " + tg.get_DTG1();
4432                break;
4433            case TacticalLines.OBSAREA:
4434                modifier = tg.get_Name();
4435                modifier += "\n" + "TIME FROM: " + tg.get_DTG();
4436                modifier += "\n" + "TIME TO: " + tg.get_DTG1();
4437                break;
4438            case TacticalLines.WFZ:
4439            case TacticalLines.ROZ:
4440            case TacticalLines.AARROZ:
4441            case TacticalLines.UAROZ:
4442            case TacticalLines.WEZ:
4443            case TacticalLines.FEZ:
4444            case TacticalLines.JEZ:
4445            case TacticalLines.FAADZ:
4446            case TacticalLines.HIDACZ:
4447            case TacticalLines.MEZ:
4448            case TacticalLines.LOMEZ:
4449            case TacticalLines.HIMEZ:
4450                modifier = label;
4451                modifier += "\n" + tg.get_Name();
4452                modifier += "\n" + "MIN ALT: " + tg.get_X();
4453                modifier += "\n" + "MAX ALT: " + tg.get_X1();
4454                modifier += "\n" + "TIME FROM: " + tg.get_DTG();
4455                modifier += "\n" + "TIME TO: " + tg.get_DTG1();
4456                break;
4457        }
4458        return modifier;
4459    }
4460
4461    private static String buildAreaGroupDTGString(TGLight tg)
4462    {
4463        String modifier = "";
4464
4465        if(tg.get_DTG() != null && !tg.get_DTG().isEmpty())
4466            modifier += "\n" + tg.get_DTG() + " -";
4467        if(tg.get_DTG1() != null && !tg.get_DTG1().isEmpty())
4468            modifier += "\n" + tg.get_DTG1();
4469
4470        return modifier;
4471    }
4472
4473    /**
4474     * Displays the tg modifiers using a client Graphics2D, this is an option
4475     * provided to clients for displaying modifiers without using shapes
4476     *
4477     * @param tg the tactical graphic
4478     * @param g2d the graphics object for drawing
4479     * @deprecated
4480     */
4481    public static void DisplayModifiers(TGLight tg,
4482            Graphics2D g2d) {
4483        try {
4484            Font font = g2d.getFont();
4485            int j = 0;
4486            Modifier2 modifier = null;
4487            g2d.setBackground(Color.white);
4488            POINT2 pt = null;
4489            double theta = 0;
4490            int stringWidth = 0, stringHeight = 0;
4491            FontMetrics metrics = g2d.getFontMetrics();
4492            String s = "";
4493            int x = 0, y = 0;
4494            POINT2 pt1 = null, pt2 = null;
4495            int quadrant = -1;
4496            int n = tg.Pixels.size();
4497            //for (j = 0; j < tg.modifiers.size(); j++) 
4498            for (j = 0; j < n; j++) {
4499                modifier = (Modifier2) tg.modifiers.get(j);
4500                double lineFactor = modifier.lineFactor;
4501                s = modifier.text;
4502                double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
4503                pt = modifier.textPath[0];
4504                x1 = pt.x;
4505                y1 = pt.y;
4506                pt = modifier.textPath[1];
4507                x2 = pt.x;
4508                y2 = pt.y;
4509                theta = Math.atan2(y2 - y1, x2 - x1);
4510                POINT2 midPt;
4511                if (x1 > x2) {
4512                    theta -= Math.PI;
4513                }
4514                switch (modifier.type) {
4515                    case toEnd: //corresponds to LabelAndTextBeforeLineTG
4516                        g2d.rotate(theta, x1, y1);
4517                        stringWidth = metrics.stringWidth(s);
4518                        stringHeight = font.getSize();
4519                        if (x1 < x2 || (x1 == x2 && y1 > y2)) {
4520                            x = (int) x1 - stringWidth;
4521                            y = (int) y1 - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
4522                            g2d.setColor(tg.get_FontBackColor());
4523                            g2d.clearRect(x, y, stringWidth, stringHeight);
4524                            y = (int) y1 + (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
4525                            g2d.setColor(tg.get_TextColor());
4526                            g2d.drawString(s, x, y);
4527                        } else {
4528                            x = (int) x1;
4529                            y = (int) y1 - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
4530                            g2d.setColor(tg.get_FontBackColor());
4531                            g2d.clearRect(x, y, stringWidth, stringHeight);
4532                            y = (int) y1 + (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
4533                            g2d.setColor(tg.get_TextColor());
4534                            g2d.drawString(s, x, y);
4535                        }
4536                        break;
4537                    case aboveMiddle:
4538                        midPt = new POINT2((x1 + x2) / 2, (y1 + y2) / 2);
4539                        g2d.rotate(theta, midPt.x, midPt.y);
4540                        stringWidth = metrics.stringWidth(s);
4541                        stringHeight = font.getSize();
4542                        x = (int) midPt.x - stringWidth / 2;
4543                        y = (int) midPt.y - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
4544                        g2d.setColor(tg.get_FontBackColor());
4545                        g2d.clearRect(x, y, stringWidth, stringHeight);
4546                        y = (int) midPt.y + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
4547                        g2d.setColor(tg.get_TextColor());
4548                        g2d.drawString(s, x, y);
4549                        break;
4550                    case area:
4551                        g2d.rotate(0, x1, y1);
4552                        stringWidth = metrics.stringWidth(s);
4553                        stringHeight = font.getSize();
4554
4555                        x = (int) x1 - stringWidth / 2;
4556                        y = (int) y1 - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
4557                        g2d.setColor(tg.get_FontBackColor());
4558                        g2d.clearRect(x, y, stringWidth, stringHeight);
4559                        y = (int) y1 + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
4560                        g2d.setColor(tg.get_TextColor());
4561                        g2d.drawString(s, x, y);
4562                        break;
4563                    case screen:    //for SCREEN, GUARD, COVER
4564                        if (tg.Pixels.size() >= 14) {
4565                            pt1 = tg.Pixels.get(3);
4566                            pt2 = tg.Pixels.get(10);
4567                            quadrant = lineutility.GetQuadrantDouble(pt1, pt2);
4568                            theta = Math.atan2(pt2.y - pt1.y, pt2.x - pt1.x);
4569                            switch (quadrant) {
4570                                case 1:
4571                                    theta += Math.PI / 2;
4572                                    break;
4573                                case 2:
4574                                    theta -= Math.PI / 2;
4575                                    break;
4576                                case 3:
4577                                    theta -= Math.PI / 2;
4578                                    break;
4579                                case 4:
4580                                    theta += Math.PI / 2;
4581                                    break;
4582                                default:
4583                                    break;
4584                            }
4585
4586                            g2d.rotate(theta, x1, y1);
4587                            stringWidth = metrics.stringWidth(s);
4588                            stringHeight = font.getSize();
4589
4590                            x = (int) x1 - stringWidth / 2;
4591                            y = (int) y1 - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
4592                            g2d.setColor(tg.get_FontBackColor());
4593                            g2d.clearRect(x, y, stringWidth, stringHeight);
4594                            y = (int) y1 + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
4595                            g2d.setColor(tg.get_TextColor());
4596                            g2d.drawString(s, x, y);
4597                        } else {
4598                            stringWidth = metrics.stringWidth(s);
4599                            stringHeight = font.getSize();
4600                            x = (int) tg.Pixels.get(0).x;//(int) x1 - stringWidth / 2;
4601                            y = (int) tg.Pixels.get(0).y;//(int) y1 - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
4602                            g2d.setColor(tg.get_FontBackColor());
4603                            g2d.clearRect(x, y, stringWidth, stringHeight);
4604                            y = (int) y + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
4605                            g2d.setColor(tg.get_TextColor());
4606                            g2d.drawString(s, x, y);
4607                        }
4608                        break;
4609                    default:
4610                        break;
4611                }   //end switch
4612            }   //end for
4613        } catch (Exception exc) {
4614            ErrorLogger.LogException(_className, "DisplayModifiers",
4615                    new RendererException("Failed inside DisplayModifiers", exc));
4616        }
4617    }//end function
4618
4619    /**
4620     * Returns a Shape object for the text background for labels and modifiers
4621     *
4622     * @param tg the tactical graphic object
4623     * @param pt0 1st point of segment
4624     * @param pt1 last point of segment
4625     * @param stringWidth string width
4626     * @param stringHeight string height
4627     * @param lineFactor number of text lines above or below the segment
4628     * @param isTextFlipped true if text is flipped
4629     * @return the modifier shape
4630     */
4631    public static Shape2 BuildModifierShape(
4632            TGLight tg,
4633            POINT2 pt0,
4634            POINT2 pt1,
4635            int stringWidth,
4636            int stringHeight,
4637            double lineFactor,
4638            boolean isTextFlipped) {
4639        Shape2 modifierFill = null;
4640        try {
4641
4642            POINT2 ptTemp0 = new POINT2(pt0), ptTemp1 = new POINT2(pt1);
4643
4644            if (isTextFlipped) {
4645                lineFactor += 1;
4646            }
4647
4648            if (lineFactor < 0) //extend pt0,pt1 above the line
4649            {
4650                ptTemp0 = lineutility.ExtendDirectedLine(pt0, pt1, pt0, 2, -lineFactor * stringHeight);
4651                ptTemp1 = lineutility.ExtendDirectedLine(pt0, pt1, pt1, 2, -lineFactor * stringHeight);
4652            }
4653            if (lineFactor > 0) //extend pt0,pt1 below the line
4654            {
4655                ptTemp0 = lineutility.ExtendDirectedLine(pt0, pt1, pt0, 3, lineFactor * stringHeight);
4656                ptTemp1 = lineutility.ExtendDirectedLine(pt0, pt1, pt1, 3, lineFactor * stringHeight);
4657            }
4658            if (ptTemp0.y == ptTemp1.y) {
4659                ptTemp0.y += 1;
4660            }
4661
4662            POINT2 pt3 = null, pt4 = null, pt5 = null, pt6 = null, pt7 = null;
4663            pt3 = lineutility.ExtendAlongLineDouble(ptTemp0, ptTemp1, -stringWidth);
4664            pt4 = lineutility.ExtendDirectedLine(ptTemp1, ptTemp0, pt3, 0, stringHeight / 2);
4665            pt5 = lineutility.ExtendDirectedLine(ptTemp1, ptTemp0, pt3, 1, stringHeight / 2);
4666            pt6 = lineutility.ExtendDirectedLine(ptTemp1, ptTemp0, ptTemp0, 1, stringHeight / 2);
4667            pt7 = lineutility.ExtendDirectedLine(ptTemp1, ptTemp0, ptTemp0, 0, stringHeight / 2);
4668            modifierFill = new Shape2(Shape2.SHAPE_TYPE_MODIFIER_FILL);
4669
4670            modifierFill.moveTo(pt4);
4671            modifierFill.lineTo(pt5);
4672            modifierFill.lineTo(pt6);
4673            modifierFill.lineTo(pt7);
4674            modifierFill.lineTo(pt4);
4675        } catch (Exception exc) {
4676            ErrorLogger.LogException(_className, "BuildModifierShape",
4677                    new RendererException("Failed inside BuildModifierShape", exc));
4678        }
4679        return modifierFill;
4680    }
4681
4682    /**
4683     * For BOUNDARY and other line types which require breaks for the integral
4684     * text. Currently only boundary uses this
4685     *
4686     * @param tg
4687     * @param g2d the graphics object for drawing
4688     * @param shapes the shape array
4689     */
4690    public static void GetIntegralTextShapes(TGLight tg,
4691            Graphics2D g2d,
4692            ArrayList<Shape2> shapes) {
4693        try {
4694            if (tg.Pixels == null || shapes == null) {
4695                return;
4696            }
4697
4698            HashMap<Integer, Color> hmap = clsUtility.getMSRSegmentColors(tg);
4699            Color color = null;
4700
4701            Shape2 shape = null;
4702            Shape2 segShape = null;//diangostic 1-22-13
4703            g2d.setFont(tg.get_Font());
4704            int j = 0;
4705            String affiliation = null;
4706            FontMetrics metrics = g2d.getFontMetrics();
4707            String echelonSymbol = null;
4708            int stringWidthEchelonSymbol = 0;
4709            //boolean lineTooShort = false;
4710            POINT2 ptEchelonStart = null, ptEchelonEnd = null, midpt,
4711                    ptENY0Start = null, ptENY0End = null, ptENY1Start, ptENY1End, pt0 = null, pt1 = null;
4712            double dist = 0;
4713            BasicStroke stroke = null;
4714            switch (tg.get_LineType()) {
4715                case TacticalLines.BOUNDARY:
4716                    echelonSymbol = tg.get_EchelonSymbol();
4717                    //shapes = new ArrayList();
4718                    shape = new Shape2(Shape2.SHAPE_TYPE_POLYLINE);
4719                    shape.setLineColor(tg.get_LineColor());
4720                    shape.set_Style(tg.get_LineStyle());
4721                    stroke = clsUtility.getLineStroke(tg.get_LineThickness(), shape.get_Style(), tg.get_lineCap(), BasicStroke.JOIN_ROUND);
4722                    shape.setStroke(stroke);
4723                    if (echelonSymbol != null && !echelonSymbol.isEmpty()) {
4724                        stringWidthEchelonSymbol = metrics.stringWidth(echelonSymbol);
4725                    }
4726                    //diagnostic
4727                    if (hmap == null || hmap.isEmpty()) {
4728                        shape.moveTo(tg.Pixels.get(0));
4729                        for (j = 1; j < tg.Pixels.size(); j++) {
4730                            shape.lineTo(tg.Pixels.get(j));
4731                        }
4732                        shapes.add(shape);
4733                        break;
4734                    }
4735                    //end section
4736                    int n = tg.Pixels.size();
4737                    //for (j = 0; j < tg.Pixels.size() - 1; j++) 
4738                    for (j = 0; j < n - 1; j++) {
4739                        segShape = null;
4740                        if (hmap != null) {
4741                            if (hmap.containsKey(j)) {
4742                                color = (Color) hmap.get(j);
4743                                segShape = new Shape2(Shape2.SHAPE_TYPE_POLYLINE);
4744                                segShape.setLineColor(color);
4745                                segShape.set_Style(tg.get_LineStyle());
4746                                segShape.setStroke(stroke);
4747                            }
4748                        }
4749
4750                        pt0 = tg.Pixels.get(j);
4751                        pt1 = tg.Pixels.get(j + 1);
4752                        //lineTooShort = GetBoundarySegmentTooShort(tg, g2d, j);
4753                        if (segShape != null) {
4754                            segShape.moveTo(pt0);
4755                        } else {
4756                            shape.moveTo(pt0);
4757                        }
4758
4759                        //uncoment comment to remove line breaks for GE
4760                        //if (lineTooShort || tg.get_Client().equals("ge")) 
4761                        if (tg.get_Client().equals("ge") || GetBoundarySegmentTooShort(tg, g2d, j) == true) {
4762                            if (segShape != null) {
4763                                segShape.lineTo(pt1);
4764                                shapes.add(segShape);
4765                                continue;
4766                            } else {
4767                                shape.lineTo(pt1);
4768                                continue;
4769                            }
4770                        }
4771
4772                        midpt = lineutility.MidPointDouble(pt0, pt1, 0);
4773                        if (segShape != null) {
4774                            segShape.moveTo(pt0);
4775                        } else {
4776                            shape.moveTo(pt0);
4777                        }
4778
4779                        if (stringWidthEchelonSymbol > 0) {
4780                            midpt = lineutility.MidPointDouble(pt0, pt1, 0);
4781                            dist = lineutility.CalcDistanceDouble(pt0, midpt) - stringWidthEchelonSymbol / 1.5;
4782                            ptEchelonStart = lineutility.ExtendAlongLineDouble(pt0, pt1, dist);
4783                            dist = lineutility.CalcDistanceDouble(pt0, midpt) + stringWidthEchelonSymbol / 1.5;
4784                            ptEchelonEnd = lineutility.ExtendAlongLineDouble(pt0, pt1, dist);
4785                            if (segShape != null) {
4786                                segShape.lineTo(ptEchelonStart);
4787                                segShape.moveTo(ptEchelonEnd);
4788                            } else {
4789                                shape.lineTo(ptEchelonStart);
4790                                shape.moveTo(ptEchelonEnd);
4791                            }
4792                        }
4793                        if (segShape != null) {
4794                            segShape.lineTo(pt1);
4795                        } else {
4796                            shape.lineTo(pt1);
4797                        }
4798                        if (segShape != null) {
4799                            shapes.add(segShape);
4800                        }
4801                    }//end for
4802                    shapes.add(shape);
4803                    break;
4804                default:
4805                    break;
4806            }
4807        } catch (Exception exc) {
4808            ErrorLogger.LogException(_className, "GetIntegralTextShapes",
4809                    new RendererException("Failed inside GetIntegralTextShapes", exc));
4810        }
4811    }
4812
4813    /**
4814     * Displays the modifiers to a Graphics2D from a BufferedImage
4815     *
4816     * @param tg the tactical graphic
4817     * @param g2d the Graphic for drawing
4818     * @param shapes the shape array
4819     * @param isTextFlipped true if text is flipped
4820     * @param converter to convert between geographic and pixel coordinates
4821     */
4822    public static void DisplayModifiers2(TGLight tg,
4823            Graphics2D g2d,
4824            ArrayList<Shape2> shapes,
4825            boolean isTextFlipped,
4826            IPointConversion converter) {
4827        try {
4828            if (shapes == null) {
4829                return;
4830            }
4831
4832            if (tg.modifiers == null || tg.modifiers.isEmpty()) {
4833                return;
4834            }
4835            Font font = null;
4836            int j = 0;
4837            Modifier2 modifier = null;
4838            Color fontBackColor = tg.get_FontBackColor();
4839            double theta = 0;
4840            double stringWidth = 0, stringHeight = 0;
4841            String s = "";
4842            Bitmap image = null;
4843            int x = 0, y = 0;
4844            POINT2 pt0 = null, pt1 = null, pt2 = null, pt3 = null;
4845            int quadrant = -1;
4846            Shape2 shape2 = null;
4847            long lineType = tg.get_LineType();
4848            font = tg.get_Font();    //might have to change this
4849            if (font == null) {
4850                font = g2d.getFont();
4851            }
4852            if (font.getSize() == 0) {
4853                return;
4854            }
4855            g2d.setFont(font);
4856            FontMetrics metrics = g2d.getFontMetrics();
4857            //we need a background color
4858            if (fontBackColor != null) {
4859                g2d.setBackground(fontBackColor);
4860            } else {
4861                g2d.setBackground(Color.white);
4862            }
4863
4864            Point2D anchor = null;
4865            Point2D anchorOffset = null;
4866
4867            int direction = -1;
4868            Point glyphPosition = null;
4869            for (j = 0; j < tg.modifiers.size(); j++) {
4870                modifier = (Modifier2) tg.modifiers.get(j);
4871
4872                double lineFactor = modifier.lineFactor;
4873
4874                if (isTextFlipped) {
4875                    lineFactor = -lineFactor;
4876                }
4877
4878                s = modifier.text;
4879                if (s == null || s.equals("")) {
4880
4881                    image = modifier.image;
4882                    if (image == null) {
4883                        continue;
4884                    }
4885                }
4886                stringWidth = s != null ? (double) metrics.stringWidth(s) + 1 : image.getWidth() + 1;
4887                stringHeight = s != null ? (double) font.getSize() : image.getHeight();
4888
4889                double x1 = 0, y1 = 0, x2 = 0, y2 = 0, dist = 0;
4890                pt0 = modifier.textPath[0];
4891                x1 = Math.round(pt0.x);
4892                y1 = Math.round(pt0.y);
4893                pt1 = modifier.textPath[1];
4894                x2 = Math.round(pt1.x);
4895                y2 = Math.round(pt1.y);
4896                theta = Math.atan2(y2 - y1, x2 - x1);
4897                POINT2 midPt;
4898                if (x1 > x2) {
4899                    theta -= Math.PI;
4900                }
4901                pt0 = new POINT2(x1, y1);
4902                pt1 = new POINT2(x2, y2);
4903                midPt = new POINT2((x1 + x2) / 2, (y1 + y2) / 2);
4904                Point2D modifierPosition = null;  //use this if using justify
4905                int justify = ShapeInfo.justify_left;
4906                switch (modifier.type) {
4907                    case aboveEnd: // On line
4908                    case toEnd: // Next to line
4909                        if (x1 == x2) {
4910                            x2 += 1;
4911                        }
4912
4913                        if (lineFactor >= 0) {
4914                            direction = 2;
4915                        } else {
4916                            direction = 3;
4917                        }
4918
4919                        if (lineType == TacticalLines.LC || tg.get_Client().equalsIgnoreCase("ge")) {
4920                            direction = lineutility.reverseDirection(direction);
4921                        }
4922
4923                        if ((modifier.type == toEnd && x1 < x2) || (modifier.type == aboveEnd && x2 < x1)) {
4924                            justify = ShapeInfo.justify_right;
4925                        } else {
4926                            justify = ShapeInfo.justify_left;
4927                        }
4928
4929                        //3rd point value is location to start perpendicular line from
4930                        pt3 = lineutility.ExtendDirectedLine(pt1, pt0, pt0, direction, lineFactor * stringHeight);
4931                        //pt3 is the end point of the perpendicularline
4932
4933                        glyphPosition = new Point((int) pt3.x, (int) pt3.y);
4934                        modifierPosition = new Point2D.Double(pt3.x, pt3.y);
4935
4936                        anchor = new Point2D.Double(pt0.x, pt0.y);
4937                        anchorOffset = new Point2D.Double(pt3.x - pt0.x, pt3.y - pt0.y);
4938
4939                        //adjust so it doesn't start right on top of the line.
4940                        if(justify == ShapeInfo.justify_right)
4941                            anchorOffset.setLocation(anchorOffset.getX() - font.getSize()/2f,anchorOffset.getY());
4942                        else if(justify == ShapeInfo.justify_left)
4943                            anchorOffset.setLocation(anchorOffset.getX() + font.getSize()/2f,anchorOffset.getY());
4944
4945                        if(image != null)//images are centered and don't have text justification
4946                            anchorOffset = new Point2D.Double(pt1.x - pt0.x, pt1.y - pt0.y);
4947
4948                        break;
4949                    case aboveStartInside:
4950                        //returns pt3 which is based on the specified distance from pt0 along the line of pt0 to pt1.
4951                        pt3 = lineutility.ExtendAlongLineDouble(pt0, pt1, stringWidth);
4952
4953                        glyphPosition = new Point((int) pt3.x, (int) pt3.y);
4954                        modifierPosition = new Point2D.Double((int) pt3.x, pt3.y);
4955
4956                        anchor = new Point2D.Double(pt0.x, pt0.y);
4957                        anchorOffset = new Point2D.Double(pt3.x - pt0.x, pt3.y - pt0.y);
4958                        break;
4959                    case aboveEndInside:
4960                        //returns pt3 which is based on the specified distance from pt0 along the line of pt1 to pt0.
4961                        pt3 = lineutility.ExtendAlongLineDouble(pt1, pt0, stringWidth);
4962
4963                        glyphPosition = new Point((int) pt3.x, (int) pt3.y);
4964                        modifierPosition = new Point2D.Double((int) pt3.x, pt3.y);
4965
4966                        anchor = new Point2D.Double(pt1.x, pt1.y);
4967                        anchorOffset = new Point2D.Double(pt3.x - pt1.x, pt3.y - pt1.y);
4968                        break;
4969                    case aboveMiddle:
4970                    case aboveMiddlePerpendicular:
4971                        pt2 = midPt;
4972                        anchor = new Point2D.Double(midPt.x,midPt.y);
4973
4974                        if (tg.get_Client().equals("2D")) {
4975                            lineFactor += 0.5;
4976                        }
4977
4978                        if (lineFactor >= 0) {
4979                            pt3 = lineutility.ExtendDirectedLine(pt0, pt2, pt2, 3, Math.abs((lineFactor) * stringHeight));
4980                            midPt = lineutility.ExtendDirectedLine(pt0, midPt, midPt, 3, Math.abs((lineFactor) * stringHeight));
4981                        } else {
4982                            pt3 = lineutility.ExtendDirectedLine(pt0, pt2, pt2, 2, Math.abs((lineFactor) * stringHeight));
4983                            midPt = lineutility.ExtendDirectedLine(pt0, midPt, midPt, 2, Math.abs((lineFactor) * stringHeight));
4984                        }
4985                        //pt3=lineutility.ExtendDirectedLine(pt0, pt2, pt2, 2, lineFactor*stringHeight);
4986                        if (x1 == x2 && y1 > y2) {
4987                            pt3 = lineutility.ExtendDirectedLine(pt0, pt2, pt2, 1, Math.abs((lineFactor) * stringHeight));
4988                            midPt = lineutility.ExtendDirectedLine(pt0, midPt, midPt, 1, Math.abs((lineFactor) * stringHeight));
4989                        }
4990                        if (x1 == x2 && y1 < y2) {
4991                            pt3 = lineutility.ExtendDirectedLine(pt0, pt2, pt2, 0, Math.abs((lineFactor) * stringHeight));
4992                            midPt = lineutility.ExtendDirectedLine(pt0, midPt, midPt, 0, Math.abs((lineFactor) * stringHeight));
4993                        }
4994
4995                        glyphPosition = new Point((int) pt3.x, (int) pt3.y);
4996                        justify = ShapeInfo.justify_center;
4997                        modifierPosition = new Point2D.Double(midPt.x, midPt.y);
4998
4999                        //anchor = new Point2D.Double(midPt.x, midPt.y);
5000                        anchorOffset = new Point2D.Double(midPt.x - anchor.getX(), midPt.y - anchor.getY());
5001
5002                        if(modifier.type == aboveMiddlePerpendicular) {
5003                            // Need to negate the original rotation
5004                            if (x1 > x2) {
5005                                theta += Math.PI;
5006                            }
5007                            // Adjust the label rotation based on the y values
5008                            if (y1 > y2) {
5009                                theta += Math.PI;
5010                            }
5011                            // Rotate by 90 degrees. This is how we rotate the label perpendicular to the line
5012                            theta -= Math.PI / 2;
5013                        }
5014                        break;
5015                    case area:
5016                        theta = 0;
5017
5018                        //y = (int) y1 + (int) (stringHeight / 2) + (int) (1.25 * lineFactor * stringHeight);
5019                        y = (int) y1 + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
5020                        x = image != null ? (int) (x1 - stringWidth / 3) : (int) x1;
5021
5022                        glyphPosition = new Point(x, y);
5023                        justify = ShapeInfo.justify_center;
5024                        modifierPosition = new Point2D.Double(x, y);
5025
5026                        anchor = new Point2D.Double(x1, y1);
5027                        anchorOffset = new Point2D.Double(x - x1, y - y1);
5028                        break;
5029                    case areaImage:
5030                        glyphPosition = new Point((int)x1, (int)y1);
5031                        justify = ShapeInfo.justify_center;
5032                        modifierPosition = new Point2D.Double((int)x1, (int)y1);
5033
5034                        anchor = new Point2D.Double(x1, y1);
5035                        anchorOffset = new Point2D.Double(0, 0);
5036                        break;
5037                    case screen:    //for SCREEN, GUARD, COVER, not currently used
5038                        if (tg.Pixels.size() >= 14) {
5039                            pt1 = tg.Pixels.get(3);
5040                            pt2 = tg.Pixels.get(10);
5041                            quadrant = lineutility.GetQuadrantDouble(pt1, pt2);
5042                            theta = Math.atan2(pt2.y - pt1.y, pt2.x - pt1.x);
5043                            if (Math.abs(theta) < Math.PI / 8) {
5044                                if (theta < 0) {
5045                                    theta -= Math.PI / 2;
5046                                } else {
5047                                    theta += Math.PI / 2;
5048                                }
5049                            }
5050                            switch (quadrant) {
5051                                case 1:
5052                                    theta += Math.PI / 2;
5053                                    break;
5054                                case 2:
5055                                    theta -= Math.PI / 2;
5056                                    break;
5057                                case 3:
5058                                    theta -= Math.PI / 2;
5059                                    break;
5060                                case 4:
5061                                    theta += Math.PI / 2;
5062                                    break;
5063                                default:
5064                                    break;
5065                            }
5066
5067                            x = (int) x1 - (int) stringWidth / 2;
5068                            y = (int) y1 - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
5069                            y = (int) y1 + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
5070                            anchor = new Point2D.Double(x1, y1);
5071                        } else {
5072                            theta = 0;
5073                            x = (int) tg.Pixels.get(0).x;
5074                            y = (int) tg.Pixels.get(0).y;
5075                            anchor = new Point2D.Double(x, y);
5076                            x = (int) x - (int) stringWidth / 2;
5077                            y = (int) y - (int) stringHeight / 2 + (int) (lineFactor * stringHeight);
5078                            y = (int) y + (int) (stringHeight / 2) + (int) (lineFactor * stringHeight);
5079                        }
5080
5081                        glyphPosition = new Point(x, y);
5082                        //glyphPosition=new Point2D.Double(x,y);
5083                        //anchor = new Point2D.Double(x1, y1);
5084                        anchorOffset = new Point2D.Double(x - anchor.getX(), y - anchor.getY());
5085                        break;
5086                    default:
5087                        break;
5088                }   //end switch
5089
5090                shape2 = new Shape2(Shape2.SHAPE_TYPE_MODIFIER_FILL);
5091
5092                shape2.setStroke(new BasicStroke(0, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3));
5093
5094                if (tg.get_TextColor() != null) {
5095                    shape2.setFillColor(tg.get_TextColor());
5096                } else if (tg.get_LineColor() != null) {
5097                    shape2.setFillColor(tg.get_LineColor());
5098                }
5099                if (tg.get_LineColor() != null) {
5100                    shape2.setLineColor(tg.get_LineColor());
5101                }
5102                //only GE uses the converter, generic uses the affine transform and draws at 0,0
5103                if (converter != null) {
5104                    shape2.setGlyphPosition(glyphPosition);
5105                } else {
5106                    shape2.setGlyphPosition(new Point2D.Double(0, 0));
5107                }
5108                //shape2.setGlyphPosition(new Point(0,0));
5109                //added two settings for use by GE
5110                if(s != null && !s.equals("")) {
5111                    shape2.setModifierString(s);
5112                    TextLayout tl = new TextLayout(s, font, g2d.getFontMetrics().getFontRenderContext());
5113                    shape2.setTextLayout(tl);
5114                    shape2.setTextJustify(justify);
5115                } else if (image != null) {
5116                    shape2.setModifierImage(image);
5117                }
5118                //shape2.setModifierStringPosition(glyphPosition);//M. Deutch 7-6-11
5119                shape2.setModifierAngle(theta * 180 / Math.PI);
5120                shape2.setModifierPosition(modifierPosition);
5121                shape2.setModifierAnchor(anchor);
5122                shape2.setModifierAnchorOffset(anchorOffset);
5123
5124                if (shape2 != null) {
5125                    shapes.add(shape2);
5126                }
5127
5128            }   //end for
5129        } //end try
5130        catch (Exception exc) {
5131            ErrorLogger.LogException(_className, "DisplayModifiers2",
5132                    exc);
5133        }
5134    }//end function
5135
5136    /**
5137     * Builds a shape object to wrap text
5138     *
5139     * @param g2d the Graphic object for drawing
5140     * @param str text to wrap
5141     * @param font the draw font
5142     * @param tx the drawing transform, text rotation and translation
5143     * @return
5144     */
5145    public static Shape getTextShape(Graphics2D g2d,
5146                                     String str,
5147                                     Font font,
5148                                     AffineTransform tx) {
5149        TextLayout tl = null;
5150        FontRenderContext frc = null;
5151        try {
5152            frc = g2d.getFontRenderContext();
5153            tl = new TextLayout(str, font, frc);
5154        } catch (Exception exc) {
5155            ErrorLogger.LogException(_className, "getTextShape",
5156                    new RendererException("Failed inside getTextShape", exc));
5157        }
5158        return tl.getOutline(tx);
5159    }
5160
5161    /**
5162     * Creates text outline as a shape
5163     *
5164     * @param originalText the original text
5165     * @return text shape
5166     */
5167    public static Shape2 createTextOutline(Shape2 originalText) {
5168        Shape2 siOutline = null;
5169        try {
5170            Shape outline = originalText.getShape();
5171
5172            siOutline = new Shape2(Shape2.SHAPE_TYPE_MODIFIER_FILL);
5173            siOutline.setShape(outline);
5174
5175            if (originalText.getFillColor().getRed() == 255
5176                    && originalText.getFillColor().getGreen() == 255
5177                    && originalText.getFillColor().getBlue() == 255) {
5178                siOutline.setLineColor(Color.BLACK);
5179            } else {
5180                siOutline.setLineColor(Color.WHITE);
5181            }
5182
5183            int width = RendererSettings.getInstance().getTextOutlineWidth();
5184
5185            siOutline.setStroke(new BasicStroke(width, BasicStroke.CAP_ROUND,
5186                    BasicStroke.JOIN_ROUND, 3));
5187
5188        } catch (Exception exc) {
5189            ErrorLogger.LogException(_className, "createTextOutline",
5190                    new RendererException("Failed inside createTextOutline", exc));
5191        }
5192        return siOutline;
5193    }
5194
5195    /**
5196     * Channels don't return points in tg.Pixels. For Channels modifiers we only
5197     * need to collect the points, don't need internal arrays, and can calculate
5198     * on which segments the modifiers lie.
5199     *
5200     * @param shape
5201     * @return
5202     */
5203    private static ArrayList<POINT2> getShapePoints(Shape shape) {
5204        try {
5205            ArrayList<Point2D> ptsPoly = new ArrayList();
5206            Point2D ptPoly = null;
5207            double[] coords = new double[6];
5208            int zeros = 0;
5209            for (PathIterator i = shape.getPathIterator(null); !i.isDone(); i.next()) {
5210                int type = i.currentSegment(coords);
5211                if (type == 0 && zeros == 2) {
5212                    break;
5213                }
5214                switch (type) {
5215                    case PathIterator.SEG_MOVETO:
5216                        ptPoly = new Point2D.Double(coords[0], coords[1]);
5217                        ptsPoly.add(ptPoly);
5218                        zeros++;
5219                        break;
5220                    case PathIterator.SEG_LINETO:
5221                        ptPoly = new Point2D.Double(coords[0], coords[1]);
5222                        ptsPoly.add(ptPoly);
5223                        break;
5224                    case PathIterator.SEG_QUADTO: //quadTo was never used
5225                        break;
5226                    case PathIterator.SEG_CUBICTO:  //curveTo was used for some METOC's
5227                        break;
5228                    case PathIterator.SEG_CLOSE:    //closePath was never used
5229                        break;
5230                }
5231            }
5232            if (ptsPoly.size() > 0) {
5233                ArrayList<POINT2> pts = null;
5234                pts = new ArrayList();
5235                for (int j = 0; j < ptsPoly.size(); j++) {
5236                    Point2D pt2d = ptsPoly.get(j);
5237                    POINT2 pt = new POINT2(pt2d.getX(), pt2d.getY());
5238                    pts.add(pt);
5239                }
5240                return pts;
5241            }
5242        } catch (Exception exc) {
5243            ErrorLogger.LogException(_className, "getshapePoints",
5244                    new RendererException("Failed inside getShapePoints", exc));
5245        }
5246        return null;
5247    }
5248
5249    private static Bitmap getImageModifier(TGLight tg) {
5250        String symbolID = tg.get_SymbolId();
5251        ImageInfo symbol = null;
5252        Map<String,String> mods = new HashMap<>();
5253        Map<String,String> sa = new HashMap<>();
5254        sa.put(MilStdAttributes.PixelSize, String.valueOf(tg.get_IconSize()));
5255        int contaminationCode = EntityCode.getSymbolForContaminationArea(SymbolID.getEntityCode(symbolID));
5256        int modifier1Code = SymbolID.getModifier1(symbolID);
5257        int lineType = GetLinetypeFromString(symbolID);
5258        if (contaminationCode > 0) {
5259            sa.put(MilStdAttributes.OutlineSymbol, "true");
5260            sa.put(MilStdAttributes.FillColor, RendererUtilities.colorToHexString(tg.get_FillColor(), true));
5261            sa.put(MilStdAttributes.LineColor, RendererUtilities.colorToHexString(tg.get_LineColor(), true));
5262            String contaminationSP = SymbolID.setEntityCode(symbolID, contaminationCode);
5263            contaminationSP = SymbolID.setHQTFD(contaminationSP, 0); // Remove dummy modifier if necessary
5264            symbol = SinglePointRenderer.getInstance().RenderSP(contaminationSP, mods, sa);
5265        } else if (lineType == TacticalLines.DEPICT || lineType == TacticalLines.MINED || lineType == TacticalLines.FENCED || lineType == TacticalLines.MINE_LINE) {
5266            if (modifier1Code < 13 || modifier1Code > 50) {
5267                // Invalid mine type
5268                modifier1Code = 13;//unspecified mine (default value if not specified as per MilStd 2525)
5269                symbolID = SymbolID.setModifier1(symbolID, modifier1Code);
5270            }
5271            if (tg.get_KeepUnitRation()) {
5272                sa.put(MilStdAttributes.PixelSize, String.valueOf((int) (tg.get_IconSize() * 1.5)));
5273            }
5274            sa.put(MilStdAttributes.OutlineSymbol, "true");
5275            symbol = SinglePointRenderer.getInstance().RenderModifier(symbolID, sa);
5276        } else if (lineType == TacticalLines.LAA && modifier1Code > 0) {
5277            sa.put(MilStdAttributes.OutlineSymbol, "true");
5278            sa.put(MilStdAttributes.FillColor, RendererUtilities.colorToHexString(tg.get_FillColor(), true));
5279            sa.put(MilStdAttributes.LineColor, RendererUtilities.colorToHexString(tg.get_LineColor(), true));
5280            if (tg.get_KeepUnitRation()) {
5281                sa.put(MilStdAttributes.PixelSize, String.valueOf((int) (tg.get_IconSize() * 1.5)));
5282            }
5283            symbol = SinglePointRenderer.getInstance().RenderModifier(symbolID, sa);
5284        }
5285        else if (lineType == TacticalLines.DECISION_LINE) {
5286
5287            sa.put(MilStdAttributes.PixelSize, String.valueOf((int) (tg.get_IconSize() * 1.5)));
5288            sa.put(MilStdAttributes.KeepUnitRatio, String.valueOf((tg.get_KeepUnitRation())));
5289            sa.put(MilStdAttributes.FillColor, RendererUtilities.colorToHexString(tg.get_FillColor(), true));
5290            sa.put(MilStdAttributes.LineColor, RendererUtilities.colorToHexString(tg.get_LineColor(), true));
5291            sa.put(MilStdAttributes.OutlineSymbol, "false");
5292            mods.put(Modifiers.T_UNIQUE_DESIGNATION_1, (tg.get_Name()));
5293
5294            String decisionPoint = SymbolID.setEntityCode(symbolID, EntityCode.EntityCode_Decision_Point);
5295            symbol = SinglePointRenderer.getInstance().RenderSP(decisionPoint, mods, sa);
5296        }
5297        else if (lineType == TacticalLines.ANCHORAGE_LINE || lineType == TacticalLines.ANCHORAGE_AREA) {
5298            sa.put(MilStdAttributes.OutlineSymbol, "false");
5299            String anchorPoint = SymbolID.setEntityCode(symbolID, EntityCode.EntityCode_AnchoragePoint);
5300            symbol = SinglePointRenderer.getInstance().RenderSP(anchorPoint, mods, sa);
5301        }
5302
5303        if (symbol != null)
5304            return symbol.getImage();
5305        else
5306            return null;
5307    }
5308
5309    private static String removeDecimal(double doubleVal) {
5310        return String.valueOf(Math.round(doubleVal));
5311    }
5312
5313    private static String removeDecimal(String strDoubleVal) {
5314        if (strDoubleVal.indexOf(" ") > 0) // String contains unit
5315            return removeDecimal(Double.parseDouble(strDoubleVal.substring(0, strDoubleVal.indexOf(" ")))) + strDoubleVal.substring(strDoubleVal.indexOf(" "));
5316        else
5317            return removeDecimal(Double.parseDouble(strDoubleVal));
5318    }
5319}