001package sec.web.render; 002 003import java.util.ArrayList; 004 005import armyc2.c2sd.graphics2d.BasicStroke; 006import armyc2.c2sd.graphics2d.Point2D; 007import armyc2.c2sd.graphics2d.Rectangle; 008import armyc2.c2sd.graphics2d.Rectangle2D; 009import armyc2.c2sd.renderer.utilities.Color; 010import armyc2.c2sd.renderer.utilities.ErrorLogger; 011import armyc2.c2sd.renderer.utilities.IPointConversion; 012import armyc2.c2sd.renderer.utilities.RendererSettings; 013import armyc2.c2sd.renderer.utilities.RendererUtilities; 014import armyc2.c2sd.renderer.utilities.ShapeInfo; 015import armyc2.c2sd.renderer.utilities.SymbolUtilities; 016import sec.web.render.utilities.Path; 017import sec.web.render.utilities.SVGTextInfo; 018 019public class MultiPointHandlerSVG { 020 public static String GeoSVGize(String id, String name, String description, String symbolID, ArrayList<ShapeInfo> shapes, ArrayList<ShapeInfo> modifiers, IPointConversion ipc, boolean normalize, String textColor, String textBackgroundColor, boolean wasClipped) { 021 return GeoSVGize(id, name, description, symbolID, shapes, modifiers, ipc, normalize, textColor, textBackgroundColor, wasClipped, null); 022 } 023 024 /** 025 * Generates an SVG which can be draped on a map. 026 * Better with RenderSymbol2D 027 * 028 * @param id 029 * @param name 030 * @param description 031 * @param symbolID 032 * @param shapes {@link ShapeInfo[]} 033 * @param modifiers {@link ShapeInfo[]} 034 * @param ipc {@link IPointConversion} 035 * @param normalize 036 * @param textColor 037 * @param textBackgroundColor 038 * @param wasClipped 039 * @return 040 */ 041 public static String GeoSVGize(String id, String name, String description, String symbolID, ArrayList<ShapeInfo> shapes, ArrayList<ShapeInfo> modifiers, IPointConversion ipc, boolean normalize, String textColor, String textBackgroundColor, boolean wasClipped, Rectangle bbox) { 042 043 int height = 10; 044 045 Rectangle tempBounds = null; 046 ArrayList<String> paths = new ArrayList<>(); 047 Rectangle pathBounds = null; 048 ArrayList<SVGTextInfo> labels = new ArrayList<>(); 049 Rectangle labelBounds = null; 050 Rectangle unionBounds = null; 051 float lineWidth; 052 String fillTexture = null; 053 Point2D geoCoordTL = null; 054 Point2D geoCoordTR = null; 055 Point2D geoCoordBL = null; 056 Point2D geoCoordBR = null; 057 Point2D west = null; 058 Point2D north = null; 059 Point2D south = null; 060 Point2D east = null; 061 int len = shapes.size(); 062 063 try { 064 height = RendererSettings.getInstance().getMPModifierFontSize(); 065 066 for (int i = 0; i < len; i++) { 067 tempBounds = new Rectangle(); 068 String svg = MultiPointHandlerSVG.ShapesToGeoSVG(symbolID, shapes.get(i), tempBounds, ipc, normalize); 069 if (svg != null) { 070 BasicStroke bsTemp = (BasicStroke)shapes.get(i).getStroke(); 071 lineWidth = bsTemp.getLineWidth(); 072 tempBounds.grow(Math.round(lineWidth), Math.round(lineWidth));//adjust for line width so nothing gets clipped. 073 if (pathBounds == null) 074 pathBounds = new Rectangle(tempBounds); 075 else 076 pathBounds = pathBounds.union(tempBounds); 077 paths.add(svg); 078 079 if (shapes.get(i).getPatternFillImage() != null && fillTexture == null) 080 fillTexture = getFillPattern(shapes.get(i)); 081 } 082 } 083 084 ShapeInfo tempModifier; 085 int len2 = modifiers.size(); 086 SVGTextInfo tiTemp = null; 087 for (int j = 0; j < len2; j++) { 088 tempModifier = modifiers.get(j); 089 090 if (tempModifier.getModifierString() != null && !tempModifier.getModifierString().isEmpty()) { 091 Point2D tempLocation = tempModifier.getModifierStringPosition(); 092 093 int justify = tempModifier.getTextJustify(); 094 String strJustify = "start"; 095 if (justify == ShapeInfo.justify_left) 096 strJustify = "start"; 097 else if (justify == ShapeInfo.justify_center) 098 strJustify = "middle"; 099 else if (justify == ShapeInfo.justify_right) 100 strJustify = "end"; 101 102 double degrees = tempModifier.getModifierStringAngle(); 103 tiTemp = new SVGTextInfo(tempModifier.getModifierString(), tempLocation, strJustify, degrees); 104 105 Rectangle bounds = tiTemp.getTextBounds().getBounds(); 106 107 //make sure labels are in the bbox, otherwise they can 108 //make the canvas grow out of control. 109 //if (tiTemp && bbox.containsRectangle(bounds)) 110 //if(bbox !== null) 111 if (tiTemp != null) { 112 if ((bbox != null && bbox.intersects(bounds)) || bbox == null) { 113 labels.add(tiTemp); 114 if (bounds != null) { 115 if (labelBounds != null) 116 labelBounds = labelBounds.union(bounds); 117 else 118 labelBounds = bounds; 119 } 120 } 121 } 122 } 123 /*else if (tempModifier.getModifierImage() != null) { 124 Bitmap imgModifier = tempModifier.getModifierImage(); 125 Rectangle bounds = new Rectangle(0, 0, imgModifier.getWidth(), imgModifier.getHeight()); 126 127 Point2D tempLocation = tempModifier.getModifierPosition(); 128 tempLocation.setLocation(tempLocation.getX() - bounds.getWidth() / 2, tempLocation.getY() - bounds.getHeight() / 2); 129 int x = (int) tempLocation.getX(); 130 int y = (int) tempLocation.getY(); 131 bounds.setLocation(x, y); 132 133 double angle = tempModifier.getModifierAngle(); 134 paths.add("<image transform=\"translate(" + x + ',' + y + ") rotate(" + angle + ")\" href=\"" + MultiPointHandler.bitmapToString(tempModifier.getModifierImage()) + "\" />"); 135 if (angle != 0) { 136 bounds = SVGTextInfo.getRotatedRectangleBounds(bounds, tempLocation, -angle, "middle"); 137 } 138 if (bounds != null) { 139 if ((bbox != null && bbox.intersects(bounds)) || bbox == null) { 140 if (pathBounds != null) 141 pathBounds = pathBounds.union(bounds); 142 else 143 pathBounds = bounds; 144 } 145 } 146 }//*/ 147 } 148 if (pathBounds != null) { 149 unionBounds = new Rectangle(pathBounds); 150 } 151 if (labelBounds != null) { 152 if (unionBounds != null) { 153 unionBounds = unionBounds.union(labelBounds); 154 } else { 155 unionBounds = labelBounds; 156 } 157 } 158 159 //get geo bounds for canvas 160 161 if (unionBounds != null) { 162 Point2D coordTL = new Point2D.Double(); 163 coordTL.setLocation(unionBounds.getX(), unionBounds.getY()); 164 Point2D coordBR = new Point2D.Double(); 165 coordBR.setLocation(unionBounds.getX() + unionBounds.getWidth(), unionBounds.getY() + unionBounds.getHeight()); 166 167 Point2D coordTR = new Point2D.Double(); 168 coordTR.setLocation(unionBounds.getX() + unionBounds.getWidth(), unionBounds.getY()); 169 Point2D coordBL = new Point2D.Double(); 170 coordBL.setLocation(unionBounds.getX(), unionBounds.getY() + unionBounds.getHeight()); 171 172 south = new Point2D.Double(unionBounds.getX() + unionBounds.getWidth() / 2, unionBounds.getY() + unionBounds.getHeight()); 173 north = new Point2D.Double(unionBounds.getX() + unionBounds.getWidth() / 2, unionBounds.getY()); 174 east = new Point2D.Double(unionBounds.getX() + unionBounds.getWidth(), unionBounds.getY() + unionBounds.getHeight() / 2); 175 west = new Point2D.Double(unionBounds.getX(), unionBounds.getY() + unionBounds.getHeight() / 2); 176 177 178 geoCoordTL = ipc.PixelsToGeo(coordTL); 179 geoCoordBR = ipc.PixelsToGeo(coordBR); 180 geoCoordTR = ipc.PixelsToGeo(coordTR); 181 geoCoordBL = ipc.PixelsToGeo(coordBL); 182 183 north = ipc.PixelsToGeo(north); 184 south = ipc.PixelsToGeo(south); 185 east = ipc.PixelsToGeo(east); 186 west = ipc.PixelsToGeo(west); 187 188 189 if (normalize) { 190 geoCoordTL = MultiPointHandler.NormalizeCoordToGECoord(geoCoordTL); 191 geoCoordBR = MultiPointHandler.NormalizeCoordToGECoord(geoCoordBR); 192 geoCoordTR = MultiPointHandler.NormalizeCoordToGECoord(geoCoordTR); 193 geoCoordBL = MultiPointHandler.NormalizeCoordToGECoord(geoCoordBL); 194 195 north = MultiPointHandler.NormalizeCoordToGECoord(north); 196 south = MultiPointHandler.NormalizeCoordToGECoord(south); 197 east = MultiPointHandler.NormalizeCoordToGECoord(east); 198 west = MultiPointHandler.NormalizeCoordToGECoord(west); 199 } 200 } else//nothing to draw 201 { 202 geoCoordTL = new Point2D.Double(0, 0); 203 geoCoordBR = new Point2D.Double(0, 0); 204 geoCoordTR = new Point2D.Double(0, 0); 205 geoCoordBL = new Point2D.Double(0, 0); 206 207 north = new Point2D.Double(0, 0); 208 south = new Point2D.Double(0, 0); 209 east = new Point2D.Double(0, 0); 210 west = new Point2D.Double(0, 0); 211 } 212 } catch (Exception err) { 213 ErrorLogger.LogException("MultiPointHandlerSVG", "GeoSVGize", err); 214 } 215 216 if (paths != null && len > 0 && unionBounds != null) { 217 //create group with offset translation 218 //ctx.translate(bounds.getX() * -1, bounds.getY() * -1); 219 String group = "<g transform=\"translate(" + (unionBounds.getX() * -1) + ',' + (unionBounds.getY() * -1) + ")\">"; 220 221 //loop through paths and labels and build SVG. 222 for (int i = 0; i < paths.size(); i++) { 223 group += paths.get(i); 224 } 225 226 ArrayList<String> labelStrs = renderTextElement(labels, textColor, textBackgroundColor); 227 for (int j = 0; j < labelStrs.size(); j++) { 228 group += labelStrs.get(j); 229 } 230 //close 231 group += "</g>"; 232 233 //wrap in SVG 234 String geoSVG = "<svg width=\"" + Math.ceil(unionBounds.getWidth()) + "px\" height=\"" + Math.ceil(unionBounds.getHeight()) + "px\" preserveAspectRatio=\"none\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"; 235 236 geoSVG += ("<metadata>\n"); 237 geoSVG += ("<id>") + id + ("</id>\n"); 238 geoSVG += ("<name>") + name + ("</name>\n"); 239 geoSVG += ("<description>") + description + ("</description>\n"); 240 geoSVG += ("<symbolID>") + symbolID + ("</symbolID>\n"); 241 geoSVG += ("<geoTL>") + geoCoordTL.getX() + " " + geoCoordTL.getY() + ("</geoTL>\n"); 242 geoSVG += ("<geoBR>") + geoCoordBR.getX() + " " + geoCoordBR.getY() + ("</geoBR>\n"); 243 geoSVG += ("<geoTR>") + geoCoordTR.getX() + " " + geoCoordTR.getY() + ("</geoTR>\n"); 244 geoSVG += ("<geoBL>") + geoCoordBL.getX() + " " + geoCoordBL.getY() + ("</geoBL>\n"); 245 geoSVG += ("<north>") + north.getY() + ("</north>\n"); 246 geoSVG += ("<south>") + south.getY() + ("</south>\n"); 247 geoSVG += ("<east>") + east.getX() + ("</east>\n"); 248 geoSVG += ("<west>") + west.getX() + ("</west>\n"); 249 geoSVG += ("<wasClipped>") + wasClipped + ("</wasClipped>\n"); 250 geoSVG += ("<width>") + unionBounds.getWidth() + ("</width>\n"); 251 geoSVG += ("<height>") + unionBounds.getHeight() + ("</height>\n"); 252 geoSVG += ("</metadata>\n"); 253 254 255 /*//Scale the image, commented out as I decided to alter scale in getReasonableScale rather than adjust after the fact. 256 var tempWidth = Math.ceil(unionBounds.getWidth()); 257 var tempHeight = Math.ceil(unionBounds.getHeight()); 258 var quality = 1.0; 259 var bigger = Math.max(tempWidth, tempHeight); 260 var max = 1000; 261 if(!converter) 262 { 263 if(bigger < max) 264 { 265 if(bigger * 2 < max) 266 { 267 quality = 2; 268 } 269 else 270 { 271 quality = max / bigger; 272 } 273 } 274 else 275 { 276 quality = 1; 277 } 278 } 279 var geoSVG = '<svg viewBox="0 0 ' + tempWidth + ' ' + tempHeight + '"' + ' width="' + (tempWidth * quality) + 'px" height="' + (tempHeight * quality) + 'px" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" version="1.1">';//*/ 280 if (fillTexture != null) 281 geoSVG += fillTexture; 282 geoSVG += group; 283 geoSVG += "</svg>";//*/ 284 285 return geoSVG; 286 287 } else { 288 //return blank 2x2 SVG 289 return "<svg width=\"2px\" height=\"2px\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"></svg>"; 290 } 291 } 292 293 /** 294 * @param tiArray {@link SVGTextInfo[]} 295 * @param color a hex string "#000000" 296 * @param outlineColor a hex string "#000000" 297 */ 298 static ArrayList<String> renderTextElement(ArrayList<SVGTextInfo> tiArray, String color, String outlineColor) { 299 //ctx.lineCap = "butt"; 300 //ctx.lineJoin = "miter"; 301 //ctx.miterLimit = 3; 302 /*ctx.lineCap = "round"; 303 ctx.lineJoin = "round"; 304 ctx.miterLimit = 3;*/ 305 ArrayList<String> svgElements = new ArrayList<>(); 306 307 int size = tiArray.size(); 308 SVGTextInfo tempShape = null; 309 String textColor = "#000000"; 310 int tbm = RendererSettings.getInstance().getTextBackgroundMethod(); 311 int outlineWidth = RendererSettings.getInstance().getTextOutlineWidth(); 312 313 if (color != null) { 314 textColor = color; 315 } 316 317 318 if (outlineColor == null || outlineColor.isEmpty()) { 319 outlineColor = RendererUtilities.colorToHexString(RendererUtilities.getIdealOutlineColor(RendererUtilities.getColorFromHexString(textColor)), false); 320 } 321 322 323 if (tbm == RendererSettings.TextBackgroundMethod_OUTLINE 324 || tbm == RendererSettings.TextBackgroundMethod_OUTLINE_QUICK 325 || tbm == RendererSettings.TextBackgroundMethod_COLORFILL) { 326 for (int i = 0; i < size; i++) { 327 tempShape = tiArray.get(i); 328 svgElements.add(tempShape.toSVGElement(textColor, outlineColor, outlineWidth)); 329 } 330 } else //if(tbm == RendererSettings.TextBackgroundMethod_NONE) 331 { 332 for (int j = 0; j < size; j++) { 333 tempShape = tiArray.get(j); 334 svgElements.add(tempShape.toSVGElement(textColor, null, 0)); 335 } 336 } 337 338 return svgElements; 339 } 340 341 static String getFillPattern(ShapeInfo shapeInfo) { 342 if (shapeInfo.getPatternFillImage() != null) { 343 int width = shapeInfo.getPatternFillImage().getWidth(); 344 int height = shapeInfo.getPatternFillImage().getHeight(); 345 return "<defs><pattern id=\"fillPattern\" patternUnits=\"userSpaceOnUse\" width=\"" + width + "\" height=\"" + height + "\"><image href=\"" + MultiPointHandler.bitmapToString(shapeInfo.getPatternFillImage()) + "\" /></pattern></defs>"; 346 } else { 347 return null; 348 } 349 } 350 351 /** 352 * @param symbolID 353 * @param shapeInfo {@link ShapeInfo} 354 * @param pathBounds {@link Rectangle} 355 * @param ipc {@link IPointConversion} 356 * @param normalize 357 */ 358 static String ShapesToGeoSVG(String symbolID, ShapeInfo shapeInfo, Rectangle pathBounds, IPointConversion ipc, boolean normalize) { 359 Path path = null; 360 String fillColor = null; 361 String lineColor = null; 362 int lineWidth = 0; 363 double lineAlpha = 1.0; 364 double fillAlpha = 1.0; 365 float[] dashArray = null; 366 String fillPattern = null; 367 368 if (shapeInfo.getLineColor() != null) { 369 Color lineColorTemp = shapeInfo.getLineColor(); 370 lineAlpha = lineColorTemp.getAlpha() / 255.0; 371 lineColor = RendererUtilities.colorToHexString(lineColorTemp, false); 372 } 373 if (shapeInfo.getFillColor() != null) { 374 Color fillColorTemp = shapeInfo.getFillColor(); 375 fillAlpha = fillColorTemp.getAlpha() / 255.0; 376 fillColor = RendererUtilities.colorToHexString(fillColorTemp, false); 377 } 378 379 BasicStroke stroke = (BasicStroke)shapeInfo.getStroke(); 380 if (stroke != null) { 381 lineWidth = Math.round(stroke.getLineWidth()); 382 dashArray = stroke.getDashArray(); 383 } 384 385 ArrayList<ArrayList<Point2D>> shapesArray = shapeInfo.getPolylines(); 386 path = new Path(); 387 if (dashArray != null && dashArray.length > 0) 388 path.setLineDash(dashArray); 389 for (int i = 0; i < shapesArray.size(); i++) { 390 ArrayList<Point2D> shape = shapesArray.get(i); 391 392 for (int j = 0; j < shape.size(); j++) { 393 Point2D coord = shape.get(j); 394 if (j == 0) { 395 path.moveTo(coord.getX(), coord.getY()); 396 } else if (dashArray != null) { 397 path.dashedLineTo(coord.getX(), coord.getY()); 398 } else { 399 path.lineTo(coord.getX(), coord.getY()); 400 } 401 } 402 } 403 if (shapeInfo.getPatternFillImage() != null) 404 fillColor = "url(#fillPattern)"; 405 String svgElement = path.toSVGElement(lineColor, lineWidth, fillColor, lineAlpha, fillAlpha); 406 pathBounds.setRect(new Rectangle(path.getBounds())); 407 return svgElement; 408 } 409}