From 9d22a7a3b1b941972c2b496631db55487a27091b Mon Sep 17 00:00:00 2001 From: Michael Spinelli Date: Wed, 24 Sep 2025 08:58:42 -0400 Subject: [PATCH 1/9] Update README.md --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index 6afd9b9..0073c9f 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ mil-sym-java is a well worn set of java libraries that have been used in US Army [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) @@ -42,12 +43,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 From 244e8d570e003595a0b4e322d9d88560b77a057c Mon Sep 17 00:00:00 2001 From: Michael Spinelli Date: Fri, 26 Sep 2025 10:36:58 -0400 Subject: [PATCH 2/9] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0073c9f..6437c78 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,10 @@ See new projects for 2525D+ rendering: [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. From 5a531561ea2135b83585538dfcbaa4a20a235164 Mon Sep 17 00:00:00 2001 From: Michael Spinelli Date: Fri, 26 Sep 2025 10:37:12 -0400 Subject: [PATCH 3/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6437c78..b9e3fea 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ See new projects for 2525D+ rendering: [Android](https://github.com/missioncommand/mil-sym-android) [TypeScript](https://github.com/missioncommand/mil-sym-ts) -Other 2525C releases +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) From 3125bf351e35ad480618353e821656220608de66 Mon Sep 17 00:00:00 2001 From: Michael Spinelli Date: Fri, 26 Sep 2025 10:37:40 -0400 Subject: [PATCH 4/9] Update README.md From fda9f50b321336d01817193c5f3b95c1f7f8c7a8 Mon Sep 17 00:00:00 2001 From: Michael Spinelli Date: Thu, 4 Dec 2025 12:22:37 -0500 Subject: [PATCH 5/9] Remove Google Group link from README Removed link to Google Group Discussion Forum. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index b9e3fea..1af6fd4 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ 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: From 3cf65b9b326c09e71d397091426d5e44202141ec Mon Sep 17 00:00:00 2001 From: "C5ISR\\Michael.P.Spinelli" Date: Mon, 22 Dec 2025 11:33:48 -0500 Subject: [PATCH 6/9] SVG output implemented. issues with svg pattern fills. --- .../ArmyC2/C2SD/Utilities/RectUtilities.java | 108 ++++ .../java/ArmyC2/C2SD/Utilities/ShapeInfo.java | 8 + .../ArmyC2/C2SD/Utilities/SymbolDraw.java | 12 +- .../sec/web/renderer/MultiPointHandler.java | 37 +- .../web/renderer/MultiPointHandlerSVG.java | 503 ++++++++++++++++++ .../java/sec/web/renderer/utilities/Path.java | 171 ++++++ .../web/renderer/utilities/SVGTextInfo.java | 195 +++++++ 7 files changed, 1025 insertions(+), 9 deletions(-) create mode 100644 core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/RectUtilities.java create mode 100644 renderer/mil-sym-renderer/src/main/java/sec/web/renderer/MultiPointHandlerSVG.java create mode 100644 renderer/mil-sym-renderer/src/main/java/sec/web/renderer/utilities/Path.java create mode 100644 renderer/mil-sym-renderer/src/main/java/sec/web/renderer/utilities/SVGTextInfo.java 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 0000000..5a63f89 --- /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/ShapeInfo.java b/core/JavaRendererUtils/src/main/java/ArmyC2/C2SD/Utilities/ShapeInfo.java index 17c3ce7..89fd13a 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 f3fdb64..a836880 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/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 0e311df..457fc3a 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(); @@ -841,7 +841,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); @@ -988,6 +995,14 @@ public static String RenderSymbol(String id, 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); @@ -1766,7 +1781,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); @@ -1897,6 +1920,14 @@ public static String RenderSymbol2D(String id, 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(); 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 0000000..d05d1ed --- /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/utilities/Path.java b/renderer/mil-sym-renderer/src/main/java/sec/web/renderer/utilities/Path.java new file mode 100644 index 0000000..d849f76 --- /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()); + + } + +} From 0710660d49983edc7f2f6b6ed24c3e00efb96116 Mon Sep 17 00:00:00 2001 From: "C5ISR\\Michael.P.Spinelli" Date: Thu, 8 Jan 2026 11:45:06 -0500 Subject: [PATCH 7/9] -version bump to 0.1.44 -added isTextScaleSensitive and isSymbolScaleSensitive -fixed a few null string bugs --- core/JavaLineArray/pom.xml | 2 +- .../java/JavaTacticalRenderer/TGLight.java | 93 +++--- .../java/JavaTacticalRenderer/clsMETOC.java | 2 +- core/JavaRenderer/pom.xml | 2 +- core/JavaRendererServer/pom.xml | 2 +- .../RenderMultipoints/clsUtilityCPOF.java | 1 + core/JavaRendererUtils/pom.xml | 2 +- .../ArmyC2/C2SD/Utilities/MilStdSymbol.java | 295 ++++++++++++++++++ core/pom.xml | 2 +- pom.xml | 2 +- renderer/RendererPluginInterface/pom.xml | 2 +- renderer/mil-sym-renderer/pom.xml | 2 +- renderer/mil-sym-renderer/pomJava7.xml | 2 +- renderer/mil-sym-renderer/pomJava8.xml | 2 +- .../sec/web/renderer/MultiPointHandler.java | 20 +- renderer/pom.xml | 2 +- samples/pom.xml | 2 +- samples/rendering-sample-1/pom.xml | 2 +- service/mil-sym-service/pom.xml | 2 +- service/pom.xml | 2 +- 20 files changed, 376 insertions(+), 65 deletions(-) diff --git a/core/JavaLineArray/pom.xml b/core/JavaLineArray/pom.xml index bc15da9..5864234 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.44 ../.. diff --git a/core/JavaLineArray/src/main/java/JavaTacticalRenderer/TGLight.java b/core/JavaLineArray/src/main/java/JavaTacticalRenderer/TGLight.java index d40e468..f422ed0 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 3c1f59e..21af6ca 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 98d97a3..cffa3dd 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.44 ../.. diff --git a/core/JavaRendererServer/pom.xml b/core/JavaRendererServer/pom.xml index 0bedd54..c45065e 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.44 ../.. diff --git a/core/JavaRendererServer/src/main/java/RenderMultipoints/clsUtilityCPOF.java b/core/JavaRendererServer/src/main/java/RenderMultipoints/clsUtilityCPOF.java index 0241876..66dd6a4 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 fc6aab6..493a273 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.44 ../.. 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 eadb1f5..9025cae 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/pom.xml b/core/pom.xml index 9acbeaa..059cc18 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.44 .. diff --git a/pom.xml b/pom.xml index 139785b..2225ee9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.44 pom mil-sym-java diff --git a/renderer/RendererPluginInterface/pom.xml b/renderer/RendererPluginInterface/pom.xml index a8f00a6..68c7460 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.44 ../.. diff --git a/renderer/mil-sym-renderer/pom.xml b/renderer/mil-sym-renderer/pom.xml index 05581cd..d6c05c2 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.44 ../.. diff --git a/renderer/mil-sym-renderer/pomJava7.xml b/renderer/mil-sym-renderer/pomJava7.xml index 16fddc3..599404d 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.44 mil-sym-renderer http://maven.apache.org diff --git a/renderer/mil-sym-renderer/pomJava8.xml b/renderer/mil-sym-renderer/pomJava8.xml index 52c75a0..9f3a43a 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.44 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 457fc3a..82dd7a5 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 @@ -929,7 +929,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////////// @@ -992,6 +992,10 @@ 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("\"}}"); } @@ -1880,7 +1884,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////////// @@ -1917,6 +1921,10 @@ 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("\"}}"); } @@ -2234,7 +2242,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); } @@ -3087,7 +3095,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(); @@ -3104,6 +3114,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/pom.xml b/renderer/pom.xml index b53639b..54b3d17 100644 --- a/renderer/pom.xml +++ b/renderer/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.44 .. diff --git a/samples/pom.xml b/samples/pom.xml index cc7bfc7..a441cde 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.44 .. diff --git a/samples/rendering-sample-1/pom.xml b/samples/rendering-sample-1/pom.xml index 1e1dd29..e2a345a 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.44 ../.. diff --git a/service/mil-sym-service/pom.xml b/service/mil-sym-service/pom.xml index 21dcb59..3f34678 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.44 ../.. diff --git a/service/pom.xml b/service/pom.xml index 55509f2..b1ec768 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.43 + 0.1.44 .. From ed1a23fcba938de5f60f758a15aa07f124b400c6 Mon Sep 17 00:00:00 2001 From: "C5ISR\\Michael.P.Spinelli" Date: Wed, 4 Feb 2026 10:46:03 -0500 Subject: [PATCH 8/9] fixed bug where SECRenderer.RenderAsMilStdSymbol() would return null for some symbols with altitude values. Did not impact RenderSymbol or RenderSymbol2D() --- core/JavaLineArray/pom.xml | 2 +- core/JavaRenderer/pom.xml | 2 +- core/JavaRendererServer/pom.xml | 2 +- core/JavaRendererUtils/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 2 +- renderer/RendererPluginInterface/pom.xml | 2 +- renderer/mil-sym-renderer/pom.xml | 2 +- renderer/mil-sym-renderer/pomJava7.xml | 2 +- renderer/mil-sym-renderer/pomJava8.xml | 2 +- .../src/main/java/sec/web/renderer/SECRenderer.java | 6 +++--- renderer/pom.xml | 2 +- samples/pom.xml | 2 +- samples/rendering-sample-1/pom.xml | 2 +- service/mil-sym-service/pom.xml | 2 +- service/pom.xml | 2 +- 16 files changed, 18 insertions(+), 18 deletions(-) diff --git a/core/JavaLineArray/pom.xml b/core/JavaLineArray/pom.xml index 5864234..f3d6c29 100644 --- a/core/JavaLineArray/pom.xml +++ b/core/JavaLineArray/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.44 + 0.1.45 ../.. diff --git a/core/JavaRenderer/pom.xml b/core/JavaRenderer/pom.xml index cffa3dd..dfc8dbc 100644 --- a/core/JavaRenderer/pom.xml +++ b/core/JavaRenderer/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.44 + 0.1.45 ../.. diff --git a/core/JavaRendererServer/pom.xml b/core/JavaRendererServer/pom.xml index c45065e..ce134c8 100644 --- a/core/JavaRendererServer/pom.xml +++ b/core/JavaRendererServer/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.44 + 0.1.45 ../.. diff --git a/core/JavaRendererUtils/pom.xml b/core/JavaRendererUtils/pom.xml index 493a273..c4b197c 100644 --- a/core/JavaRendererUtils/pom.xml +++ b/core/JavaRendererUtils/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.44 + 0.1.45 ../.. diff --git a/core/pom.xml b/core/pom.xml index 059cc18..c991dcf 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.44 + 0.1.45 .. diff --git a/pom.xml b/pom.xml index 2225ee9..088edd5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.missioncommand mil-sym-java - 0.1.44 + 0.1.45 pom mil-sym-java diff --git a/renderer/RendererPluginInterface/pom.xml b/renderer/RendererPluginInterface/pom.xml index 68c7460..b3eafe2 100644 --- a/renderer/RendererPluginInterface/pom.xml +++ b/renderer/RendererPluginInterface/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.44 + 0.1.45 ../.. diff --git a/renderer/mil-sym-renderer/pom.xml b/renderer/mil-sym-renderer/pom.xml index d6c05c2..82f2190 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.44 + 0.1.45 ../.. diff --git a/renderer/mil-sym-renderer/pomJava7.xml b/renderer/mil-sym-renderer/pomJava7.xml index 599404d..9e634be 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.44 + 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 9f3a43a..8a30987 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.44 + 0.1.45 mil-sym-renderer http://maven.apache.org 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 5de2b60..32ae61c 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/pom.xml b/renderer/pom.xml index 54b3d17..bc49aba 100644 --- a/renderer/pom.xml +++ b/renderer/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.44 + 0.1.45 .. diff --git a/samples/pom.xml b/samples/pom.xml index a441cde..52cc9c2 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.44 + 0.1.45 .. diff --git a/samples/rendering-sample-1/pom.xml b/samples/rendering-sample-1/pom.xml index e2a345a..708e3f5 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.44 + 0.1.45 ../.. diff --git a/service/mil-sym-service/pom.xml b/service/mil-sym-service/pom.xml index 3f34678..0c50f93 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.44 + 0.1.45 ../.. diff --git a/service/pom.xml b/service/pom.xml index b1ec768..2368bc9 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -4,7 +4,7 @@ io.github.missioncommand mil-sym-java - 0.1.44 + 0.1.45 .. From b5c91f6a03032d6dcc4593566b6ad715d9d64082 Mon Sep 17 00:00:00 2001 From: "C5ISR\\Michael.P.Spinelli" Date: Wed, 11 Feb 2026 14:44:37 -0500 Subject: [PATCH 9/9] -added get/setAutoAdjustScale to RendererSettings -updated shouldClipSymbol in MultiPointHandler --- .../C2SD/Utilities/RendererSettings.java | 15 +++ .../sec/web/renderer/MultiPointHandler.java | 124 ++++++++++++------ 2 files changed, 97 insertions(+), 42 deletions(-) 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 a33b624..fb9f5f4 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/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 82dd7a5..59fa59b 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 @@ -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); @@ -857,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); @@ -1481,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); @@ -1510,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) @@ -1821,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();