diff --git a/README.md b/README.md index 6afd9b9a..1af6fd48 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,17 @@ About mil-sym-java is a well worn set of java libraries that have been used in US Army Mission Command software for years. In November 2013 Mission Command was given the approval to release and maintain these libraries as public open source. [Developer's Guide Wiki](https://github.com/missioncommand/mil-sym-java/wiki/2525C-Renderer-Overview) -[Google Group Discussion Forum](https://groups.google.com/forum/#!forum/mission-command-milstd-renderer) +[JavaDocs](https://missioncommand.github.io/javadoc/2525C/java/index.html) See new projects for 2525D+ rendering: [Java](https://github.com/missioncommand/mil-sym-java) [Android](https://github.com/missioncommand/mil-sym-android) [TypeScript](https://github.com/missioncommand/mil-sym-ts) +Other 2525C releases +[JavaScript](https://github.com/missioncommand/mil-sym-ts/wiki/2525C-Renderer-Overview) +[Android](https://github.com/missioncommand/mil-sym-android/wiki/2525C-Renderer-Overview) + MIL-STD-2525 ----------- The [MIL-STD-2525] standard defines how to visualize military symbology. This project provides support for the entire MIL-STD-2525B Change II plus USAS 13-14 and MIL-STD-2525C. @@ -42,12 +46,6 @@ Updated to deploy on MavenCentral. Search for mil-sym-renderer. groupdID changed to io.github.missioncommand -https://s01.oss.sonatype.org/#nexus-search;quick~mil-sym-renderer - -https://s01.oss.sonatype.org/#nexus-search;quick~mil-sym-service - -Also: - https://search.maven.org/search?q=a:mil-sym-renderer https://search.maven.org/search?q=a:mil-sym-service diff --git a/core/JavaLineArray/pom.xml b/core/JavaLineArray/pom.xml index bc15da98..f3d6c29e 100644 --- a/core/JavaLineArray/pom.xml +++ b/core/JavaLineArray/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.45 ../.. diff --git a/core/JavaLineArray/src/main/java/JavaTacticalRenderer/TGLight.java b/core/JavaLineArray/src/main/java/JavaTacticalRenderer/TGLight.java index d40e468f..f422ed09 100644 --- a/core/JavaLineArray/src/main/java/JavaTacticalRenderer/TGLight.java +++ b/core/JavaLineArray/src/main/java/JavaTacticalRenderer/TGLight.java @@ -414,52 +414,55 @@ public void set_SymbolId(String value) { } //build the echelon symbol from the echelon //regarless of symbolId.length - if (echelon.equals("M")) //REGION + if(echelon != null) { - echelonSymbol = "XXXXXX"; - } else if (echelon.equals("L")) //FRONT - { - echelonSymbol = "XXXXX"; - } else if (echelon.equals("K")) //ARMY - { - echelonSymbol = "XXXX"; - } else if (echelon.equals("J")) //CORPS - { - echelonSymbol = "XXX"; - } else if (echelon.equals("I")) //DIVISION - { - echelonSymbol = "XX"; - } else if (echelon.equals("H")) //BRIGADE - { - echelonSymbol = "X"; - } else if (echelon.equals("G")) //REGIMENT - { - echelonSymbol = "III"; - } else if (echelon.equals("F")) //BATTALION - { - echelonSymbol = "II"; - } else if (echelon.equals("E")) //COMPANY - { - echelonSymbol = "I"; - } else if (echelon.equals("D")) //PLATOON - { - letter = (char) 9679; - s = Character.toString(letter); - echelonSymbol = s + s + s; - } else if (echelon.equals("C")) //SECTION - { - letter = (char) 9679; - s = Character.toString(letter); - echelonSymbol = s + s; - } else if (echelon.equals("B")) //SQUAD - { - letter = (char) 9679; - s = Character.toString(letter); - echelonSymbol = s; - } else if (echelon.equals("A")) //GROUP - { - letter = (char) 216; - echelonSymbol = Character.toString(letter); + if (echelon.equals("M")) //REGION + { + echelonSymbol = "XXXXXX"; + } else if (echelon.equals("L")) //FRONT + { + echelonSymbol = "XXXXX"; + } else if (echelon.equals("K")) //ARMY + { + echelonSymbol = "XXXX"; + } else if (echelon.equals("J")) //CORPS + { + echelonSymbol = "XXX"; + } else if (echelon.equals("I")) //DIVISION + { + echelonSymbol = "XX"; + } else if (echelon.equals("H")) //BRIGADE + { + echelonSymbol = "X"; + } else if (echelon.equals("G")) //REGIMENT + { + echelonSymbol = "III"; + } else if (echelon.equals("F")) //BATTALION + { + echelonSymbol = "II"; + } else if (echelon.equals("E")) //COMPANY + { + echelonSymbol = "I"; + } else if (echelon.equals("D")) //PLATOON + { + letter = (char) 9679; + s = Character.toString(letter); + echelonSymbol = s + s + s; + } else if (echelon.equals("C")) //SECTION + { + letter = (char) 9679; + s = Character.toString(letter); + echelonSymbol = s + s; + } else if (echelon.equals("B")) //SQUAD + { + letter = (char) 9679; + s = Character.toString(letter); + echelonSymbol = s; + } else if (echelon.equals("A")) //GROUP + { + letter = (char) 216; + echelonSymbol = Character.toString(letter); + } } } catch (Exception exc) { ErrorLogger.LogException(_className, "set_SymbolId", diff --git a/core/JavaLineArray/src/main/java/JavaTacticalRenderer/clsMETOC.java b/core/JavaLineArray/src/main/java/JavaTacticalRenderer/clsMETOC.java index 3c1f59e1..21af6cad 100644 --- a/core/JavaLineArray/src/main/java/JavaTacticalRenderer/clsMETOC.java +++ b/core/JavaLineArray/src/main/java/JavaTacticalRenderer/clsMETOC.java @@ -39,7 +39,7 @@ public static int IsWeather(String symbolID) { try { //added section for revD - if(symbolID.length()>15) + if(symbolID != null && symbolID.length()>15) { //test for hold,brdghd //String setA=Modifier2.getSetA(symbolID); diff --git a/core/JavaRenderer/pom.xml b/core/JavaRenderer/pom.xml index 98d97a31..dfc8dbc6 100644 --- a/core/JavaRenderer/pom.xml +++ b/core/JavaRenderer/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.45 ../.. diff --git a/core/JavaRendererServer/pom.xml b/core/JavaRendererServer/pom.xml index 0bedd540..ce134c87 100644 --- a/core/JavaRendererServer/pom.xml +++ b/core/JavaRendererServer/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.45 ../.. diff --git a/core/JavaRendererServer/src/main/java/RenderMultipoints/clsUtilityCPOF.java b/core/JavaRendererServer/src/main/java/RenderMultipoints/clsUtilityCPOF.java index 0241876a..66dd6a4a 100644 --- a/core/JavaRendererServer/src/main/java/RenderMultipoints/clsUtilityCPOF.java +++ b/core/JavaRendererServer/src/main/java/RenderMultipoints/clsUtilityCPOF.java @@ -984,6 +984,7 @@ protected static TGLight GetCircularRangeFanFillTG(TGLight tg) { try { //instantiate a dummy tg which will be used to call GetSectorRangeFan tg1 = new TGLight(); + tg1.set_SymbolId(""); tg1.set_VisibleModifiers(true); tg1.set_LineThickness(0); tg1.set_FillColor(tg.get_FillColor()); diff --git a/core/JavaRendererUtils/pom.xml b/core/JavaRendererUtils/pom.xml index fc6aab6b..c4b197ca 100644 --- a/core/JavaRendererUtils/pom.xml +++ b/core/JavaRendererUtils/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.45 ../.. diff --git a/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/MilStdSymbol.java b/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/MilStdSymbol.java index eadb1f58..9025cae9 100644 --- a/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/MilStdSymbol.java +++ b/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/MilStdSymbol.java @@ -1307,6 +1307,301 @@ public void set_WasClipped(boolean value) { public boolean get_WasClipped() { return _wasClipped; } + + + /** + * Determines if the symbol has integral or modifier/amplifier text that would + * be impacted if the maps is zoomed in or out after initial draw. + * @return 0=not sensitive, 1=slightly little zoom in sensitive, 2=zoom in sensitive, 3=zoom in/out sensitive + */ + public int isTextScaleSensitive() + { + ArrayList modifiers = this.getModifierShapes(); + if(_Properties == null) + return 0;//no scale sensitive text + if (_Properties.isEmpty()) + return 0; + else if(_symbolID.startsWith("G")) + { + String id = SymbolUtilities.getBasicSymbolID(_symbolID); + SymbolDef sd = SymbolDefTable.getInstance().getSymbolDef(id, this.getSymbologyStandard()); + if(sd != null) + { + int dr = sd.getDrawCategory(); + switch (dr) + { + case SymbolDef.DRAW_CATEGORY_ARROW: + if(_Properties.containsKey(ModifiersTG.W_DTG_1) || + _Properties.containsKey(ModifiersTG.W1_DTG_2)) + return 3; + else + return 0; + + case SymbolDef.DRAW_CATEGORY_LINE://Linear Targets (2407##) + if(id.startsWith("G*F*LT")) + { + if(_Properties.containsKey(ModifiersTG.T1_UNIQUE_DESIGNATION_2)) + return 3; + else if(_Properties.containsKey(ModifiersTG.T_UNIQUE_DESIGNATION_1)) + return 2; + else + return 0; + } + break; + case SymbolDef.DRAW_CATEGORY_RECTANGULAR_PARAMETERED_AUTOSHAPE: + if(modifiers != null && modifiers.size() > 1) + return 3; + else + return 0; + case SymbolDef.DRAW_CATEGORY_CIRCULAR_PARAMETERED_AUTOSHAPE: + if(modifiers != null && modifiers.size() > 1) + return 3; + else + return 0; + + default: + break; + } + } + + switch (id) + { + + //A Little Zoom in sensitive (1) + case "G*G*GLP---****X"://Phase line, only 5% sensitive + case "G*G*DLF---****X"://Forward Edge of Battle, only 5% sensitive + case "G*S*LCM---****X"://Moving Convoy + case "G*S*LCH---****X"://Halted Convoy + return 1; + + //Zoom in sensitive (2) + case "G*G*GLL---****X"://Light Line + case "G*G*OLF---****X"://Final Coordination Line + case "G*G*OLL---****X"://Limit of Advance + case "G*G*OLT---****X"://Line of Departure + case "G*G*OLC---****X"://Line of Departure / Line of Contact + case "G*G*OLP---****X"://Probable Line of Deployment + case "G*G*SLB---****X"://Bridgehead Line + case "G*G*SLH---****X"://Holding Line + case "G*G*SLR---****X"://Release Line + case "G*G*SAN---****X"://Named Area of Interest Line (NAI) + case "G*S*LRM---****X"://Main Supply Route (MSR) + case "G*S*LRO---****X"://One Way Traffic + case "G*S*LRW---****X"://Two Way Traffic + case "G*S*LRT---****X"://Alternating Traffic + case "G*S*LRA---****X"://Alternate Supply Route (ASR) + return 2; + case "G*G*GLF---****X"://FLOT + case "G*G*GLC---****X"://LOC + if(SymbolUtilities.getAffiliation(_symbolID).equals("H")) + return 2; + else + return 1; + + //Very Zoom in/out sensitive (multi-line text) (3) + //friendly and more than 1 text + //Hostile and more than 3 text assuming ENY is present + case "G*G*GLB---****X"://boundary line + if(_Properties.containsKey(ModifiersTG.T_UNIQUE_DESIGNATION_1) || + _Properties.containsKey(ModifiersTG.T1_UNIQUE_DESIGNATION_2)) + return 3; + else + return 0; + + + case "G*G*GAY---****X"://Limited Access Area if sector 1 modifier present + if(_Properties.containsKey(ModifiersTG.H_ADDITIONAL_INFO_1)) + return 3; + else + return 0; + + + //Labels all contained in area but can drift away from each-other or overlap + case "G*G*GAG---****X": //Generic + case "G*G*AAH---****X": //High-Density Airspace Control Zone + case "G*G*AAR---****X": //Restricted Operations Zone (ROZ) + case "G*G*AAM---****X": //Missile Engagement Zone (MEZ) + case "G*G*AAML--****X": //Low (Altitude) Missile Engagement Zone (LOMEZ) + case "G*G*AAMH--****X": //High (Altitude) Missile Engagement Zone (HIMEZ) + case "G*G*AAF---****X": //Short Range Air Defense Engagement Zone (SHORADEZ) + case "G*G*AAW---****X": //Weapons Free Zone + case "G*F*ACAI--****X": //Airspace Coordination Area (ACA) - Irregular + case "G*F*ACFI--****X": //Free Fire Area (FFA) - Irregular + case "G*F*ACNI--****X": //No Fire Area (NFA) - Irregular + case "G*F*ACRI--****X": //Restricted Fire Area (RFA) - Irregular + case "G*F*ATS---****X": //Smoke + case "G*F*ACSI--****X"://Fire Support Area - Irregular + case "G*F*AZII--****X": //Artillery Target Intelligence Zone (ATI), - Irregular + case "G*F*AZXI--****X": //Call For Fire Zone (CFFZ) - Irregular + case "G*F*AZCI--****X": //Censor Zone, - Irregular + case "G*F*AZFI--****X": //Critical Friendly Zone (CFZ), - Irregular + case "G*F*ACDI--****X": //Dead Space Area (DA), - Irregular + case "G*F*ACEI--****X": //Sensor Zone, Irregular + case "G*F*ACBI--****X": //Target Build-up Area, Irregular + case "G*F*ACVI--****X": //Target Value Area, Irregular + case "G*F*ACZI--****X": //Zone of Responsibility, Irregular + //case "G*F*ACT---****X": //Terminally Guided Munition Footprint (TGMF) + case "G*F*AKBI--****X": //Blue Kill Box, Irregular + case "G*F*AKPI--****X": //Purple Kill Box, Irregular + case "G*M*OGF---****X": //Obstacle Free Zone + case "G*M*BCL---****X": //Lane + case "G*S*AD----****X": //Detainee Holding Area + case "G*S*AE----****X": //Enemy Prisoner of War Holding Area + case "G*S*AR----****X": //Forward Arming and Refueling Point (FARP) + case "G*S*AH----****X": //Refugee Holding Area + case "G*S*ASR---****X": //Regimental Support Area (RSA) + case "G*G*ALC---****X": //Air Corridor + case "G*G*ALM---****X": //MRR + case "G*G*ALS---****X": //SAAFR + case "G*G*ALU---****X": //UA + case "G*G*ALL---****X": //LLTR + if(modifiers != null && modifiers.size()>1) + return 3; + else + return 0; + + case "G*F*LCF---****X"://Fire Support Coordination Line (FSCL) + case "G*F*LCC---****X"://Coordinated Fire Line (CFL) + case "G*F*LCN---****X"://No Fire Line + case "G*F*LCR---****X"://Restrictive Fire Line + if(_Properties.containsKey(ModifiersTG.W_DTG_1) || + _Properties.containsKey(ModifiersTG.W1_DTG_2)) + return 3; + else + return 2; + + case "G*F*LCM---****X"://Munition Flight Path + case "G*T*L-----****X"://Delay + if(_Properties.containsKey(ModifiersTG.W_DTG_1) || + _Properties.containsKey(ModifiersTG.W1_DTG_2)) + return 2; + else + return 0; + + + default://No Scale Sensitive text + return 0; + } + } + + return 0; + } + + /** + * Checks if the symbol has features that make it scale aware and would require a refresh + * on zooming in or out. + * @return 0=No,1=arrowheads,2=decoratedLines,3=patternFills + */ + public int isSymbolScaleSensitive() + { + //return SymbolUtilities.isScaleAware(this._symbolID); + String id = SymbolUtilities.getBasicSymbolID(_symbolID); + + switch (id) + { + //ArrowHead or smaller detail + case "G*G*OAF---****X"://Attack By Fire + case "G*G*OAS---****X"://Support By Fire + case "G*G*GAS---****X"://Search Area/Reconnaissance Area + case "G*G*SLA---****X"://Ambush + case "G*G*OLKA--****X"://Airborne/Aviation + case "G*G*OLKGM-****X"://Direction of Main attack + case "G*G*OLKGS-****X"://Direction of Supporting attack + case "G*F*LT----****X"://Linear Target + case "G*F*LTS---****X"://Linear Smoke Target + case "G*F*LTF---****X"://Final Protective Fire + case "G*T*T-----****X"://Disrupt + case "G*M*OET---****X"://Turn + case "G*M*BDE---****X"://Obstacle Bypass Easy + case "G*M*BDD---****X"://Obstacle Bypass Difficult + case "G*M*BDI---****X"://Obstacle Bypass Impossible + case "G*M*BCB---****X"://Bridge or Gap? + case "G*M*BCA---****X"://Assault Crossing? + case "G*M*OS----****X"://Abatis? + case "G*M*BCL---****X"://?Lane? + case "G*M*BCF---****X"://Ferry + case "G*M*BCR---****X"://Raft Site + case "G*T*H-----****X"://Breach + case "G*T*Y-----****X"://Bypass + case "G*T*C-----****X"://Canalize + case "G*T*X-----****X"://Clear + case "G*T*L-----****X"://Delay + case "G*M*OED---****X"://Disrupt + case "G*T*A-----****X"://Follow and Assume + case "G*T*AS----****X"://Follow and Support + //case "G*T*O-----****X"://Occupy, details relative to size + //case "G*T*P-----****X"://Penetrate, details relative to size + //case "G*T*R-----****X"://Relief in Place (RIP), details relative to size + //case "G*T*M-----****X"://Retire/Retirement, details relative to size + case "G*T*S-----****X"://Secure, details relative to size + case "G*T*UC----****X"://Cover, details relative to size + case "G*T*UG----****X"://Guard, details relative to size + case "G*T*US----****X"://Screen, details relative to size + case "G*T*Z-----****X"://Seize, details relative to size + case "G*T*W-----****X"://Withdraw, details relative to size + case "G*T*WP----****X"://Withdraw under pressure, details relative to size + return 1;//arrowhead + + //Decorated Lines + case "G*G*GLF---****X"://FLOT + case "G*G*GLC---****X"://Line of Contact + //case 140500://?Principal Direction of Fire? + case "G*G*GAF---****X"://Fortified Area + case "G*G*SAE---****X"://Encirclement + case "G*M*SP----****X"://Strong Point + case "G*G*DABP--****X"://Battle Position Prepared (P) but not Occupied + case "G*T*J-----****X"://Contain + case "G*T*Q-----****X"://Retain + case "G*M*OGB---****X"://Obstacle Belt + case "G*M*OGL---****X"://Obstacle Line + case "G*M*OGZ---****X"://Obstacle Zone + case "G*M*OGF---****X"://Obstacle Free Area + case "G*M*OGR---****X"://Obstacle Restricted Area + //case "G*MPOEF---****X"://Fix? + case "G*G*PY----****X"://Mined Area, Fenced + case "G*M*OHO---****X"://Overhead Wire + case "G*M*OADU--****X"://Ditch Under Construction + case "G*M*OADC--****X"://Ditch Completed + case "G*M*OAR---****X"://Ditch Reinforced + case "G*M*OAW---****X"://Antitank Wall + case "G*M*OWU---****X"://Wire Obstacles, Unspecified + case "G*M*OWS---****X"://Wire Obstacles, Single Fence + case "G*M*OWD---****X"://Wire Obstacles, Double Fence + case "G*M*OWA---****X"://Wire Obstacles, Double Apron Fence + case "G*M*OWL---****X"://Wire Obstacles, Low Wire Fence + case "G*M*OWH---****X"://Wire Obstacles, High Wire Fence + case "G*M*OWCS--****X"://Wire Obstacles, Single Concertina + case "G*M*OWCD--****X"://Wire Obstacles, Double Strand Concertina + case "G*M*OWCT--****X"://Wire Obstacles, Triple Strand Concertina + case "G*M*SL----****X"://Fortified Line + case "G*S*LCM---****X"://Moving Convoy + case "G*S*LCH---****X"://Halted Convoy + case "G*S*LRO---****X"://MSR One Way Traffic + case "G*S*LRW---****X"://MSR Two Way Traffic + case "G*S*LRT---****X"://MSR Alternating Traffic + case "G*T*F-----****X"://Fix + //case 341500://Isolate, most detail contained inside + //case 342600://Cordon and Knock, most detail contained inside + //case 342700://Cordon and Search, most detail contained inside + return 2;//decoration + + //Areas with Pattern Fill + case "G*G*GAY---****X"://Limited Access Area + case "G*G*AAW---****X"://Weapons Free Zone + case "G*F*ACNI--****X"://NFA Irregular + case "G*F*ACNR--****X"://NFA Rectangular + case "G*F*ACNC--****X"://NFA Circular + case "G*M*NB----****X"://Bio Contaminated Area + case "G*M*NC----****X"://Chem Contaminated Area + case "G*M*NR----****X"://Rad Contaminated Area + if(!this.getUsePatternFill()) + return 3;//pattern fill + else + return 0; + default: + return 0; + } + } /** * returns just the symbol as an ImageInfo object. diff --git a/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/RectUtilities.java b/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/RectUtilities.java new file mode 100644 index 00000000..5a63f89a --- /dev/null +++ b/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/RectUtilities.java @@ -0,0 +1,108 @@ +/* + * Copyright 2025 Michael.P.Spinelli. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ArmyC2.C2SD.Utilities; + +import java.awt.Rectangle; +import java.awt.geom.Rectangle2D; + +/** + * + */ +public class RectUtilities { + + public static Rectangle makeRectangleFromRect(int x1, int y1, int x2, int y2) { + return new Rectangle(x1, y1, x2-x1, y2-y1); + } + + public static Rectangle2D makeRectangle2DFromRect(double x1, double y1, double x2, double y2) { + return new Rectangle2D.Double(x1, y1, x2-x1, y2-y1); + } + + public static Rectangle2D makeRectangle2DFromRect(float x1, float y1, float x2, float y2) { + return new Rectangle2D.Float(x1, y1, x2-x1, y2-y1); + } + + /** + * Copies a Rectangle + * @param rect {@link Rectangle2D} + * @return {@link Rectangle2D} + */ + public static Rectangle2D copyRect(Rectangle2D rect) { + return new Rectangle2D.Double((int)rect.getX(), (int)rect.getY(), (int)(rect.getWidth()+0.5), (int)(rect.getHeight()+0.5)); + } + + /** + * copies and rounds the points. x,y round down & width,height round up + * @param rect {@link Rectangle2D} + * @return {@link Rectangle2D} + */ + public static Rectangle2D roundRect(Rectangle2D rect) { + double offsetX = rect.getX() - (int)(rect.getX()); + double offsetY = rect.getY() - (int)(rect.getY()); + + return new Rectangle2D.Double((int)rect.getX(), (int)rect.getY(), (int)(Math.round(rect.getWidth()+offsetX+0.5)), (int)Math.round(rect.getHeight()+offsetY+0.5)); + } + + public static void grow(Rectangle2D rect, int size) { + rect.setRect(rect.getX() - size, rect.getY() - size, rect.getWidth() + (size*2), rect.getHeight() + (size*2)); + //return new Rectangle2D.Double(rect.left - size, rect.top - size, rect.right + size, rect.bottom + size); + } + + + public static void shift(Rectangle2D rect, int x, int y) { + rect.setRect(rect.getX() + x, rect.getY() + y, rect.getWidth(), rect.getHeight()); + } + + + public static void shiftBR(Rectangle2D rect, int x, int y) { + rect.setRect(rect.getX(), rect.getY(), rect.getWidth() + x, rect.getHeight() + y); + } + + public static Rectangle toRectangle(Rectangle2D b) { + if (b == null) { + return null; + }/*from w ww . j a va 2s . c o m*/ + if (b instanceof Rectangle) { + return (Rectangle) b; + } else { + return new Rectangle((int) b.getX(), (int) b.getY(), + (int) (b.getWidth()+0.5), (int) (b.getHeight()+0.5)); + } + } + + public static Rectangle toRectangle(double x, double y, double w, double h) { + return new Rectangle((int) x, (int) y, + (int)(w + 0.5), (int)(h + 0.5)); + } + + public static Rectangle2D toRectangle2D(double x, double y, double w, double h) { + return new Rectangle2D.Double(x, y, + w, h); + } + + public static Rectangle2D toRectangle2D(Rectangle b) { + if (b == null) { + return null; + }/*from w ww . j a va 2s . c o m*/ + else if (b instanceof Rectangle2D) { + return (Rectangle2D) b; + } else { + return new Rectangle2D.Double(b.getX(), b.getY(), + b.getWidth(), b.getHeight()); + } + } + +} diff --git a/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/RendererSettings.java b/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/RendererSettings.java index a33b6248..fb9f5f4b 100644 --- a/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/RendererSettings.java +++ b/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/RendererSettings.java @@ -143,6 +143,8 @@ public class RendererSettings { private boolean _TwoLabelOnly = true; private double _overscanScale = 1.0; + + private boolean _autoAdjustScale = true; //acevedo - 12/8/17 - allow the setting of affiliation colors. private Color _friendlyUnitFillColor = AffiliationColors.FriendlyUnitFillColor; @@ -811,6 +813,19 @@ public void setOverscanScale(double overscanScale) { public double getOverscanScale() { return this._overscanScale; } + + /** + * Will attempt to adjust the scale if it doesn't seem to make sense with the passed in bbox. + * If you wish to have absolute control over the scale, set to false. + * @param autoAdjustScale default true + */ + public void setAutoAdjustScale(boolean autoAdjustScale) { + this._autoAdjustScale = autoAdjustScale; + } + + public boolean getAutoAdjustScale() { + return this._autoAdjustScale; + } /** * get the preferred fill affiliation color for units. diff --git a/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/ShapeInfo.java b/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/ShapeInfo.java index 17c3ce70..89fd13a7 100644 --- a/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/ShapeInfo.java +++ b/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/ShapeInfo.java @@ -400,6 +400,14 @@ public void setTexturePaint(TexturePaint value) { texturePaint=value; } + public BufferedImage getPatternFillImage() + { + if(texturePaint != null && texturePaint.getImage() != null) + return texturePaint.getImage(); + else + return null; + } + public int getFillStyle() { diff --git a/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/SymbolDraw.java b/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/SymbolDraw.java index f3fdb64a..a836880f 100644 --- a/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/SymbolDraw.java +++ b/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/SymbolDraw.java @@ -435,7 +435,7 @@ public static ArrayList createTextOutlineQuick(ShapeInfo originalText siOutline3.setLineColor(backgroundColor); siOutline4.setLineColor(backgroundColor); - Stroke tempStroke = new BasicStroke(1,BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3); + BasicStroke tempStroke = new BasicStroke(1,BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3); siOutline1.setStroke(tempStroke); siOutline2.setStroke(tempStroke); siOutline3.setStroke(tempStroke); @@ -490,7 +490,7 @@ else if(originalText.getGlyphVector() != null) siOutline3.setLineColor(backgroundColor); siOutline4.setLineColor(backgroundColor); - Stroke tempStroke = new BasicStroke(1,BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3); + BasicStroke tempStroke = new BasicStroke(1,BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3); siOutline1.setStroke(tempStroke); siOutline2.setStroke(tempStroke); siOutline3.setStroke(tempStroke); @@ -534,7 +534,7 @@ else if(originalText.getShape() != null) siOutline3.setLineColor(backgroundColor); siOutline4.setLineColor(backgroundColor); - Stroke tempStroke = new BasicStroke(1,BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3); + BasicStroke tempStroke = new BasicStroke(1,BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3); siOutline1.setStroke(tempStroke); siOutline2.setStroke(tempStroke); siOutline3.setStroke(tempStroke); @@ -680,7 +680,7 @@ public static ArrayList createSinglePointOutline(ShapeInfo symbolFram siOutline7.setLineColor(backgroundColor); siOutline8.setLineColor(backgroundColor); - Stroke tempStroke = new BasicStroke(1,BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3); + BasicStroke tempStroke = new BasicStroke(1,BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3); siOutline1.setStroke(tempStroke); siOutline2.setStroke(tempStroke); siOutline3.setStroke(tempStroke); @@ -756,7 +756,7 @@ else if(symbolFrame.getGlyphVector() != null) siOutline7.setLineColor(backgroundColor); siOutline8.setLineColor(backgroundColor); - Stroke tempStroke = new BasicStroke(1,BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3); + BasicStroke tempStroke = new BasicStroke(1,BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3); siOutline1.setStroke(tempStroke); siOutline2.setStroke(tempStroke); siOutline3.setStroke(tempStroke); @@ -818,7 +818,7 @@ else if(symbolFrame.getShape() != null) siOutline7.setLineColor(backgroundColor); siOutline8.setLineColor(backgroundColor); - Stroke tempStroke = new BasicStroke(1,BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3); + BasicStroke tempStroke = new BasicStroke(1,BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3); siOutline1.setStroke(tempStroke); siOutline2.setStroke(tempStroke); siOutline3.setStroke(tempStroke); diff --git a/core/pom.xml b/core/pom.xml index 9acbeaa0..c991dcfb 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.45 .. diff --git a/pom.xml b/pom.xml index 139785b1..088edd5c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.45 pom mil-sym-java diff --git a/renderer/RendererPluginInterface/pom.xml b/renderer/RendererPluginInterface/pom.xml index a8f00a67..b3eafe27 100644 --- a/renderer/RendererPluginInterface/pom.xml +++ b/renderer/RendererPluginInterface/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.45 ../.. diff --git a/renderer/mil-sym-renderer/pom.xml b/renderer/mil-sym-renderer/pom.xml index 05581cd8..82f2190b 100644 --- a/renderer/mil-sym-renderer/pom.xml +++ b/renderer/mil-sym-renderer/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.45 ../.. diff --git a/renderer/mil-sym-renderer/pomJava7.xml b/renderer/mil-sym-renderer/pomJava7.xml index 16fddc35..9e634be3 100644 --- a/renderer/mil-sym-renderer/pomJava7.xml +++ b/renderer/mil-sym-renderer/pomJava7.xml @@ -7,7 +7,7 @@ sec.web.renderer mil-sym-renderer jar - 0.1.43 + 0.1.45 mil-sym-renderer http://maven.apache.org diff --git a/renderer/mil-sym-renderer/pomJava8.xml b/renderer/mil-sym-renderer/pomJava8.xml index 52c75a08..8a309879 100644 --- a/renderer/mil-sym-renderer/pomJava8.xml +++ b/renderer/mil-sym-renderer/pomJava8.xml @@ -7,7 +7,7 @@ sec.web.renderer mil-sym-renderer jar - 0.1.43 + 0.1.45 mil-sym-renderer http://maven.apache.org diff --git a/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/MultiPointHandler.java b/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/MultiPointHandler.java index 0e311df2..59fa59bd 100644 --- a/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/MultiPointHandler.java +++ b/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/MultiPointHandler.java @@ -140,7 +140,7 @@ public static void NormalizeGECoordsToGEExtents(double leftLongitude, * @param pt2d * @return */ - private static Point2D NormalizeCoordToGECoord(Point2D pt2d) { + protected static Point2D NormalizeCoordToGECoord(Point2D pt2d) { Point2D ptGeo = null; try { double x = pt2d.getX(), y = pt2d.getY(); @@ -406,10 +406,25 @@ private static boolean crossesIDL(ArrayList geoCoords) { * @param symbolID * @return */ - public static Boolean ShouldClipSymbol(String symbolID) { + public static Boolean ShouldClipSymbol(String symbolID) + { + return ShouldClipSymbol(symbolID, false, false); + } + + /** + * Checks if a symbol is one with decorated lines which puts a strain on + * google earth when rendering like FLOT. These complicated lines should be + * clipped when possible. + * + * @param symbolID + * @param useDashArray default true, some symbols don't need to be clipped if using dash array MilStdAttribute + * @param useFillPattern default true, some symbols don't need to be clipped if using fill pattern MilStdAttribute + * @return + */ + public static Boolean ShouldClipSymbol(String symbolID, boolean useDashArray, boolean useFillPattern) { String affiliation = SymbolUtilities.getStatus(symbolID); - if (symbolID.substring(0, 1).equals("G") && affiliation.equals("A")) { + if (symbolID.substring(0, 1).equals("G") && affiliation.equals("A") && !useDashArray) { //SymbolDef sd = SymbolDefTable.getInstance().getSymbolDef(symbolID); //if(sd.getDrawCategory()==SymbolDef.DRAW_CATEGORY_LINE || // sd.getDrawCategory()==SymbolDef.DRAW_CATEGORY_POLYGON) @@ -422,14 +437,14 @@ public static Boolean ShouldClipSymbol(String symbolID) { return true; } + boolean shouldClip = false; String id = SymbolUtilities.getBasicSymbolID(symbolID); - if (id.equals("G*T*F-----****X") - || id.equals("G*F*LCC---****X") ||//CFL + if ( + id.equals("G*F*LCC---****X") ||//CFL id.equals("G*G*GLB---****X") || id.equals("G*G*GLF---****X") || id.equals("G*G*GLC---****X") || id.equals("G*G*GAF---****X") - || id.equals("G*G*AAW---****X") || id.equals("G*G*DABP--****X") || id.equals("G*G*OLP---****X") || id.equals("G*G*PY----****X") @@ -441,7 +456,6 @@ public static Boolean ShouldClipSymbol(String symbolID) { || id.equals("G*G*ALS---****X") || id.equals("G*G*SLB---****X") || id.equals("G*G*SLH---****X") - || id.equals("G*G*GAY---****X") || id.equals("G*M*OFA---****X") || id.equals("G*M*OGB---****X") || id.equals("G*M*OGL---****X") @@ -452,8 +466,7 @@ public static Boolean ShouldClipSymbol(String symbolID) { || id.equals("G*M*OADC--****X") || id.equals("G*M*OAR---****X") || id.equals("G*M*OAW---****X") - || id.equals("G*M*OEF---****X") || //Obstacles Effect Fix - id.equals("G*M*OMC---****X") + || id.equals("G*M*OEF---****X") //Obstacles Effect Fix || id.equals("G*M*OWU---****X") || id.equals("G*M*OWS---****X") || id.equals("G*M*OWD---****X") @@ -469,15 +482,6 @@ public static Boolean ShouldClipSymbol(String symbolID) { id.equals("G*M*BCE---****X") || //Ford Easy id.equals("G*M*SL----****X") || id.equals("G*M*SP----****X") - || id.equals("G*M*NR----****X") - || id.equals("G*M*NB----****X") - || id.equals("G*M*NC----****X") - || id.equals("G*F*ACNI--****X") - || id.equals("G*F*ACNR--****X") - || id.equals("G*F*ACNC--****X") - || id.equals("G*F*AKBC--****X") - || id.equals("G*F*AKBI--****X") - || id.equals("G*F*AKBR--****X") || id.equals("G*F*AKPC--****X") || id.equals("G*F*AKPI--****X") || id.equals("G*F*AKPR--****X") @@ -492,17 +496,45 @@ public static Boolean ShouldClipSymbol(String symbolID) { || id.equals("G*S*LRW---****X") || id.equals("G*T*Q-----****X") || id.equals("G*T*E-----****X") - || id.equals("G*T*F-----****X") || //Tasks Fix - id.equals("G*T*K-----****X") || //counterattack. - id.equals("G*T*KF----****X") || //counterattack by fire. - id.equals("G*G*PA----****X") || //AoA for Feint - id.equals("G*M*ORP---****X") - || id.equals("G*M*ORS---****X") - || id.equals("G*T*A-----****X")) { - return true; - } else { - return false; + || id.equals("G*T*F-----****X") || //Tasks Fix + id.equals("G*G*PA----****X") //AoA for Feint + ) + { + shouldClip = true;//decorated lines + } + else if(!useFillPattern) + { + if(id.equals("G*G*GAY---****X") //limited access area + || id.equals("G*G*AAW---****X")//weapons free zone + || id.equals("G*M*NB----****X")//bio area + || id.equals("G*M*NC----****X")//chem area + || id.equals("G*M*NR----****X")//radioactive area + || id.equals("G*F*AKBC--****X")//kill box blue circular + || id.equals("G*F*AKBI--****X")//kb irr + || id.equals("G*F*AKBR--****X")//kb rect + || id.equals("G*F*ACNI--****X")//NFA + || id.equals("G*F*ACNR--****X")//NFA rectnagular + || id.equals("G*F*ACNC--****X")//NFA circular + ) + { + shouldClip = true;//not using fill pattern so clip to not draw more lines than we have to + } + } + else if (!useDashArray) + { + if(id.equals("G*M*OMC---****X") //mine cluster + || id.equals("G*T*K-----****X") //counterattack. + || id.equals("G*T*KF----****X") //counterattack by fire. + || id.equals("G*M*ORP---****X") //blown bridges planned + || id.equals("G*M*ORS---****X") //blown bridges explosive + || id.equals("G*T*A-----****X") //follow and assume + ) + { + shouldClip = true;//not using dash array so clip to not draw more lines than we have to + } } + + return shouldClip; } /** @@ -516,6 +548,8 @@ public static Boolean ShouldClipSymbol(String symbolID) { */ private static double getReasonableScale(String bbox, double origScale) { double scale = origScale; + if(!RendererSettings.getInstance().getAutoAdjustScale()) + return origScale; try { String[] bounds = bbox.split(","); double left = Double.valueOf(bounds[0]).doubleValue(); @@ -826,13 +860,8 @@ public static String RenderSymbol(String id, //if(normalize) //NormalizeGECoordsToGEExtents(0,360,geoCoords2); - //disable clipping unless it spans IDL - if (ShouldClipSymbol(symbolCode) == false) { - if (crossesIDL(geoCoords) == false) { - rect = null; - bboxCoords = null; - } - } + + tgl.set_SymbolId(symbolCode);// "GFGPSLA---****X" AMBUSH symbol code tgl.set_Pixels(null); @@ -841,7 +870,14 @@ public static String RenderSymbol(String id, MilStdSymbol mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null); - mSymbol.setUseDashArray(false); + if(format == 3)//GEOSVG + { + mSymbol.setUseDashArray(true); + //mSymbol.setUsePatternFill(true); + } + else + mSymbol.setUseDashArray(false); + //set milstd symbology standard. mSymbol.setSymbologyStandard(symStd); @@ -850,6 +886,15 @@ public static String RenderSymbol(String id, } else { mSymbol.setFillColor(null); } + + + //disable clipping unless it spans IDL + if (ShouldClipSymbol(symbolCode, mSymbol.getUseDashArray(), mSymbol.getUsePatternFill()) == false) { + if (crossesIDL(geoCoords) == false) { + rect = null; + bboxCoords = null; + } + } //check for required points & parameters String symbolIsValid = canRenderMultiPoint(mSymbol); @@ -922,7 +967,7 @@ public static String RenderSymbol(String id, textColor = Color.white;//textColor = "#FFFFFFFF"; } - jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, mSymbol.get_WasClipped()); + jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, mSymbol.get_WasClipped(),mSymbol.isTextScaleSensitive(),mSymbol.isSymbolScaleSensitive()); jsonOutput.append(jsonContent); //if there's a symbol fill or line pattern, add to KML////////// @@ -985,9 +1030,21 @@ public static String RenderSymbol(String id, jsonOutput.append(symbolCode); jsonOutput.append("\",\"wasClipped\":\""); jsonOutput.append(String.valueOf(mSymbol.get_WasClipped())); + jsonOutput.append("\",\"textScaleSensitive\":\""); + jsonOutput.append(String.valueOf(mSymbol.isTextScaleSensitive())); + jsonOutput.append("\",\"symbolScaleSensitive\":\""); + jsonOutput.append(String.valueOf(mSymbol.isSymbolScaleSensitive())); jsonOutput.append("\"}}"); } + else if(format == 3)//GEOSVG + { + String textColor = mSymbol.getTextColor() != null ? SymbolUtilities.colorToHexString(mSymbol.getTextColor(), false) : ""; + String backgroundColor = mSymbol.getTextBackgroundColor() != null ? SymbolUtilities.colorToHexString(mSymbol.getTextBackgroundColor(), false) : ""; + //returns an svg with a geoTL and geoBR value to use to place the canvas on the map + jsonContent = MultiPointHandlerSVG.GeoSVGize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, backgroundColor, mSymbol.get_WasClipped()); + jsonOutput.append(jsonContent); + } } catch (Exception exc) { String st = JavaRendererUtilities.getStackTrace(exc); @@ -1462,13 +1519,7 @@ public static MilStdSymbol RenderSymbolAsMilStdSymbol(String id, //if(normalize) //NormalizeGECoordsToGEExtents(0,360,geoCoords2); - //disable clipping unless it spans IDL - if (ShouldClipSymbol(symbolCode) == false) { - if (crossesIDL(geoCoords) == false) { - rect = null; - bboxCoords = null; - } - } + tgl.set_SymbolId(symbolCode);// "GFGPSLA---****X" AMBUSH symbol code tgl.set_Pixels(null); @@ -1491,6 +1542,14 @@ public static MilStdSymbol RenderSymbolAsMilStdSymbol(String id, Color fc = mSymbol.getFillColor(); fillColor = Integer.toHexString(fc.getRGB()); } + + //disable clipping unless it spans IDL + if (ShouldClipSymbol(symbolCode,mSymbol.getUseDashArray(),mSymbol.getUsePatternFill()) == false) { + if (crossesIDL(geoCoords) == false) { + rect = null; + bboxCoords = null; + } + } //get pixel values in case we need to do a fill. if (mSymbol.getModifierMap().containsKey(SYMBOL_FILL_IDS) @@ -1766,7 +1825,15 @@ public static String RenderSymbol2D(String id, try { MilStdSymbol mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null); - mSymbol.setUseDashArray(false); + + if(format == 3)//GEOSVG + { + mSymbol.setUseDashArray(true); + //mSymbol.setUsePatternFill(true); + } + else + mSymbol.setUseDashArray(false); + //set milstd symbology standard. mSymbol.setSymbologyStandard(symStd); @@ -1794,7 +1861,7 @@ public static String RenderSymbol2D(String id, // { // ((PointConversion)ipc).set_normalize(false); // } - if (ShouldClipSymbol(symbolCode) || crossesIDL(geoCoords)) { + if (ShouldClipSymbol(symbolCode,mSymbol.getUseDashArray(),mSymbol.getUsePatternFill()) || crossesIDL(geoCoords)) { temp = ipc.GeoToPixels(new Point2D.Double(left, top)); leftX = (int) temp.getX(); topY = (int) temp.getY(); @@ -1857,7 +1924,7 @@ public static String RenderSymbol2D(String id, { textColor = Color.white;//textColor = "#FFFFFFFF"; } - jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, mSymbol.get_WasClipped()); + jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, mSymbol.get_WasClipped(),mSymbol.isTextScaleSensitive(),mSymbol.isSymbolScaleSensitive()); jsonOutput.append(jsonContent); //if there's a symbol fill or line pattern, add to KML////////// @@ -1894,9 +1961,21 @@ public static String RenderSymbol2D(String id, jsonOutput.append(symbolCode); jsonOutput.append("\",\"wasClipped\":\""); jsonOutput.append(String.valueOf(mSymbol.get_WasClipped())); + jsonOutput.append("\",\"textScaleSensitive\":\""); + jsonOutput.append(String.valueOf(mSymbol.isTextScaleSensitive())); + jsonOutput.append("\",\"symbolScaleSensitive\":\""); + jsonOutput.append(String.valueOf(mSymbol.isSymbolScaleSensitive())); jsonOutput.append("\"}}"); } + else if (format == 3)//GEOSVG + { + String textColor = mSymbol.getTextColor() != null ? SymbolUtilities.colorToHexString(mSymbol.getTextColor(), false) : ""; + String backgroundColor = mSymbol.getTextBackgroundColor() != null ? SymbolUtilities.colorToHexString(mSymbol.getTextBackgroundColor(), false) : ""; + //returns an svg with a geoTL and geoBR value to use to place the canvas on the map + jsonContent = MultiPointHandlerSVG.GeoSVGize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, backgroundColor, mSymbol.get_WasClipped()); + jsonOutput.append(jsonContent); + } } catch (Exception exc) { jsonOutput = new StringBuilder(); @@ -2203,7 +2282,7 @@ public static String RenderSymbol2DX(String id, if (mSymbol.getFillColor() != null) { fillColor = Integer.toHexString(mSymbol.getFillColor().getRGB());//Integer.toHexString(shapeInfo.getFillColor().getRGB() } - jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, mSymbol.getLineColor(), mSymbol.get_WasClipped()); + jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, mSymbol.getLineColor(), mSymbol.get_WasClipped(),mSymbol.isTextScaleSensitive(),mSymbol.isSymbolScaleSensitive()); jsonOutput.append(jsonContent); } @@ -3056,7 +3135,9 @@ private static String KMLize(String id, String name, IPointConversion ipc, boolean normalize, Color textColor, - boolean wasClipped) { + boolean wasClipped, + int textScaleSensitive, + int symbolScaleSensitive) { StringBuilder kml = new StringBuilder(); @@ -3073,6 +3154,8 @@ private static String KMLize(String id, String name, kml.append(""); kml.append("").append(symbolCode).append(""); kml.append("").append(wasClipped).append(""); + kml.append("").append(textScaleSensitive).append(""); + kml.append("").append(symbolScaleSensitive).append(""); kml.append(""); for (int i = 0; i < len; i++) { diff --git a/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/MultiPointHandlerSVG.java b/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/MultiPointHandlerSVG.java new file mode 100644 index 00000000..d05d1ed3 --- /dev/null +++ b/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/MultiPointHandlerSVG.java @@ -0,0 +1,503 @@ +/* + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package sec.web.renderer; + +import ArmyC2.C2SD.Utilities.ErrorLogger; +import ArmyC2.C2SD.Utilities.IPointConversion; +import ArmyC2.C2SD.Utilities.RendererSettings; +import ArmyC2.C2SD.Utilities.ShapeInfo; +import ArmyC2.C2SD.Utilities.SymbolDraw; +import ArmyC2.C2SD.Utilities.SymbolUtilities; +import com.sun.org.apache.xerces.internal.impl.dv.util.Base64; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import javax.imageio.ImageIO; +import sec.web.renderer.utilities.Path; +import sec.web.renderer.utilities.SVGTextInfo; + + +/** + * + */ +public class MultiPointHandlerSVG { + public static String GeoSVGize(String id, String name, String description, String symbolID, ArrayList shapes, ArrayList modifiers, IPointConversion ipc, boolean normalize, String textColor, String textBackgroundColor, boolean wasClipped) { + return GeoSVGize(id, name, description, symbolID, shapes, modifiers, ipc, normalize, textColor, textBackgroundColor, wasClipped, null); + } + + /** + * Generates an SVG which can be draped on a map. + * Better with RenderSymbol2D + * + * @param id + * @param name + * @param description + * @param symbolID + * @param shapes {@link ShapeInfo[]} + * @param modifiers {@link ShapeInfo[]} + * @param ipc {@link IPointConversion} + * @param normalize + * @param textColor + * @param textBackgroundColor + * @param wasClipped + * @return + */ + public static String GeoSVGize(String id, String name, String description, String symbolID, ArrayList shapes, ArrayList modifiers, IPointConversion ipc, boolean normalize, String textColor, String textBackgroundColor, boolean wasClipped, Rectangle bbox) { + + int height = 10; + + Rectangle tempBounds = null; + ArrayList paths = new ArrayList<>(); + Rectangle pathBounds = null; + ArrayList labels = new ArrayList<>(); + Rectangle labelBounds = null; + Rectangle unionBounds = null; + float lineWidth; + String fillTexture = null; + Point2D geoCoordTL = null; + Point2D geoCoordTR = null; + Point2D geoCoordBL = null; + Point2D geoCoordBR = null; + Point2D west = null; + Point2D north = null; + Point2D south = null; + Point2D east = null; + int len = shapes.size(); + + try { + Font fontInfo = RendererSettings.getInstance().getMPLabelFont(); + height = fontInfo.getSize(); + + + for (int i = 0; i < len; i++) { + tempBounds = new Rectangle(); + String svg = MultiPointHandlerSVG.ShapesToGeoSVG(symbolID, shapes.get(i), tempBounds, ipc, normalize); + if (svg != null) { + BasicStroke bsTemp = (BasicStroke)shapes.get(i).getStroke(); + lineWidth = bsTemp.getLineWidth(); + tempBounds.grow(Math.round(lineWidth / 2), Math.round(lineWidth / 2));//adjust for line width so nothing gets clipped. + if (pathBounds == null) + pathBounds = (Rectangle) tempBounds.clone(); + else + pathBounds = pathBounds.union(tempBounds); + paths.add(svg); + + + if (shapes.get(i).getPatternFillImage() != null && fillTexture == null) + fillTexture = getFillPattern(shapes.get(i)); + } + } + + ShapeInfo tempModifier; + int len2 = modifiers.size(); + SVGTextInfo tiTemp = null; + for (int j = 0; j < len2; j++) { + tempModifier = modifiers.get(j); + + if (tempModifier.getModifierString() != null && !tempModifier.getModifierString().isEmpty()) { + Point2D tempLocation = tempModifier.getModifierStringPosition(); + + int justify = tempModifier.getTextJustify(); + String strJustify = "start"; + if (justify == ShapeInfo.justify_left) + strJustify = "start"; + else if (justify == ShapeInfo.justify_center) + strJustify = "middle"; + else if (justify == ShapeInfo.justify_right) + strJustify = "end"; + + double degrees = tempModifier.getModifierStringAngle(); + tiTemp = new SVGTextInfo(tempModifier.getModifierString(), tempLocation, fontInfo, strJustify, degrees); + + Rectangle bounds = tiTemp.getTextBounds().getBounds(); + + //make sure labels are in the bbox, otherwise they can + //make the canvas grow out of control. + //if (tiTemp && bbox.containsRectangle(bounds)) + //if(bbox !== null) + if (tiTemp != null) { + if ((bbox != null && bbox.intersects(bounds)) || bbox == null) { + labels.add(tiTemp); + if (bounds != null) { + if (labelBounds != null) + labelBounds = labelBounds.union(bounds); + else + labelBounds = bounds; + } + } + } + } + /* //Not implemented in 2525C renderer + else if (tempModifier.getModifierImage() != null) + { + BufferedImage imgModifier = tempModifier.getModifierImage(); + Rectangle bounds = new Rectangle(0, 0, imgModifier.getWidth(), imgModifier.getHeight()); + + Point2D tempLocation = tempModifier.getModifierStringPosition(); + tempLocation.setLocation(tempLocation.getX() - bounds.getWidth() / 2, tempLocation.getY() - bounds.getHeight() / 2); + int x = (int) tempLocation.getX(); + int y = (int) tempLocation.getY(); + bounds.setLocation(x, y); + + double angle = tempModifier.getModifierStringAngle(); + paths.add(""); + if (angle != 0) { + Rectangle2D bounds2D = SVGTextInfo.getRotatedRectangleBounds(bounds, tempLocation, -angle, "middle"); + bounds = bounds2D.getBounds(); + } + if (bounds != null) { + if ((bbox != null && bbox.intersects(bounds)) || bbox == null) { + if (pathBounds != null) + pathBounds = pathBounds.union(bounds); + else + pathBounds = bounds; + } + } + }//*/ + } + if (pathBounds != null) { + unionBounds = (Rectangle) pathBounds.clone(); + } + if (labelBounds != null) { + if (unionBounds != null) { + unionBounds = unionBounds.union(labelBounds); + } else { + unionBounds = labelBounds; + } + } + + //get geo bounds for canvas + + if (unionBounds != null) { + Point2D coordTL = new Point2D.Double(); + coordTL.setLocation(unionBounds.getX(), unionBounds.getY()); + Point2D coordBR = new Point2D.Double(); + coordBR.setLocation(unionBounds.getX() + unionBounds.getWidth(), unionBounds.getY() + unionBounds.getHeight()); + + Point2D coordTR = new Point2D.Double(); + coordTR.setLocation(unionBounds.getX() + unionBounds.getWidth(), unionBounds.getY()); + Point2D coordBL = new Point2D.Double(); + coordBL.setLocation(unionBounds.getX(), unionBounds.getY() + unionBounds.getHeight()); + + south = new Point2D.Double(unionBounds.getX() + unionBounds.getWidth() / 2, unionBounds.getY() + unionBounds.getHeight()); + north = new Point2D.Double(unionBounds.getX() + unionBounds.getWidth() / 2, unionBounds.getY()); + east = new Point2D.Double(unionBounds.getX() + unionBounds.getWidth(), unionBounds.getY() + unionBounds.getHeight() / 2); + west = new Point2D.Double(unionBounds.getX(), unionBounds.getY() + unionBounds.getHeight() / 2); + + + geoCoordTL = ipc.PixelsToGeo(coordTL); + geoCoordBR = ipc.PixelsToGeo(coordBR); + geoCoordTR = ipc.PixelsToGeo(coordTR); + geoCoordBL = ipc.PixelsToGeo(coordBL); + + north = ipc.PixelsToGeo(north); + south = ipc.PixelsToGeo(south); + east = ipc.PixelsToGeo(east); + west = ipc.PixelsToGeo(west); + + + if (normalize) { + geoCoordTL = MultiPointHandler.NormalizeCoordToGECoord(geoCoordTL); + geoCoordBR = MultiPointHandler.NormalizeCoordToGECoord(geoCoordBR); + geoCoordTR = MultiPointHandler.NormalizeCoordToGECoord(geoCoordTR); + geoCoordBL = MultiPointHandler.NormalizeCoordToGECoord(geoCoordBL); + + north = MultiPointHandler.NormalizeCoordToGECoord(north); + south = MultiPointHandler.NormalizeCoordToGECoord(south); + east = MultiPointHandler.NormalizeCoordToGECoord(east); + west = MultiPointHandler.NormalizeCoordToGECoord(west); + } + } else//nothing to draw + { + geoCoordTL = new Point2D.Double(0, 0); + geoCoordBR = new Point2D.Double(0, 0); + geoCoordTR = new Point2D.Double(0, 0); + geoCoordBL = new Point2D.Double(0, 0); + + north = new Point2D.Double(0, 0); + south = new Point2D.Double(0, 0); + east = new Point2D.Double(0, 0); + west = new Point2D.Double(0, 0); + } + } catch (Exception err) { + ErrorLogger.LogException("MultiPointHandler", "GeoSVGize", err); + } + + if (paths != null && len > 0 && unionBounds != null) { + //create group with offset translation + //ctx.translate(bounds.getX() * -1, bounds.getY() * -1); + String group = ""; + + //loop through paths and labels and build SVG. + for (int i = 0; i < paths.size(); i++) { + group += paths.get(i); + } + + ArrayList labelStrs = renderTextElement(labels, textColor, textBackgroundColor); + for (int j = 0; j < labelStrs.size(); j++) { + group += labelStrs.get(j); + } + //close + group += ""; + + //wrap in SVG + String geoSVG = ""; + + geoSVG += ("\n"); + geoSVG += ("") + id + ("\n"); + geoSVG += ("") + name + ("\n"); + geoSVG += ("") + description + ("\n"); + geoSVG += ("") + symbolID + ("\n"); + geoSVG += ("") + geoCoordTL.getX() + " " + geoCoordTL.getY() + ("\n"); + geoSVG += ("") + geoCoordBR.getX() + " " + geoCoordBR.getY() + ("\n"); + geoSVG += ("") + geoCoordTR.getX() + " " + geoCoordTR.getY() + ("\n"); + geoSVG += ("") + geoCoordBL.getX() + " " + geoCoordBL.getY() + ("\n"); + geoSVG += ("") + north.getY() + ("\n"); + geoSVG += ("") + south.getY() + ("\n"); + geoSVG += ("") + east.getX() + ("\n"); + geoSVG += ("") + west.getX() + ("\n"); + geoSVG += ("") + wasClipped + ("\n"); + geoSVG += ("") + unionBounds.getWidth() + ("\n"); + geoSVG += ("") + unionBounds.getHeight() + ("\n"); + geoSVG += ("\n"); + + + /*//Scale the image, commented out as I decided to alter scale in getReasonableScale rather than adjust after the fact. + var tempWidth = Math.ceil(unionBounds.getWidth()); + var tempHeight = Math.ceil(unionBounds.getHeight()); + var quality = 1.0; + var bigger = Math.max(tempWidth, tempHeight); + var max = 1000; + if(!converter) + { + if(bigger < max) + { + if(bigger * 2 < max) + { + quality = 2; + } + else + { + quality = max / bigger; + } + } + else + { + quality = 1; + } + } + var geoSVG = '';//*/ + if (fillTexture != null) + geoSVG += fillTexture; + geoSVG += group; + geoSVG += "";//*/ + + return geoSVG; + + } else { + //return blank 2x2 SVG + return ""; + } + } + + /** + * @param tiArray {@link SVGTextInfo[]} + * @param color a hex string "#000000" + * @param outlineColor a hex string "#000000" + */ + static ArrayList renderTextElement(ArrayList tiArray, String color, String outlineColor) { + //ctx.lineCap = "butt"; + //ctx.lineJoin = "miter"; + //ctx.miterLimit = 3; + /*ctx.lineCap = "round"; + ctx.lineJoin = "round"; + ctx.miterLimit = 3;*/ + ArrayList svgElements = new ArrayList<>(); + + int size = tiArray.size(); + SVGTextInfo tempShape = null; + String textColor = "#000000"; + int tbm = RendererSettings.getInstance().getTextBackgroundMethod(); + int outlineWidth = RendererSettings.getInstance().getTextOutlineWidth(); + + if (color != null) { + textColor = color; + } + + + if (outlineColor == null || outlineColor.isEmpty()) { + outlineColor = SymbolUtilities.colorToHexString(SymbolDraw.getIdealTextBackgroundColor(SymbolUtilities.getColorFromHexString(textColor)), false); + } + + + if (tbm == RendererSettings.TextBackgroundMethod_OUTLINE + || tbm == RendererSettings.TextBackgroundMethod_OUTLINE_QUICK + || tbm == RendererSettings.TextBackgroundMethod_COLORFILL) { + for (int i = 0; i < size; i++) { + tempShape = tiArray.get(i); + svgElements.add(tempShape.toSVGElement(textColor, outlineColor, outlineWidth)); + } + } else //if(tbm == RendererSettings.TextBackgroundMethod_NONE) + { + for (int j = 0; j < size; j++) { + tempShape = tiArray.get(j); + svgElements.add(tempShape.toSVGElement(textColor, null, 0)); + } + } + + return svgElements; + } + + /** + * + * @param shapeInfo + * @return + */ + static String getFillPattern(ShapeInfo shapeInfo) + { + if (shapeInfo.getPatternFillImage() != null) { + int width = shapeInfo.getPatternFillImage().getWidth(); + int height = shapeInfo.getPatternFillImage().getHeight(); + return ""; + } else { + return null; + } + + } + + /** + * @param symbolID + * @param shapeInfo {@link ShapeInfo} + * @param pathBounds {@link Rectangle} + * @param ipc {@link IPointConversion} + * @param normalize + */ + static String ShapesToGeoSVG(String symbolID, ShapeInfo shapeInfo, Rectangle pathBounds, IPointConversion ipc, boolean normalize) { + Path path = null; + String fillColor = null; + String lineColor = null; + int lineWidth = 0; + double lineAlpha = 1.0; + double fillAlpha = 1.0; + float[] dashArray = null; + String fillPattern = null; + + if (shapeInfo.getLineColor() != null) { + Color lineColorTemp = shapeInfo.getLineColor(); + lineAlpha = lineColorTemp.getAlpha() / 255.0; + lineColor = SymbolUtilities.colorToHexString(lineColorTemp, false); + } + if (shapeInfo.getFillColor() != null) { + Color fillColorTemp = shapeInfo.getFillColor(); + fillAlpha = fillColorTemp.getAlpha() / 255.0; + fillColor = SymbolUtilities.colorToHexString(fillColorTemp, false); + } + + BasicStroke stroke = (BasicStroke)shapeInfo.getStroke(); + if (stroke != null) { + lineWidth = Math.round(stroke.getLineWidth()); + dashArray = stroke.getDashArray(); + } + + ArrayList> shapesArray = shapeInfo.getPolylines(); + path = new Path(); + if (dashArray != null && dashArray.length > 0) + path.setLineDash(dashArray); + for (int i = 0; i < shapesArray.size(); i++) { + ArrayList shape = shapesArray.get(i); + + for (int j = 0; j < shape.size(); j++) { + Point2D coord = shape.get(j); + if (j == 0) { + path.moveTo(coord.getX(), coord.getY()); + } else if (dashArray != null) { + path.dashedLineTo(coord.getX(), coord.getY()); + } else { + path.lineTo(coord.getX(), coord.getY()); + } + } + } + + if (shapeInfo.getPatternFillImage() != null) + fillColor = "url(#fillPattern)"; + + String svgElement = path.toSVGElement(lineColor, lineWidth, fillColor, lineAlpha, fillAlpha); + pathBounds.setBounds(path.getBounds().getBounds()); + return svgElement; + } + + public static String imgToBase64String(BufferedImage img) + { + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + + try + { + // Base64 index table + final char[] BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); + byte data[] = null; + if(img != null) + { + ImageIO.write(img, "png", os); + data = os.toByteArray(); + } + if(data != null) + { + StringBuilder encoded = new StringBuilder((data.length * 4 + 2) / 3); + + int i = 0; + while (i < data.length) { + // Take 3 bytes (24 bits) at a time + int b1 = data[i++] & 0xFF; + int b2 = (i < data.length) ? data[i++] & 0xFF : 0; + int b3 = (i < data.length) ? data[i++] & 0xFF : 0; + + // Break into four 6-bit chunks + int chunk1 = b1 >>> 2; + int chunk2 = ((b1 & 0x03) << 4) | (b2 >>> 4); + int chunk3 = ((b2 & 0x0F) << 2) | (b3 >>> 6); + int chunk4 = b3 & 0x3F; + + // Append Base64 characters + encoded.append(BASE64_CHARS[chunk1]); + encoded.append(BASE64_CHARS[chunk2]); + encoded.append(i - 1 < data.length ? BASE64_CHARS[chunk3] : '='); + encoded.append(i < data.length ? BASE64_CHARS[chunk4] : '='); + } + return "data:image/png;base64," + encoded.toString(); + } + return "data:image/png;base64,"; + + } + catch(IOException ioe) + { + ErrorLogger.LogException("MultiPointHandlerSVG", "imgToBase64String", ioe); + return "data:image/png;base64,"; + } + catch(Exception exc) + { + ErrorLogger.LogException("MultiPointHandlerSVG", "imgToBase64String", exc); + return "data:image/png;base64,"; + } + } +} diff --git a/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/SECRenderer.java b/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/SECRenderer.java index 5de2b60e..32ae61c5 100644 --- a/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/SECRenderer.java +++ b/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/SECRenderer.java @@ -1187,11 +1187,11 @@ public MilStdSymbol RenderMultiPointAsMilStdSymbol(String id, String name, Strin MilStdSymbol mSymbol = null; try { - if (JavaRendererUtilities.is3dSymbol(symbolCode, modifiers)==false) - { + //if (JavaRendererUtilities.is3dSymbol(symbolCode, modifiers)==false) + //{ mSymbol = MultiPointHandler.RenderSymbolAsMilStdSymbol(id, name, description, symbolCode, controlPoints, scale, bbox, modifiers, symStd); - } + //} } catch (Exception ea) { mSymbol=null; diff --git a/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/utilities/Path.java b/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/utilities/Path.java new file mode 100644 index 00000000..d849f760 --- /dev/null +++ b/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/utilities/Path.java @@ -0,0 +1,171 @@ +/* + * Copyright 2025 Michael.P.Spinelli. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package sec.web.renderer.utilities; + +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; + +/** + * + */ +public class Path { + static class ActionTypes { + public static final int ACTION_MOVE_TO = 0; + public static final int ACTION_LINE_TO = 1; + public static final int ACTION_DASHED_LINE_TO = 6; + } + + static class Action { + int actionType; + double x, y; + + public Action(int actionType, double x, double y) { + this.actionType = actionType; + this.x = x; + this.y = y; + } + } + + private ArrayList _actions = new ArrayList<>(); + private String _dashArray = null; + private Rectangle2D _rectangle = new Rectangle2D.Double(); + + + public void setLineDash(float[] dashArray) { + this._dashArray = dashArray.toString()//.toString(dashArray) + .replace(",", "") + .replace("[", "") + .replace("]", "") + .trim(); + } + + + public Rectangle2D getBounds() { + return this._rectangle; + } + + + /** + * Adds a point to the path by moving to the specified coordinates specified + * + * @param x + * @param y + */ + public void moveTo(double x, double y) { + + if (this._actions.size() == 0) { + this._rectangle = new Rectangle2D.Double(x, y, 1, 1); + } + this._rectangle.add(x, y); + this._actions.add(new Action(ActionTypes.ACTION_MOVE_TO, x, y)); + } + + /** + * Adds a point to the path by drawing a straight line from the current + * coordinates to the new specified coordinates specified + * + * @param x + * @param y + */ + public void lineTo(double x, double y) { + + if (this._actions.size() == 0) { + this.moveTo(0, 0); + } + this._actions.add(new Action(ActionTypes.ACTION_LINE_TO, x, y)); + this._rectangle.add(x, y); + } + + /** + * Adds a point to the path by drawing a straight line from the current + * coordinates to the new specified coordinates specified + * + * @param x + * @param y + */ + public void dashedLineTo(double x, double y) { + if (this._actions.size() == 0) { + this.moveTo(0, 0); + } + this._actions.add(new Action(ActionTypes.ACTION_DASHED_LINE_TO, x, y)); + this._rectangle.add(x, y); + } + + + public String toSVGElement(String stroke, int strokeWidth, String fill, double strokeOpacity, double fillOpacity) { + //context.beginPath(); + int size = this._actions.size(); + Action temp = null; + String path = ""; + + for (int i = 0; i < size; i++) { + temp = this._actions.get(i); + + if (temp.actionType == ActionTypes.ACTION_LINE_TO) { + path += "L" + temp.x + " " + temp.y; + } else if (temp.actionType == ActionTypes.ACTION_MOVE_TO) { + path += "M" + temp.x + " " + temp.y; + } else if (temp.actionType == ActionTypes.ACTION_DASHED_LINE_TO) { + path += "L" + temp.x + " " + temp.y; + + } + + } + + String line = " 0) { + + seStroke = se + " stroke=\"" + stroke + '"'; + /*else + seStroke = se + ' stroke="' + stroke.replace(/#/g,"#") + '"';*/ + + if (strokeWidth != 0) + seStroke += " stroke-width=\"" + (strokeWidth + 2) + '"'; + seStroke += " fill=\"none\""; + seStroke += ">"; + seStroke += text; + seStroke += ""; + } + + if (fill != null) { + + seFill = se + " fill=\"" + fill + '"'; + /*else + seFill = se + ' fill="' + fill.replace(/#/g,"%23") + '"';*/ + seFill += ">"; + seFill += text; + seFill += ""; + } + + if (stroke != null && !stroke.isEmpty() && fill != null && !fill.isEmpty()) + se = seStroke + seFill; + else if (fill != null && !fill.isEmpty()) + se = seFill; + else + se = ""; + return se; + } + + public static Rectangle2D getRotatedRectangleBounds(Rectangle2D rectangle, Point2D pivotPt, double angle, String justification) { + double textWidth = rectangle.getWidth(); + + if (justification.equals("start")) + rectangle.setRect(rectangle.getX() - textWidth / 2, rectangle.getY(), rectangle.getWidth(), rectangle.getHeight()); + else if (justification.equals("end")) + rectangle.setRect(rectangle.getX() + textWidth / 2, rectangle.getY(), rectangle.getWidth(), rectangle.getHeight()); + + Point2D ptTL = new Point2D.Double(rectangle.getMinX(), rectangle.getMinY()); + Point2D ptTR = new Point2D.Double(rectangle.getMaxX(), rectangle.getMinY()); + Point2D ptBL = new Point2D.Double(rectangle.getMinX(), rectangle.getMaxY()); + Point2D ptBR = new Point2D.Double(rectangle.getMaxX(), rectangle.getMaxY()); + + SVGTextInfo.rotatePoint(ptTL, pivotPt, angle); + SVGTextInfo.rotatePoint(ptTR, pivotPt, angle); + SVGTextInfo.rotatePoint(ptBL, pivotPt, angle); + SVGTextInfo.rotatePoint(ptBR, pivotPt, angle); + + rectangle = new Rectangle2D.Double(ptTL.getX(), ptTL.getY(), 0, 0); + rectangle.add(ptTR.getX(), ptTR.getY()); + rectangle.add(ptBL.getX(), ptBL.getY()); + rectangle.add(ptBR.getX(), ptBR.getY()); + + if (justification == "start") { + double s = Math.sin(angle * 2 * Math.PI / 360); + double c = Math.cos(angle * 2 * Math.PI / 360); + rectangle.setRect(rectangle.getX() + textWidth / 2 * c, rectangle.getY() + textWidth / 2 * s, rectangle.getWidth(), rectangle.getHeight()); + } else if (justification == "end") { + double s = Math.sin(angle * 2 * Math.PI / 360); + double c = Math.cos(angle * 2 * Math.PI / 360); + rectangle.setRect(rectangle.getX() - textWidth / 2 * c, rectangle.getY() - textWidth / 2 * s, rectangle.getWidth(), rectangle.getHeight()); + + } + + return rectangle; + } + + static void rotatePoint(Point2D pt, Point2D pivotPt, double angle) { + final double s = Math.sin(-angle * 2 * Math.PI / 360); + final double c = Math.cos(-angle * 2 * Math.PI / 360); + + pt.setLocation(pt.getX() - pivotPt.getX(), pt.getY() - pivotPt.getY()); + + double xnew = pt.getX() * c - pt.getY() * s; + double ynew = pt.getX() * s + pt.getY() * c; + + pt.setLocation(xnew + pivotPt.getX(), ynew + pivotPt.getY()); + + } + +} diff --git a/renderer/pom.xml b/renderer/pom.xml index b53639bc..bc49aba7 100644 --- a/renderer/pom.xml +++ b/renderer/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.45 .. diff --git a/samples/pom.xml b/samples/pom.xml index cc7bfc79..52cc9c2a 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.45 .. diff --git a/samples/rendering-sample-1/pom.xml b/samples/rendering-sample-1/pom.xml index 1e1dd297..708e3f5c 100644 --- a/samples/rendering-sample-1/pom.xml +++ b/samples/rendering-sample-1/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.45 ../.. diff --git a/service/mil-sym-service/pom.xml b/service/mil-sym-service/pom.xml index 21dcb598..0c50f93d 100644 --- a/service/mil-sym-service/pom.xml +++ b/service/mil-sym-service/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.45 ../.. diff --git a/service/pom.xml b/service/pom.xml index 55509f2e..2368bc9b 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.45 ..