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