001package armyc2.c5isr.renderer; 002 003import android.graphics.Bitmap; 004import android.graphics.Bitmap.Config; 005import android.graphics.Canvas; 006import android.graphics.Paint; 007import android.graphics.Paint.FontMetrics; 008import android.graphics.Point; 009import android.graphics.Rect; 010import android.graphics.RectF; 011import android.util.Log; 012 013import com.caverock.androidsvg.SVG; 014 015import java.util.HashMap; 016import java.util.Map; 017 018import armyc2.c5isr.renderer.utilities.Color; 019import armyc2.c5isr.renderer.utilities.DrawRules; 020import armyc2.c5isr.renderer.utilities.ErrorLogger; 021import armyc2.c5isr.renderer.utilities.ImageInfo; 022import armyc2.c5isr.renderer.utilities.MSInfo; 023import armyc2.c5isr.renderer.utilities.MSLookup; 024import armyc2.c5isr.renderer.utilities.MilStdAttributes; 025import armyc2.c5isr.renderer.utilities.Modifiers; 026import armyc2.c5isr.renderer.utilities.RectUtilities; 027import armyc2.c5isr.renderer.utilities.RendererSettings; 028import armyc2.c5isr.renderer.utilities.RendererUtilities; 029import armyc2.c5isr.renderer.utilities.SVGInfo; 030import armyc2.c5isr.renderer.utilities.SVGLookup; 031import armyc2.c5isr.renderer.utilities.SVGSymbolInfo; 032import armyc2.c5isr.renderer.utilities.SettingsChangedEvent; 033import armyc2.c5isr.renderer.utilities.SettingsChangedEventListener; 034import armyc2.c5isr.renderer.utilities.SymbolDimensionInfo; 035import armyc2.c5isr.renderer.utilities.SymbolID; 036import armyc2.c5isr.renderer.utilities.SymbolUtilities; 037 038public class SinglePointSVGRenderer implements SettingsChangedEventListener 039{ 040 041 private final String TAG = "SinglePointRenderer"; 042 private static SinglePointSVGRenderer _instance = null; 043 044 private final Object _SinglePointCacheMutex = new Object(); 045 private final Object _UnitCacheMutex = new Object(); 046 047 private Paint _modifierFont = new Paint(); 048 private Paint _modifierOutlineFont = new Paint(); 049 private float _modifierDescent = 2; 050 private float _modifierFontHeight = 10; 051 private int _deviceDPI = 72; 052 053 054 private SinglePointSVGRenderer() 055 { 056 RendererSettings.getInstance().addEventListener(this); 057 058 //get modifier font values. 059 onSettingsChanged(new SettingsChangedEvent(SettingsChangedEvent.EventType_FontChanged)); 060 } 061 062 public static synchronized SinglePointSVGRenderer getInstance() 063 { 064 if (_instance == null) 065 { 066 _instance = new SinglePointSVGRenderer(); 067 } 068 069 return _instance; 070 } 071 072 /** 073 * 074 * @param symbolID 075 * @param modifiers 076 * @return 077 */ 078 public SVGSymbolInfo RenderUnit(String symbolID, Map<String,String> modifiers, Map<String,String> attributes) 079 { 080 SVGSymbolInfo si = null; 081 SymbolDimensionInfo newSDI = null; 082 083 String lineColor = null;//SymbolUtilitiesD.getLineColorOfAffiliation(symbolID); 084 String fillColor = null; 085 086 if(SymbolID.getSymbolSet(symbolID)==SymbolID.SymbolSet_MineWarfare && RendererSettings.getInstance().getSeaMineRenderMethod()==RendererSettings.SeaMineRenderMethod_MEDAL) 087 { 088 lineColor = RendererUtilities.colorToHexString(SymbolUtilities.getLineColorOfAffiliation(symbolID), false); 089 fillColor = RendererUtilities.colorToHexString(SymbolUtilities.getFillColorOfAffiliation(symbolID), true); 090 } 091 092 String iconColor = null; 093 094 int alpha = 255; 095 096 //SVG values 097 String frameID = null; 098 String iconID = null; 099 String mod1ID = null; 100 String mod2ID = null; 101 SVGInfo siFrame = null; 102 SVGInfo siIcon = null; 103 SVGInfo siMod1 = null; 104 SVGInfo siMod2 = null; 105 SVG mySVG = null; 106 int top = 0; 107 int left = 0; 108 int width = 0; 109 int height = 0; 110 String svgStart = null; 111 String strSVG = null; 112 String strSVGFrame = null; 113 114 115 Rect symbolBounds = null; 116 Rect fullBounds = null; 117 Bitmap fullBMP = null; 118 119 boolean hasDisplayModifiers = false; 120 boolean hasTextModifiers = false; 121 122 int pixelSize = -1; 123 boolean keepUnitRatio = true; 124 boolean icon = false; 125 boolean noFrame = false; 126 127 int ver = SymbolID.getVersion(symbolID); 128 129 // <editor-fold defaultstate="collapsed" desc="Parse Attributes"> 130 try 131 { 132 if(attributes != null) 133 { 134 if (attributes.containsKey(MilStdAttributes.PixelSize)) { 135 pixelSize = Integer.parseInt(attributes.get(MilStdAttributes.PixelSize)); 136 } else { 137 pixelSize = RendererSettings.getInstance().getDefaultPixelSize(); 138 } 139 140 if (attributes.containsKey(MilStdAttributes.KeepUnitRatio)) { 141 keepUnitRatio = Boolean.parseBoolean(attributes.get(MilStdAttributes.KeepUnitRatio)); 142 } 143 144 if (attributes.containsKey(MilStdAttributes.DrawAsIcon)) { 145 icon = Boolean.parseBoolean(attributes.get(MilStdAttributes.DrawAsIcon)); 146 } 147 148 if (icon)//icon won't show modifiers or display icons 149 { 150 //TODO: symbolID modifications as necessary 151 keepUnitRatio = false; 152 hasDisplayModifiers = false; 153 hasTextModifiers = false; 154 //symbolID = symbolID.substring(0, 10) + "-----"; 155 } else { 156 hasDisplayModifiers = ModifierRenderer.hasDisplayModifiers(symbolID, modifiers); 157 hasTextModifiers = ModifierRenderer.hasTextModifiers(symbolID, modifiers); 158 } 159 160 if (attributes.containsKey(MilStdAttributes.LineColor)) { 161 lineColor = (attributes.get(MilStdAttributes.LineColor)); 162 } 163 if (attributes.containsKey(MilStdAttributes.FillColor)) { 164 fillColor = (attributes.get(MilStdAttributes.FillColor)); 165 } 166 if (attributes.containsKey(MilStdAttributes.IconColor)) { 167 iconColor = (attributes.get(MilStdAttributes.IconColor)); 168 }//*/ 169 if (attributes.containsKey(MilStdAttributes.Alpha)) { 170 alpha = Integer.parseInt(attributes.get(MilStdAttributes.Alpha)); 171 } 172 } 173 } 174 catch (Exception excModifiers) 175 { 176 ErrorLogger.LogException("SinglePointSVGRenderer", "RenderUnit", excModifiers); 177 } 178 // </editor-fold> 179 180 try 181 { 182 183 //if not, generate symbol 184 if (si == null)//*/ 185 { 186 int version = SymbolID.getVersion(symbolID); 187 //Get SVG pieces of symbol 188 frameID = SVGLookup.getFrameID(symbolID); 189 iconID = SVGLookup.getMainIconID(symbolID); 190 mod1ID = SVGLookup.getMod1ID(symbolID); 191 mod2ID = SVGLookup.getMod2ID(symbolID); 192 siFrame = SVGLookup.getInstance().getSVGLInfo(frameID, version); 193 siIcon = SVGLookup.getInstance().getSVGLInfo(iconID, version); 194 195 if(siFrame == null) 196 { 197 frameID = SVGLookup.getFrameID(SymbolUtilities.reconcileSymbolID(symbolID)); 198 siFrame = SVGLookup.getInstance().getSVGLInfo(frameID, version); 199 if(siFrame == null)//still no match, get unknown frame 200 { 201 frameID = SVGLookup.getFrameID(SymbolID.setSymbolSet(symbolID,SymbolID.SymbolSet_Unknown)); 202 siFrame = SVGLookup.getInstance().getSVGLInfo(frameID, version); 203 } 204 } 205 206 if(siIcon == null) 207 { 208 if(iconID.substring(2,8).equals("000000")==false && MSLookup.getInstance().getMSLInfo(symbolID) == null) 209 siIcon = SVGLookup.getInstance().getSVGLInfo("98100000", version);//inverted question mark 210 else if(SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_Unknown) 211 siIcon = SVGLookup.getInstance().getSVGLInfo("00000000", version);//question mark 212 } 213 214 if(RendererSettings.getInstance().getScaleMainIcon()) 215 siIcon = RendererUtilities.scaleIcon(symbolID,siIcon); 216 217 siMod1 = SVGLookup.getInstance().getSVGLInfo(mod1ID, version); 218 siMod2 = SVGLookup.getInstance().getSVGLInfo(mod2ID, version); 219 top = Math.round(siFrame.getBbox().top); 220 left = Math.round(siFrame.getBbox().left); 221 width = Math.round(siFrame.getBbox().width()); 222 height = Math.round(siFrame.getBbox().height()); 223 if(siFrame.getBbox().bottom > 400) 224 svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 612 792\">"; 225 else 226 svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 400 400\">"; 227 228 //update line and fill color of frame SVG 229 if(lineColor != null || fillColor != null) 230 strSVGFrame = RendererUtilities.setSVGFrameColors(symbolID,siFrame.getSVG(),RendererUtilities.getColorFromHexString(lineColor),RendererUtilities.getColorFromHexString(fillColor)); 231 else 232 strSVGFrame = siFrame.getSVG(); 233 234 if(frameID.equals("octagon"))//for the 1 unit symbol that doesn't have a frame: 30 + 15000 235 { 236 noFrame = true; 237 strSVGFrame = strSVGFrame.replaceFirst("<g id=\"octagon\">", "<g id=\"octagon\" display=\"none\">"); 238 } 239 240 241 //get SVG dimensions and target dimensions 242 symbolBounds = RectUtilities.makeRect(left,top,width,height); 243 Rect rect = new Rect(symbolBounds); 244 float ratio = -1; 245 246 if (pixelSize > 0 && keepUnitRatio == true) 247 { 248 float heightRatio = SymbolUtilities.getUnitRatioHeight(symbolID); 249 float widthRatio = SymbolUtilities.getUnitRatioWidth(symbolID); 250 251 if(noFrame == true)//using octagon with display="none" as frame for a 1x1 shape 252 { 253 heightRatio = 1.0f; 254 widthRatio = 1.0f; 255 } 256 257 if (heightRatio > widthRatio) 258 { 259 pixelSize = (int) ((pixelSize / 1.5f) * heightRatio); 260 } 261 else 262 { 263 pixelSize = (int) ((pixelSize / 1.5f) * widthRatio); 264 } 265 } 266 if (pixelSize > 0) 267 { 268 float p = pixelSize; 269 float h = rect.height(); 270 float w = rect.width(); 271 272 ratio = Math.min((p / h), (p / w)); 273 274 symbolBounds = RectUtilities.makeRect(0f, 0f, w * ratio, h * ratio); 275 } 276 277 //StringBuilder sbGroupUnit = new StringBuilder(); 278 String sbGroupUnit = ""; 279 if(siFrame != null) 280 { 281 sbGroupUnit += ("<g transform=\"translate(" + (siFrame.getBbox().left * -ratio) + ',' + (siFrame.getBbox().top * -ratio) + ") scale(" + ratio + "," + ratio + ")\"" + ">"); 282 if(siFrame != null) 283 sbGroupUnit += (strSVGFrame);//(siFrame.getSVG()); 284 285 String color = ""; 286 if(iconColor != null) 287 { 288 //make sure string is properly formatted. 289 iconColor = RendererUtilities.colorToHexString(RendererUtilities.getColorFromHexString(iconColor),false); 290 if(iconColor != null && iconColor != "#000000" && iconColor != "") 291 color = " fill=\"" + iconColor + "\" "; 292 else 293 iconColor = null; 294 } 295 String unit = "<g" + color + ">"; 296 if (siIcon != null) 297 unit += (siIcon.getSVG()); 298 if (siMod1 != null) 299 unit += (siMod1.getSVG()); 300 if (siMod2 != null) 301 unit += (siMod2.getSVG()); 302 if(iconColor != null) 303 unit = unit.replaceAll("#000000",iconColor); 304 unit += "</g>"; 305 306 sbGroupUnit += unit + "</g>"; 307 } 308 309 //center of octagon is the center of all unit symbols 310 Point centerOctagon = new Point(306, 396); 311 centerOctagon.offset(-left,-top);//offset for the symbol bounds x,y 312 //scale center point by same ratio as the symbol 313 centerOctagon = new Point((int)(centerOctagon.x * ratio), (int)(centerOctagon.y * ratio)); 314 315 //set centerpoint of the image 316 Point centerPoint = centerOctagon; 317 Point centerCache = new Point(centerOctagon.x, centerOctagon.y); 318 319 //y offset to get centerpoint so we set back to zero when done. 320 //symbolBounds.top = 0; 321 RectUtilities.shift(symbolBounds,0,(int)-symbolBounds.top); 322 323 //Add core symbol to SVGSymbolInfo 324 si = new SVGSymbolInfo(sbGroupUnit.toString(), centerPoint,symbolBounds,symbolBounds); 325 326 hasDisplayModifiers = ModifierRenderer.hasDisplayModifiers(symbolID, modifiers); 327 hasTextModifiers = ModifierRenderer.hasTextModifiers(symbolID, modifiers); 328 329 //process display modifiers 330 if (hasDisplayModifiers) 331 { 332 newSDI = ModifierRenderer.processUnitDisplayModifiers(si, symbolID, modifiers, hasTextModifiers, attributes); 333 if(newSDI != null) 334 { 335 si = (SVGSymbolInfo) newSDI; 336 newSDI = null; 337 } 338 } 339 } 340 341 //process text modifiers 342 if (hasTextModifiers) 343 { 344 newSDI = ModifierRenderer.processSPTextModifiers(si, symbolID, modifiers, attributes); 345 } 346 347 if (newSDI != null) 348 { 349 si = (SVGSymbolInfo) newSDI; 350 } 351 newSDI = null; 352 353 if(modifiers != null) 354 si = (SVGSymbolInfo) ModifierRenderer.processSpeedLeader(si,symbolID,modifiers,attributes); 355 356 int widthOffset = 0; 357 if(hasTextModifiers) 358 widthOffset = 2;//add for the text outline 359 360 int svgWidth = (int)(si.getImageBounds().width() + widthOffset); 361 int svgHeight = (int)si.getImageBounds().height(); 362 //add SVG tag with dimensions 363 //draw unit from SVG 364 String svgAlpha = ""; 365 if(alpha >=0 && alpha <= 255) 366 svgAlpha = " opacity=\"" + alpha/255f + "\""; 367 svgStart = "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"" + svgWidth + "\" height=\"" + svgHeight +"\" viewBox=\"" + 0 + " " + 0 + " " + svgWidth + " " + svgHeight + "\"" + svgAlpha + ">\n"; 368 String svgTranslateGroup = null; 369 370 double transX = si.getImageBounds().left * -1; 371 double transY = si.getImageBounds().top * -1; 372 Point anchor = si.getCenterPoint(); 373 Rect imageBounds = si.getImageBounds(); 374 if(transX > 0 || transY > 0) 375 { 376 anchor.offset((int)transX,(int)transY); 377 //ShapeUtilities.offset(anchor,transX,transY); 378 RectUtilities.shift(symbolBounds,(int)transX,(int)transY); 379 //ShapeUtilities.offset(symbolBounds,transX,transY); 380 RectUtilities.shift(imageBounds,(int)transX,(int)transY); 381 //ShapeUtilities.offset(imageBounds,transX,transY); 382 svgTranslateGroup = "<g transform=\"translate(" + transX + "," + transY + ")" +"\">\n"; 383 } 384 imageBounds = RectUtilities.makeRect(imageBounds.left,imageBounds.top,svgWidth,svgHeight); 385 386 si = new SVGSymbolInfo(si.getSVG(),anchor,symbolBounds,imageBounds); 387 StringBuilder sbSVG = new StringBuilder(); 388 sbSVG.append(svgStart); 389 sbSVG.append(makeDescTag(si)); 390 sbSVG.append(makeMetadataTag(symbolID, si)); 391 if(svgTranslateGroup != null) 392 sbSVG.append(svgTranslateGroup); 393 sbSVG.append(si.getSVG()); 394 if(svgTranslateGroup != null) 395 sbSVG.append("\n</g>"); 396 sbSVG.append("\n</svg>"); 397 si = new SVGSymbolInfo(sbSVG.toString(),anchor,symbolBounds,imageBounds); 398 399 } 400 catch (Exception exc) 401 { 402 ErrorLogger.LogException("SinglePointSVGRenderer", "RenderUnit", exc); 403 } 404 return si; 405 } 406 407 /** 408 * 409 * @param symbolID 410 * @param modifiers 411 * @return 412 */ 413 @SuppressWarnings("unused") 414 public SVGSymbolInfo RenderSP(String symbolID, Map<String,String> modifiers, Map<String,String> attributes) 415 { 416 417 SVGSymbolInfo si = null; 418 419 ImageInfo temp = null; 420 String basicSymbolID = null; 421 422 Color lineColor = SymbolUtilities.getDefaultLineColor(symbolID); 423 Color fillColor = null;//SymbolUtilities.getFillColorOfAffiliation(symbolID); 424 425 int alpha = -1; 426 427 428 //SVG rendering variables 429 MSInfo msi = null; 430 String iconID = null; 431 SVGInfo siIcon = null; 432 String mod1ID = null; 433 SVGInfo siMod1 = null; 434 int top = 0; 435 int left = 0; 436 int width = 0; 437 int height = 0; 438 String svgStart = null; 439 String strSVG = null; 440 SVG mySVG = null; 441 442 float ratio = 0; 443 444 Rect symbolBounds = null; 445 RectF fullBounds = null; 446 Bitmap fullBMP = null; 447 448 boolean drawAsIcon = false; 449 int pixelSize = -1; 450 boolean keepUnitRatio = true; 451 boolean hasDisplayModifiers = false; 452 boolean hasTextModifiers = false; 453 boolean drawCustomOutline = false; 454 455 456 msi = MSLookup.getInstance().getMSLInfo(symbolID); 457 458 int ss = SymbolID.getSymbolSet(symbolID); 459 int ec = SymbolID.getEntityCode(symbolID); 460 int mod1 = 0; 461 int drawRule = 0; 462 if (msi != null) { 463 drawRule = msi.getDrawRule(); 464 } 465 boolean hasAPFill = false; 466 if(RendererSettings.getInstance().getActionPointDefaultFill()) { 467 if (SymbolUtilities.isActionPoint(symbolID) || //action points 468 ec/100 == 2135 || //sonobuoy 469 ec == 180100 || ec == 180200 || ec == 180400) //ACP, CCP, PUP 470 { 471 if (SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_ControlMeasure) { 472 lineColor = Color.BLACK; 473 hasAPFill = true; 474 } 475 } 476 } 477 478 try 479 { 480 if (modifiers == null) 481 modifiers = new HashMap<>(); 482 483 484 485 //get symbol info 486 487 msi = MSLookup.getInstance().getMSLInfo(symbolID); 488 489 if (msi == null)//if lookup fails, fix code/use unknown symbol code. 490 { 491 //TODO: change symbolID to Action Point with bad symbolID in the T or H field 492 } 493 494 495 if (attributes != null) { 496 if (attributes.containsKey(MilStdAttributes.KeepUnitRatio)) { 497 keepUnitRatio = Boolean.parseBoolean(attributes.get(MilStdAttributes.KeepUnitRatio)); 498 } 499 500 if (attributes.containsKey(MilStdAttributes.LineColor)) { 501 lineColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.LineColor)); 502 } 503 504 if (attributes.containsKey(MilStdAttributes.FillColor)) { 505 fillColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.FillColor)); 506 } 507 508 if (attributes.containsKey(MilStdAttributes.Alpha)) { 509 alpha = Integer.parseInt(attributes.get(MilStdAttributes.Alpha)); 510 } 511 512 if (attributes.containsKey(MilStdAttributes.DrawAsIcon)) { 513 drawAsIcon = Boolean.parseBoolean(attributes.get(MilStdAttributes.DrawAsIcon)); 514 } 515 516 if (attributes.containsKey(MilStdAttributes.PixelSize)) { 517 pixelSize = Integer.parseInt(attributes.get(MilStdAttributes.PixelSize)); 518 } else { 519 pixelSize = RendererSettings.getInstance().getDefaultPixelSize(); 520 } 521 /*if (keepUnitRatio == true && msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure && msi.getGeometry().equalsIgnoreCase("point")) { 522 if(msi.getDrawRule() == DrawRules.POINT1)//Action Points 523 pixelSize = (int)Math.ceil((pixelSize/1.5f) * 2.0f); 524 else if(SymbolID.getSymbolSet(symbolID)==SymbolID.SymbolSet_ControlMeasure && 525 ec/100 == 2135)//Sonobuoy 526 { 527 pixelSize = (int)Math.ceil((pixelSize/1.5f) * 2.0f); 528 } 529 else 530 pixelSize = (int)Math.ceil((pixelSize/1.5f) * 1.2f); 531 }//*/ 532 533 if(!(drawAsIcon==true || hasAPFill==true))//don't outline icons because they're not going on the map and icons with fills don't need it 534 { 535 if (attributes.containsKey(MilStdAttributes.OutlineSymbol)) 536 drawCustomOutline = Boolean.parseBoolean(attributes.get(MilStdAttributes.OutlineSymbol)); 537 else 538 drawCustomOutline = RendererSettings.getInstance().getOutlineSPControlMeasures(); 539 } 540 541 if (SymbolUtilities.isMultiPoint(symbolID)) 542 drawCustomOutline = false;//icon previews for multipoints do not need outlines since they shouldn't be on the map 543 } 544 545 if (drawAsIcon)//icon won't show modifiers or display icons 546 { 547 keepUnitRatio = false; 548 hasDisplayModifiers = false; 549 hasTextModifiers = false; 550 drawCustomOutline = false; 551 } else { 552 hasDisplayModifiers = ModifierRenderer.hasDisplayModifiers(symbolID, modifiers); 553 hasTextModifiers = ModifierRenderer.hasTextModifiers(symbolID, modifiers); 554 } 555 556 //Check if we need to set 'N' to "ENY" 557 int aff = SymbolID.getAffiliation(symbolID); 558 //int ss = msi.getSymbolSet(); 559 if (ss == SymbolID.SymbolSet_ControlMeasure && 560 (aff == SymbolID.StandardIdentity_Affiliation_Hostile_Faker || 561 aff == SymbolID.StandardIdentity_Affiliation_Suspect_Joker) && 562 modifiers.containsKey(Modifiers.N_HOSTILE) && 563 drawAsIcon == false) { 564 modifiers.put(Modifiers.N_HOSTILE, "ENY"); 565 } 566 567 } catch (Exception excModifiers) { 568 ErrorLogger.LogException("SinglePointSVGRenderer", "RenderSP-ParseModifiers", excModifiers); 569 } 570 571 try 572 { 573 int intFill = -1; 574 if (fillColor != null) { 575 intFill = fillColor.toInt(); 576 } 577 578 579 if (msi.getSymbolSet() != SymbolID.SymbolSet_ControlMeasure) 580 lineColor = Color.BLACK;//color isn't black but should be fine for weather since colors can't be user defined. 581 582 583 if (SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_ControlMeasure && SymbolID.getEntityCode(symbolID) == 270701)//static depiction 584 { 585 //add mine fill to image 586 mod1 = SymbolID.getModifier1(symbolID); 587 if (!(mod1 >= 13 && mod1 <= 50)) 588 symbolID = SymbolID.setModifier1(symbolID, 13); 589 } 590 591 592 //if not, generate symbol. 593 if (si == null)//*/ 594 { 595 int version = SymbolID.getVersion(symbolID); 596 //check symbol size//////////////////////////////////////////// 597 Rect rect = null; 598 iconID = SVGLookup.getMainIconID(symbolID); 599 siIcon = SVGLookup.getInstance().getSVGLInfo(iconID, version); 600 mod1ID = SVGLookup.getMod1ID(symbolID); 601 siMod1 = SVGLookup.getInstance().getSVGLInfo(mod1ID, version); 602 float borderPadding = 0; 603 if (drawCustomOutline) { 604 borderPadding = RendererUtilities.findWidestStrokeWidth(siIcon.getSVG()); 605 } 606 607 //Oceanographic / Bottom Feature - essentially italic serif fonts need more vertical space 608 //pixel sizes above 150 it's fine, which is weird 609 if(SymbolUtilities.getBasicSymbolID(symbolID).startsWith("461206")) 610 { 611 double va = siIcon.getBbox().height() * 0.025; 612 double ha = siIcon.getBbox().width() * 0.025;//some also need to be slightly wider 613 Rect adjustment = RectUtilities.makeRect((float)(siIcon.getBbox().left),(float)(siIcon.getBbox().top - va),(float)(siIcon.getBbox().width() + ha),(float)(siIcon.getBbox().height() + va)); 614 siIcon.getBbox().set(adjustment); 615 } 616 617 top = (int)Math.floor(siIcon.getBbox().top); 618 left = (int)Math.floor(siIcon.getBbox().left); 619 width = (int)Math.ceil(siIcon.getBbox().width() + (siIcon.getBbox().left - left)); 620 height = (int)Math.ceil(siIcon.getBbox().height() + (siIcon.getBbox().top - top)); 621 if (siIcon.getBbox().bottom > 400) 622 svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 612 792\">"; 623 else 624 svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 400 400\">"; 625 626 String strSVGIcon = null; 627 628 if(keepUnitRatio) 629 { 630 double scaler = Math.max(width/(float)height, height/(float)width); 631 if (scaler < 1.2) 632 scaler = 1.2; 633 if (scaler > 2) 634 scaler = 2; 635 636 if(!SymbolUtilities.isCBRNEvent(symbolID)) 637 pixelSize = (int) Math.ceil((pixelSize / 1.5f) * scaler); 638 639 /* 640 double min = Math.min(width/(float)height, height/(float)width); 641 if (min < 0.6)//Rectangle 642 pixelSize = (int) Math.ceil((pixelSize / 1.5f) * 2.0f); 643 else if(min < 0.85) 644 pixelSize = (int) Math.ceil((pixelSize / 1.5f) * 1.8f); 645 else //more of a square 646 pixelSize = (int) Math.ceil((pixelSize / 1.5f) * 1.2f);//*/ 647 } 648 649 if (hasAPFill) //action points and a few others //Sonobuoy //ACP, CCP, PUP 650 { 651 String apFill; 652 if (fillColor != null) 653 apFill = RendererUtilities.colorToHexString(fillColor, false); 654 else 655 apFill = RendererUtilities.colorToHexString(SymbolUtilities.getFillColorOfAffiliation(symbolID), false); 656 siIcon = new SVGInfo(siIcon.getID(), siIcon.getBbox(), siIcon.getSVG().replaceAll("fill=\"none\"", "fill=\"" + apFill + "\"")); 657 } 658 659 //update line and fill color of frame SVG 660 if (msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure && (lineColor != null || fillColor != null)) { 661 if (drawCustomOutline) { 662 // create outline with larger stroke-width first (if selected) 663 strSVGIcon = RendererUtilities.setSVGSPCMColors(symbolID, siIcon.getSVG(), RendererUtilities.getIdealOutlineColor(lineColor), fillColor, true); 664 } 665 666 // append normal symbol SVG to be layered on top of outline 667 strSVGIcon += RendererUtilities.setSVGSPCMColors(symbolID, siIcon.getSVG(), lineColor, fillColor, false); 668 } else//weather symbol (don't change color of weather graphics) 669 strSVGIcon = siIcon.getSVG(); 670 671 //If symbol is Static Depiction, add internal mine graphic based on sector modifier 1 672 if (SymbolID.getEntityCode(symbolID) == 270701 && siMod1 != null) { 673 if (drawCustomOutline) { 674 // create outline with larger stroke-width first (if selected) 675 strSVGIcon += RendererUtilities.setSVGSPCMColors(mod1ID, siMod1.getSVG(), RendererUtilities.getIdealOutlineColor(RendererUtilities.getColorFromHexString("#00A651")), RendererUtilities.getColorFromHexString("#00A651"), true); 676 } 677 //strSVGIcon += siMod1.getSVG(); 678 strSVGIcon += RendererUtilities.setSVGSPCMColors(mod1ID, siMod1.getSVG(), lineColor, fillColor, false); 679 } 680 681 if (pixelSize > 0) { 682 symbolBounds = RectUtilities.makeRect(left, top, width, height); 683 rect = new Rect(symbolBounds); 684 685 //adjust size 686 float p = pixelSize; 687 float h = rect.height(); 688 float w = rect.width(); 689 690 ratio = Math.min((p / h), (p / w)); 691 692 symbolBounds = RectUtilities.makeRect(0f, 0f, w * ratio, h * ratio); 693 694 //make sure border padding isn't excessive. 695 w = symbolBounds.width(); 696 h = symbolBounds.height(); 697 698 if (h / (h + borderPadding) > 0.10) { 699 borderPadding = (float) (h * 0.03); 700 } else if (w / (w + borderPadding) > 0.10) { 701 borderPadding = (float) (w * 0.03); 702 } 703 704 } 705 706 Rect borderPaddingBounds = null; 707 int offset = 0; 708 if(msi.getSymbolSet()==SymbolID.SymbolSet_ControlMeasure && drawCustomOutline && borderPadding != 0) 709 { 710 borderPaddingBounds = RectUtilities.makeRect(0, 0, (rect.width()+(borderPadding)) * ratio, (rect.height()+(borderPadding)) * ratio);//.makeRect(0f, 0f, w * ratio, h * ratio); 711 symbolBounds = borderPaddingBounds; 712 713 //grow size SVG to accommodate the outline we added 714 offset = (int)borderPadding/2;//4; 715 RectUtilities.grow(rect, offset); 716 } 717 718 String strLineJoin = ""; 719 720 if(msi.getSymbolSet()==SymbolID.SymbolSet_ControlMeasure && msi.getDrawRule()==DrawRules.POINT1)//smooth out action points 721 strLineJoin = " stroke-linejoin=\"round\" "; 722 723 StringBuilder sbGroupUnit = new StringBuilder(); 724 if(siIcon != null) 725 { 726 sbGroupUnit.append("<g transform=\"translate(" + (rect.left * -ratio) + ',' + (rect.top * -ratio) + ") scale(" + ratio + "," + ratio + ")\"" + strLineJoin + ">"); 727 sbGroupUnit.append(strSVGIcon);//(siIcon.getSVG()); 728 sbGroupUnit.append("</g>"); 729 } 730 731 //Point centerPoint = SymbolUtilities.getCMSymbolAnchorPoint(symbolID, RectUtilities.makeRectangle2DFromRect(offset, offset, symbolBounds.getWidth()-offset, symbolBounds.getHeight()-offset)); 732 Point centerPoint = SymbolUtilities.getCMSymbolAnchorPoint(symbolID, RectUtilities.makeRectF(0, 0, symbolBounds.width(), symbolBounds.height())); 733 734 /*if(borderPaddingBounds != null) { 735 RectUtilities.grow(symbolBounds, 4); 736 }//*/ 737 738 si = new SVGSymbolInfo(sbGroupUnit.toString(), centerPoint,symbolBounds,symbolBounds); 739 740 } 741 742 //Process Modifiers 743 SVGSymbolInfo siNew = null; 744 if (drawAsIcon == false && (hasTextModifiers || hasDisplayModifiers)) { 745 SymbolDimensionInfo sdiTemp = null; 746 if (SymbolUtilities.isSPWithSpecialModifierLayout(symbolID))//(SymbolUtilitiesD.isTGSPWithSpecialModifierLayout(symbolID)) 747 { 748 sdiTemp = ModifierRenderer.ProcessTGSPWithSpecialModifierLayout(si, symbolID, modifiers, attributes, lineColor); 749 } else { 750 sdiTemp = ModifierRenderer.ProcessTGSPModifiers(si, symbolID, modifiers, attributes, lineColor); 751 } 752 siNew = (sdiTemp instanceof SVGSymbolInfo ? (SVGSymbolInfo)sdiTemp : null); 753 754 } 755 756 if (siNew != null) { 757 si = siNew; 758 } 759 760 //add SVG tag with dimensions 761 //draw unit from SVG 762 String svgAlpha = ""; 763 if(alpha >=0 && alpha <= 255) 764 svgAlpha = " opacity=\"" + alpha/255f + "\""; 765 svgStart = "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"" + (int)si.getImageBounds().width() + "\" height=\"" + (int)si.getImageBounds().height() +"\" viewBox=\"" + 0 + " " + 0 + " " + (int)si.getImageBounds().width() + " " + (int)si.getImageBounds().height() + "\"" + svgAlpha + ">\n"; 766 String svgTranslateGroup = null; 767 768 double transX = si.getImageBounds().left * -1; 769 double transY = si.getImageBounds().top * -1; 770 Point anchor = si.getCenterPoint(); 771 Rect imageBounds = si.getImageBounds(); 772 if(transX > 0 || transY > 0) 773 { 774 //ShapeUtilities.offset(anchor,transX,transY); 775 anchor.offset(Math.round((float)transX),Math.round((float)transY)); 776 //ShapeUtilities.offset(symbolBounds,transX,transY); 777 symbolBounds.offset((int)transX,(int)Math.ceil(transY)); 778 //ShapeUtilities.offset(imageBounds,transX,transY); 779 imageBounds.offset((int)transX,(int)Math.ceil(transY)); 780 781 svgTranslateGroup = "<g transform=\"translate(" + transX + "," + transY + ")" +"\">\n"; 782 } 783 si = new SVGSymbolInfo(si.getSVG(),anchor,symbolBounds,imageBounds); 784 StringBuilder sbSVG = new StringBuilder(); 785 sbSVG.append(svgStart); 786 sbSVG.append(makeDescTag(si)); 787 sbSVG.append(makeMetadataTag(symbolID, si)); 788 if(svgTranslateGroup != null) 789 sbSVG.append(svgTranslateGroup); 790 sbSVG.append(si.getSVG()); 791 if(svgTranslateGroup != null) 792 sbSVG.append("\n</g>"); 793 sbSVG.append("\n</svg>"); 794 si = new SVGSymbolInfo(sbSVG.toString(),anchor,symbolBounds,imageBounds); 795 796 //cleanup 797 //bmp.recycle(); 798 symbolBounds = null; 799 fullBMP = null; 800 fullBounds = null; 801 mySVG = null; 802 803 804 } catch (Exception exc) { 805 ErrorLogger.LogException("SinglePointSVGRenderer", "RenderSP", exc); 806 return null; 807 } 808 809 return si; 810 811 } 812 813 814 /** 815 * 816 * @param symbolID 817 * @return 818 */ 819 @SuppressWarnings("unused") 820 public ImageInfo RenderModifier(String symbolID, Map<String,String> attributes) 821 { 822 ImageInfo temp = null; 823 String basicSymbolID = null; 824 825 Color lineColor = null; 826 Color fillColor = null;//SymbolUtilities.getFillColorOfAffiliation(symbolID); 827 828 int alpha = -1; 829 830 831 //SVG rendering variables 832 MSInfo msi = null; 833 String iconID = null; 834 SVGInfo siIcon = null; 835 int top = 0; 836 int left = 0; 837 int width = 0; 838 int height = 0; 839 String svgStart = null; 840 String strSVG = null; 841 SVG mySVG = null; 842 843 float ratio = 0; 844 845 Rect symbolBounds = null; 846 RectF fullBounds = null; 847 Bitmap fullBMP = null; 848 849 boolean drawAsIcon = false; 850 int pixelSize = -1; 851 boolean keepUnitRatio = true; 852 boolean hasDisplayModifiers = false; 853 boolean hasTextModifiers = false; 854 int symbolOutlineWidth = RendererSettings.getInstance().getSinglePointSymbolOutlineWidth(); 855 boolean drawCustomOutline = false; 856 857 try 858 { 859 860 msi = MSLookup.getInstance().getMSLInfo(symbolID); 861 if (attributes != null) 862 { 863 if (attributes.containsKey(MilStdAttributes.KeepUnitRatio)) 864 { 865 keepUnitRatio = Boolean.parseBoolean(attributes.get(MilStdAttributes.KeepUnitRatio)); 866 } 867 868 if (attributes.containsKey(MilStdAttributes.LineColor)) 869 { 870 lineColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.LineColor)); 871 } 872 873 if (attributes.containsKey(MilStdAttributes.FillColor)) 874 { 875 fillColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.FillColor)); 876 } 877 878 if (attributes.containsKey(MilStdAttributes.Alpha)) 879 { 880 alpha = Integer.parseInt(attributes.get(MilStdAttributes.Alpha)); 881 } 882 883 if (attributes.containsKey(MilStdAttributes.DrawAsIcon)) 884 { 885 drawAsIcon = Boolean.parseBoolean(attributes.get(MilStdAttributes.DrawAsIcon)); 886 } 887 888 if (attributes.containsKey(MilStdAttributes.PixelSize)) 889 { 890 pixelSize = Integer.parseInt(attributes.get(MilStdAttributes.PixelSize)); 891 if(msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure) 892 { 893 if(SymbolID.getEntityCode(symbolID)==270701)//static depiction 894 pixelSize = (int)(pixelSize * 0.9);//try to scale to be somewhat in line with units 895 } 896 } 897 898 if(drawAsIcon==false)//don't outline icons because they're not going on the map 899 { 900 if(attributes.containsKey(MilStdAttributes.OutlineSymbol)) 901 drawCustomOutline = Boolean.parseBoolean(attributes.get(MilStdAttributes.OutlineSymbol)); 902 else 903 drawCustomOutline = RendererSettings.getInstance().getOutlineSPControlMeasures(); 904 } 905 906 if(SymbolUtilities.isMultiPoint(symbolID)) 907 drawCustomOutline=false;//icon previews for multipoints do not need outlines since they shouldn't be on the map 908 909 /*if (attributes.containsKey(MilStdAttributes.OutlineWidth)>=0) 910 symbolOutlineWidth = Integer.parseInt(attributes.get(MilStdAttributes.OutlineWidth));//*/ 911 } 912 913 int outlineOffset = symbolOutlineWidth; 914 if (drawCustomOutline && outlineOffset > 2) 915 { 916 outlineOffset = (outlineOffset - 1) / 2; 917 } 918 else 919 { 920 outlineOffset = 0; 921 } 922 923 } 924 catch (Exception excModifiers) 925 { 926 ErrorLogger.LogException("SinglePointSVGRenderer", "RenderModifier", excModifiers); 927 } 928 929 try 930 { 931 ImageInfo ii = null; 932 int intFill = -1; 933 if (fillColor != null) 934 { 935 intFill = fillColor.toInt(); 936 } 937 938 939 if(msi.getSymbolSet() != SymbolID.SymbolSet_ControlMeasure) 940 lineColor = Color.BLACK;//color isn't black but should be fine for weather since colors can't be user defined. 941 942 943 //if not, generate symbol 944 if (ii == null)//*/ 945 { 946 int version = SymbolID.getVersion(symbolID); 947 //check symbol size//////////////////////////////////////////// 948 Rect rect = null; 949 950 iconID = SVGLookup.getMod1ID(symbolID); 951 siIcon = SVGLookup.getInstance().getSVGLInfo(iconID, version); 952 top = Math.round(siIcon.getBbox().top); 953 left = Math.round(siIcon.getBbox().left); 954 width = Math.round(siIcon.getBbox().width()); 955 height = Math.round(siIcon.getBbox().height()); 956 if(siIcon.getBbox().bottom > 400) 957 svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 612 792\">"; 958 else 959 svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 400 400\">"; 960 961 String strSVGIcon = null; 962 String strSVGOutline = null; 963 964 //update line and fill color of frame SVG 965 if(msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure && (lineColor != null || fillColor != null)) 966 strSVGIcon = RendererUtilities.setSVGFrameColors(symbolID,siIcon.getSVG(),lineColor,fillColor); 967 else 968 strSVGIcon = siIcon.getSVG(); 969 970 if (pixelSize > 0) 971 { 972 symbolBounds = RectUtilities.makeRect(left,top,width,height); 973 rect = new Rect(symbolBounds); 974 975 //adjust size 976 float p = pixelSize; 977 float h = rect.height(); 978 float w = rect.width(); 979 980 ratio = Math.min((p / h), (p / w)); 981 982 symbolBounds = RectUtilities.makeRect(0f, 0f, w * ratio, h * ratio); 983 984 } 985 986 987 //TODO: figure out how to draw an outline and adjust the symbol bounds accordingly 988 989 //Draw glyphs to bitmap 990 Bitmap bmp = Bitmap.createBitmap((symbolBounds.width()), (symbolBounds.height()), Config.ARGB_8888); 991 Canvas canvas = new Canvas(bmp); 992 993 symbolBounds = new Rect(0, 0, bmp.getWidth(), bmp.getHeight()); 994 995 strSVG = svgStart + strSVGIcon + "</svg>"; 996 mySVG = SVG.getFromString(strSVG); 997 mySVG.setDocumentViewBox(left,top,width,height); 998 mySVG.renderToCanvas(canvas); 999 1000 Point centerPoint = SymbolUtilities.getCMSymbolAnchorPoint(symbolID,new RectF(0, 0, symbolBounds.right, symbolBounds.bottom)); 1001 1002 ii = new ImageInfo(bmp, centerPoint, symbolBounds); 1003 1004 1005 /*if (drawAsIcon == false && pixelSize <= 100) 1006 { 1007 _tgCache.put(key, ii); 1008 }//*/ 1009 } 1010 1011 1012 //cleanup 1013 //bmp.recycle(); 1014 symbolBounds = null; 1015 fullBMP = null; 1016 fullBounds = null; 1017 mySVG = null; 1018 1019 1020 if (drawAsIcon) 1021 { 1022 return ii.getSquareImageInfo(); 1023 } 1024 else 1025 { 1026 return ii; 1027 } 1028 1029 } 1030 catch (Exception exc) 1031 { 1032 ErrorLogger.LogException("SinglePointSVGRenderer", "RenderModifier", exc); 1033 } 1034 return null; 1035 } 1036 1037 private String makeDescTag(SVGSymbolInfo si) 1038 { 1039 StringBuilder sbDesc = new StringBuilder(); 1040 1041 if(si != null) 1042 { 1043 Rect bounds = si.getSymbolBounds(); 1044 Rect iBounds = si.getImageBounds(); 1045 sbDesc.append("<desc>").append(si.getCenterX()).append(" ").append(si.getCenterY()).append(" "); 1046 sbDesc.append(bounds.left).append(" ").append(bounds.top).append(" ").append(bounds.width()).append(" ").append(bounds.height()).append(" "); 1047 sbDesc.append(iBounds.left).append(" ").append(iBounds.top).append(" ").append(iBounds.width()).append(" ").append(iBounds.height()); 1048 sbDesc.append("</desc>\n"); 1049 } 1050 return sbDesc.toString(); 1051 } 1052 1053 private String makeMetadataTag(String symbolID, SVGSymbolInfo si) 1054 { 1055 StringBuilder sbDesc = new StringBuilder(); 1056 1057 if(si != null) 1058 { 1059 Rect bounds = si.getSymbolBounds(); 1060 Rect iBounds = si.getImageBounds(); 1061 sbDesc.append("<metadata>\n"); 1062 sbDesc.append("<symbolID>").append(symbolID).append("</symbolID>\n"); 1063 sbDesc.append("<anchor>").append(si.getCenterX()).append(" ").append(si.getCenterY()).append("</anchor>\n"); 1064 sbDesc.append("<symbolBounds>").append(bounds.left).append(" ").append(bounds.top).append(" ").append(bounds.width()).append(" ").append(bounds.height()).append("</symbolBounds>\n"); 1065 sbDesc.append("<imageBounds>").append(iBounds.left).append(" ").append(iBounds.top).append(" ").append(iBounds.width()).append(" ").append(iBounds.height()).append("</imageBounds>\n");; 1066 sbDesc.append("</metadata>\n"); 1067 } 1068 return sbDesc.toString(); 1069 } 1070 1071 public void logError(String tag, Throwable thrown) 1072 { 1073 if (tag == null || tag.equals("")) 1074 { 1075 tag = "singlePointRenderer"; 1076 } 1077 1078 String message = thrown.getMessage(); 1079 String stack = getStackTrace(thrown); 1080 if (message != null) 1081 { 1082 Log.e(tag, message); 1083 } 1084 if (stack != null) 1085 { 1086 Log.e(tag, stack); 1087 } 1088 } 1089 1090 public String getStackTrace(Throwable thrown) 1091 { 1092 try 1093 { 1094 if (thrown != null) 1095 { 1096 if (thrown.getStackTrace() != null) 1097 { 1098 String eol = System.getProperty("line.separator"); 1099 StringBuilder sb = new StringBuilder(); 1100 sb.append(thrown.toString()); 1101 sb.append(eol); 1102 for (StackTraceElement element : thrown.getStackTrace()) 1103 { 1104 sb.append(" at "); 1105 sb.append(element); 1106 sb.append(eol); 1107 } 1108 return sb.toString(); 1109 } 1110 else 1111 { 1112 return thrown.getMessage() + "- no stack trace"; 1113 } 1114 } 1115 else 1116 { 1117 return "no stack trace"; 1118 } 1119 } 1120 catch (Exception exc) 1121 { 1122 Log.e("getStackTrace", exc.getMessage()); 1123 } 1124 return thrown.getMessage(); 1125 }// 1126 1127 /* 1128 private static String PrintList(ArrayList list) 1129 { 1130 String message = ""; 1131 for(Object item : list) 1132 { 1133 1134 message += item.toString() + "\n"; 1135 } 1136 return message; 1137 }//*/ 1138 /* 1139 private static String PrintObjectMap(Map<String, Object> map) 1140 { 1141 Iterator<Object> itr = map.values().iterator(); 1142 String message = ""; 1143 String temp = null; 1144 while(itr.hasNext()) 1145 { 1146 temp = String.valueOf(itr.next()); 1147 if(temp != null) 1148 message += temp + "\n"; 1149 } 1150 //ErrorLogger.LogMessage(message); 1151 return message; 1152 }//*/ 1153 @Override 1154 public void onSettingsChanged(SettingsChangedEvent sce) 1155 { 1156 1157 if(sce != null && sce.getEventType().equals(SettingsChangedEvent.EventType_FontChanged)) 1158 { 1159 synchronized (_modifierFont) 1160 { 1161 _modifierFont = RendererSettings.getInstance().getModiferFont(); 1162 _modifierOutlineFont = RendererSettings.getInstance().getModiferFont(); 1163 FontMetrics fm = new FontMetrics(); 1164 fm = _modifierFont.getFontMetrics(); 1165 _modifierDescent = fm.descent; 1166 //_modifierFontHeight = fm.top + fm.bottom; 1167 _modifierFontHeight = fm.bottom - fm.top; 1168 1169 _modifierFont.setStrokeWidth(RendererSettings.getInstance().getTextOutlineWidth()); 1170 _modifierOutlineFont.setColor(Color.white.toInt()); 1171 _deviceDPI = RendererSettings.getInstance().getDeviceDPI(); 1172 1173 ModifierRenderer.setModifierFont(_modifierFont, _modifierFontHeight, _modifierDescent); 1174 1175 } 1176 } 1177 } 1178}