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 //Protection of Cultural Property doesn't get outlined 541 if(ss==25 && ec >= 360000 && ec < 360400) 542 drawCustomOutline = false; 543 } 544 545 if (SymbolUtilities.isMultiPoint(symbolID)) 546 drawCustomOutline = false;//icon previews for multipoints do not need outlines since they shouldn't be on the map 547 } 548 549 if (drawAsIcon)//icon won't show modifiers or display icons 550 { 551 keepUnitRatio = false; 552 hasDisplayModifiers = false; 553 hasTextModifiers = false; 554 drawCustomOutline = false; 555 } else { 556 hasDisplayModifiers = ModifierRenderer.hasDisplayModifiers(symbolID, modifiers); 557 hasTextModifiers = ModifierRenderer.hasTextModifiers(symbolID, modifiers); 558 } 559 560 //Check if we need to set 'N' to "ENY" 561 int aff = SymbolID.getAffiliation(symbolID); 562 //int ss = msi.getSymbolSet(); 563 if (ss == SymbolID.SymbolSet_ControlMeasure && 564 (aff == SymbolID.StandardIdentity_Affiliation_Hostile_Faker || 565 aff == SymbolID.StandardIdentity_Affiliation_Suspect_Joker) && 566 modifiers.containsKey(Modifiers.N_HOSTILE) && 567 drawAsIcon == false) { 568 modifiers.put(Modifiers.N_HOSTILE, "ENY"); 569 } 570 571 } catch (Exception excModifiers) { 572 ErrorLogger.LogException("SinglePointSVGRenderer", "RenderSP-ParseModifiers", excModifiers); 573 } 574 575 try 576 { 577 int intFill = -1; 578 if (fillColor != null) { 579 intFill = fillColor.toInt(); 580 } 581 582 583 if (msi.getSymbolSet() != SymbolID.SymbolSet_ControlMeasure) 584 lineColor = Color.BLACK;//color isn't black but should be fine for weather since colors can't be user defined. 585 586 587 if (SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_ControlMeasure && SymbolID.getEntityCode(symbolID) == 270701)//static depiction 588 { 589 //add mine fill to image 590 mod1 = SymbolID.getModifier1(symbolID); 591 if (!(mod1 >= 13 && mod1 <= 50)) 592 symbolID = SymbolID.setModifier1(symbolID, 13); 593 } 594 595 596 //if not, generate symbol. 597 if (si == null)//*/ 598 { 599 int version = SymbolID.getVersion(symbolID); 600 //check symbol size//////////////////////////////////////////// 601 Rect rect = null; 602 iconID = SVGLookup.getMainIconID(symbolID); 603 siIcon = SVGLookup.getInstance().getSVGLInfo(iconID, version); 604 mod1ID = SVGLookup.getMod1ID(symbolID); 605 siMod1 = SVGLookup.getInstance().getSVGLInfo(mod1ID, version); 606 float borderPadding = 0; 607 if (drawCustomOutline) { 608 borderPadding = RendererUtilities.findWidestStrokeWidth(siIcon.getSVG()); 609 } 610 611 //Oceanographic / Bottom Feature - essentially italic serif fonts need more vertical space 612 //pixel sizes above 150 it's fine, which is weird 613 if(SymbolUtilities.getBasicSymbolID(symbolID).startsWith("461206")) 614 { 615 double va = siIcon.getBbox().height() * 0.025; 616 double ha = siIcon.getBbox().width() * 0.025;//some also need to be slightly wider 617 Rect adjustment = RectUtilities.makeRect((float)(siIcon.getBbox().left),(float)(siIcon.getBbox().top - va),(float)(siIcon.getBbox().width() + ha),(float)(siIcon.getBbox().height() + va)); 618 siIcon.getBbox().set(adjustment); 619 } 620 621 top = (int)Math.floor(siIcon.getBbox().top); 622 left = (int)Math.floor(siIcon.getBbox().left); 623 width = (int)Math.ceil(siIcon.getBbox().width() + (siIcon.getBbox().left - left)); 624 height = (int)Math.ceil(siIcon.getBbox().height() + (siIcon.getBbox().top - top)); 625 if (siIcon.getBbox().bottom > 400) 626 svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 612 792\">"; 627 else 628 svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 400 400\">"; 629 630 String strSVGIcon = null; 631 632 if(keepUnitRatio) 633 { 634 double scaler = Math.max(width/(float)height, height/(float)width); 635 if (scaler < 1.2) 636 scaler = 1.2; 637 if (scaler > 2) 638 scaler = 2; 639 640 if(!SymbolUtilities.isCBRNEvent(symbolID)) 641 pixelSize = (int) Math.ceil((pixelSize / 1.5f) * scaler); 642 643 /* 644 double min = Math.min(width/(float)height, height/(float)width); 645 if (min < 0.6)//Rectangle 646 pixelSize = (int) Math.ceil((pixelSize / 1.5f) * 2.0f); 647 else if(min < 0.85) 648 pixelSize = (int) Math.ceil((pixelSize / 1.5f) * 1.8f); 649 else //more of a square 650 pixelSize = (int) Math.ceil((pixelSize / 1.5f) * 1.2f);//*/ 651 } 652 653 if (hasAPFill) //action points and a few others //Sonobuoy //ACP, CCP, PUP 654 { 655 String apFill; 656 if (fillColor != null) 657 apFill = RendererUtilities.colorToHexString(fillColor, false); 658 else 659 apFill = RendererUtilities.colorToHexString(SymbolUtilities.getFillColorOfAffiliation(symbolID), false); 660 siIcon = new SVGInfo(siIcon.getID(), siIcon.getBbox(), siIcon.getSVG().replaceAll("fill=\"none\"", "fill=\"" + apFill + "\"")); 661 } 662 663 //Set dash array depending on affiliation and status 664 siIcon = RendererUtilities.setAffiliationDashArray(symbolID, siIcon); 665 666 //update line and fill color of frame SVG 667 if (msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure && (lineColor != null || fillColor != null)) { 668 if (drawCustomOutline) { 669 // create outline with larger stroke-width first (if selected) 670 strSVGIcon = RendererUtilities.setSVGSPCMColors(symbolID, siIcon.getSVG(), RendererUtilities.getIdealOutlineColor(lineColor), fillColor, true); 671 } 672 673 // append normal symbol SVG to be layered on top of outline 674 strSVGIcon += RendererUtilities.setSVGSPCMColors(symbolID, siIcon.getSVG(), lineColor, fillColor, false); 675 } else//weather symbol (don't change color of weather graphics) 676 strSVGIcon = siIcon.getSVG(); 677 678 //If symbol is Static Depiction, add internal mine graphic based on sector modifier 1 679 if (SymbolID.getEntityCode(symbolID) == 270701 && siMod1 != null) { 680 if (drawCustomOutline) { 681 // create outline with larger stroke-width first (if selected) 682 strSVGIcon += RendererUtilities.setSVGSPCMColors(mod1ID, siMod1.getSVG(), RendererUtilities.getIdealOutlineColor(RendererUtilities.getColorFromHexString("#00A651")), RendererUtilities.getColorFromHexString("#00A651"), true); 683 } 684 //strSVGIcon += siMod1.getSVG(); 685 strSVGIcon += RendererUtilities.setSVGSPCMColors(mod1ID, siMod1.getSVG(), lineColor, fillColor, false); 686 } 687 688 if (pixelSize > 0) { 689 symbolBounds = RectUtilities.makeRect(left, top, width, height); 690 rect = new Rect(symbolBounds); 691 692 //adjust size 693 float p = pixelSize; 694 float h = rect.height(); 695 float w = rect.width(); 696 697 ratio = Math.min((p / h), (p / w)); 698 699 symbolBounds = RectUtilities.makeRect(0f, 0f, w * ratio, h * ratio); 700 701 //make sure border padding isn't excessive. 702 w = symbolBounds.width(); 703 h = symbolBounds.height(); 704 705 if (h / (h + borderPadding) > 0.10) { 706 borderPadding = (float) (h * 0.03); 707 } else if (w / (w + borderPadding) > 0.10) { 708 borderPadding = (float) (w * 0.03); 709 } 710 711 } 712 713 Rect borderPaddingBounds = null; 714 int offset = 0; 715 if(msi.getSymbolSet()==SymbolID.SymbolSet_ControlMeasure && drawCustomOutline && borderPadding != 0) 716 { 717 borderPaddingBounds = RectUtilities.makeRect(0, 0, (rect.width()+(borderPadding)) * ratio, (rect.height()+(borderPadding)) * ratio);//.makeRect(0f, 0f, w * ratio, h * ratio); 718 symbolBounds = borderPaddingBounds; 719 720 //grow size SVG to accommodate the outline we added 721 offset = (int)borderPadding/2;//4; 722 RectUtilities.grow(rect, offset); 723 } 724 725 String strLineJoin = ""; 726 727 if(SymbolUtilities.isActionPoint(symbolID))//smooth out action points 728 strLineJoin = " stroke-linejoin=\"round\" "; 729 730 StringBuilder sbGroupUnit = new StringBuilder(); 731 if(siIcon != null) 732 { 733 sbGroupUnit.append("<g transform=\"translate(" + (rect.left * -ratio) + ',' + (rect.top * -ratio) + ") scale(" + ratio + "," + ratio + ")\"" + strLineJoin + ">"); 734 sbGroupUnit.append(strSVGIcon);//(siIcon.getSVG()); 735 sbGroupUnit.append("</g>"); 736 } 737 738 //Point centerPoint = SymbolUtilities.getCMSymbolAnchorPoint(symbolID, RectUtilities.makeRectangle2DFromRect(offset, offset, symbolBounds.getWidth()-offset, symbolBounds.getHeight()-offset)); 739 Point centerPoint = SymbolUtilities.getCMSymbolAnchorPoint(symbolID, RectUtilities.makeRectF(0, 0, symbolBounds.width(), symbolBounds.height())); 740 741 /*if(borderPaddingBounds != null) { 742 RectUtilities.grow(symbolBounds, 4); 743 }//*/ 744 745 si = new SVGSymbolInfo(sbGroupUnit.toString(), centerPoint,symbolBounds,symbolBounds); 746 747 } 748 749 //Process Modifiers 750 SVGSymbolInfo siNew = null; 751 if (drawAsIcon == false && (hasTextModifiers || hasDisplayModifiers)) { 752 SymbolDimensionInfo sdiTemp = null; 753 if (SymbolUtilities.isSPWithSpecialModifierLayout(symbolID))//(SymbolUtilitiesD.isTGSPWithSpecialModifierLayout(symbolID)) 754 { 755 sdiTemp = ModifierRenderer.ProcessTGSPWithSpecialModifierLayout(si, symbolID, modifiers, attributes, lineColor); 756 } else { 757 sdiTemp = ModifierRenderer.ProcessTGSPModifiers(si, symbolID, modifiers, attributes, lineColor); 758 } 759 siNew = (sdiTemp instanceof SVGSymbolInfo ? (SVGSymbolInfo)sdiTemp : null); 760 761 } 762 763 if (siNew != null) { 764 si = siNew; 765 } 766 767 //add SVG tag with dimensions 768 //draw unit from SVG 769 String svgAlpha = ""; 770 if(alpha >=0 && alpha <= 255) 771 svgAlpha = " opacity=\"" + alpha/255f + "\""; 772 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"; 773 String svgTranslateGroup = null; 774 775 double transX = si.getImageBounds().left * -1; 776 double transY = si.getImageBounds().top * -1; 777 Point anchor = si.getCenterPoint(); 778 Rect imageBounds = si.getImageBounds(); 779 if(transX > 0 || transY > 0) 780 { 781 //ShapeUtilities.offset(anchor,transX,transY); 782 anchor.offset(Math.round((float)transX),Math.round((float)transY)); 783 //ShapeUtilities.offset(symbolBounds,transX,transY); 784 symbolBounds.offset((int)transX,(int)Math.ceil(transY)); 785 //ShapeUtilities.offset(imageBounds,transX,transY); 786 imageBounds.offset((int)transX,(int)Math.ceil(transY)); 787 788 svgTranslateGroup = "<g transform=\"translate(" + transX + "," + transY + ")" +"\">\n"; 789 } 790 si = new SVGSymbolInfo(si.getSVG(),anchor,symbolBounds,imageBounds); 791 StringBuilder sbSVG = new StringBuilder(); 792 sbSVG.append(svgStart); 793 sbSVG.append(makeDescTag(si)); 794 sbSVG.append(makeMetadataTag(symbolID, si)); 795 if(svgTranslateGroup != null) 796 sbSVG.append(svgTranslateGroup); 797 sbSVG.append(si.getSVG()); 798 if(svgTranslateGroup != null) 799 sbSVG.append("\n</g>"); 800 sbSVG.append("\n</svg>"); 801 si = new SVGSymbolInfo(sbSVG.toString(),anchor,symbolBounds,imageBounds); 802 803 //cleanup 804 //bmp.recycle(); 805 symbolBounds = null; 806 fullBMP = null; 807 fullBounds = null; 808 mySVG = null; 809 810 811 } catch (Exception exc) { 812 ErrorLogger.LogException("SinglePointSVGRenderer", "RenderSP", exc); 813 return null; 814 } 815 816 return si; 817 818 } 819 820 821 /** 822 * 823 * @param symbolID 824 * @return 825 */ 826 @SuppressWarnings("unused") 827 public ImageInfo RenderModifier(String symbolID, Map<String,String> attributes) 828 { 829 ImageInfo temp = null; 830 String basicSymbolID = null; 831 832 Color lineColor = null; 833 Color fillColor = null;//SymbolUtilities.getFillColorOfAffiliation(symbolID); 834 835 int alpha = -1; 836 837 838 //SVG rendering variables 839 MSInfo msi = null; 840 String iconID = null; 841 SVGInfo siIcon = null; 842 int top = 0; 843 int left = 0; 844 int width = 0; 845 int height = 0; 846 String svgStart = null; 847 String strSVG = null; 848 SVG mySVG = null; 849 850 float ratio = 0; 851 852 Rect symbolBounds = null; 853 RectF fullBounds = null; 854 Bitmap fullBMP = null; 855 856 boolean drawAsIcon = false; 857 int pixelSize = -1; 858 boolean keepUnitRatio = true; 859 boolean hasDisplayModifiers = false; 860 boolean hasTextModifiers = false; 861 int symbolOutlineWidth = RendererSettings.getInstance().getSinglePointSymbolOutlineWidth(); 862 boolean drawCustomOutline = false; 863 864 try 865 { 866 867 msi = MSLookup.getInstance().getMSLInfo(symbolID); 868 if (attributes != null) 869 { 870 if (attributes.containsKey(MilStdAttributes.KeepUnitRatio)) 871 { 872 keepUnitRatio = Boolean.parseBoolean(attributes.get(MilStdAttributes.KeepUnitRatio)); 873 } 874 875 if (attributes.containsKey(MilStdAttributes.LineColor)) 876 { 877 lineColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.LineColor)); 878 } 879 880 if (attributes.containsKey(MilStdAttributes.FillColor)) 881 { 882 fillColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.FillColor)); 883 } 884 885 if (attributes.containsKey(MilStdAttributes.Alpha)) 886 { 887 alpha = Integer.parseInt(attributes.get(MilStdAttributes.Alpha)); 888 } 889 890 if (attributes.containsKey(MilStdAttributes.DrawAsIcon)) 891 { 892 drawAsIcon = Boolean.parseBoolean(attributes.get(MilStdAttributes.DrawAsIcon)); 893 } 894 895 if (attributes.containsKey(MilStdAttributes.PixelSize)) 896 { 897 pixelSize = Integer.parseInt(attributes.get(MilStdAttributes.PixelSize)); 898 if(msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure) 899 { 900 if(SymbolID.getEntityCode(symbolID)==270701)//static depiction 901 pixelSize = (int)(pixelSize * 0.9);//try to scale to be somewhat in line with units 902 } 903 } 904 905 if(drawAsIcon==false)//don't outline icons because they're not going on the map 906 { 907 if(attributes.containsKey(MilStdAttributes.OutlineSymbol)) 908 drawCustomOutline = Boolean.parseBoolean(attributes.get(MilStdAttributes.OutlineSymbol)); 909 else 910 drawCustomOutline = RendererSettings.getInstance().getOutlineSPControlMeasures(); 911 } 912 913 if(SymbolUtilities.isMultiPoint(symbolID)) 914 drawCustomOutline=false;//icon previews for multipoints do not need outlines since they shouldn't be on the map 915 916 /*if (attributes.containsKey(MilStdAttributes.OutlineWidth)>=0) 917 symbolOutlineWidth = Integer.parseInt(attributes.get(MilStdAttributes.OutlineWidth));//*/ 918 } 919 920 int outlineOffset = symbolOutlineWidth; 921 if (drawCustomOutline && outlineOffset > 2) 922 { 923 outlineOffset = (outlineOffset - 1) / 2; 924 } 925 else 926 { 927 outlineOffset = 0; 928 } 929 930 } 931 catch (Exception excModifiers) 932 { 933 ErrorLogger.LogException("SinglePointSVGRenderer", "RenderModifier", excModifiers); 934 } 935 936 try 937 { 938 ImageInfo ii = null; 939 int intFill = -1; 940 if (fillColor != null) 941 { 942 intFill = fillColor.toInt(); 943 } 944 945 946 if(msi.getSymbolSet() != SymbolID.SymbolSet_ControlMeasure) 947 lineColor = Color.BLACK;//color isn't black but should be fine for weather since colors can't be user defined. 948 949 950 //if not, generate symbol 951 if (ii == null)//*/ 952 { 953 int version = SymbolID.getVersion(symbolID); 954 //check symbol size//////////////////////////////////////////// 955 Rect rect = null; 956 957 iconID = SVGLookup.getMod1ID(symbolID); 958 siIcon = SVGLookup.getInstance().getSVGLInfo(iconID, version); 959 top = Math.round(siIcon.getBbox().top); 960 left = Math.round(siIcon.getBbox().left); 961 width = Math.round(siIcon.getBbox().width()); 962 height = Math.round(siIcon.getBbox().height()); 963 if(siIcon.getBbox().bottom > 400) 964 svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 612 792\">"; 965 else 966 svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 400 400\">"; 967 968 String strSVGIcon = null; 969 String strSVGOutline = null; 970 971 //update line and fill color of frame SVG 972 if(msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure && (lineColor != null || fillColor != null)) 973 strSVGIcon = RendererUtilities.setSVGFrameColors(symbolID,siIcon.getSVG(),lineColor,fillColor); 974 else 975 strSVGIcon = siIcon.getSVG(); 976 977 if (pixelSize > 0) 978 { 979 symbolBounds = RectUtilities.makeRect(left,top,width,height); 980 rect = new Rect(symbolBounds); 981 982 //adjust size 983 float p = pixelSize; 984 float h = rect.height(); 985 float w = rect.width(); 986 987 ratio = Math.min((p / h), (p / w)); 988 989 symbolBounds = RectUtilities.makeRect(0f, 0f, w * ratio, h * ratio); 990 991 } 992 993 994 //TODO: figure out how to draw an outline and adjust the symbol bounds accordingly 995 996 //Draw glyphs to bitmap 997 Bitmap bmp = Bitmap.createBitmap((symbolBounds.width()), (symbolBounds.height()), Config.ARGB_8888); 998 Canvas canvas = new Canvas(bmp); 999 1000 symbolBounds = new Rect(0, 0, bmp.getWidth(), bmp.getHeight()); 1001 1002 strSVG = svgStart + strSVGIcon + "</svg>"; 1003 mySVG = SVG.getFromString(strSVG); 1004 mySVG.setDocumentViewBox(left,top,width,height); 1005 mySVG.renderToCanvas(canvas); 1006 1007 Point centerPoint = SymbolUtilities.getCMSymbolAnchorPoint(symbolID,new RectF(0, 0, symbolBounds.right, symbolBounds.bottom)); 1008 1009 ii = new ImageInfo(bmp, centerPoint, symbolBounds); 1010 1011 1012 /*if (drawAsIcon == false && pixelSize <= 100) 1013 { 1014 _tgCache.put(key, ii); 1015 }//*/ 1016 } 1017 1018 1019 //cleanup 1020 //bmp.recycle(); 1021 symbolBounds = null; 1022 fullBMP = null; 1023 fullBounds = null; 1024 mySVG = null; 1025 1026 1027 if (drawAsIcon) 1028 { 1029 return ii.getSquareImageInfo(); 1030 } 1031 else 1032 { 1033 return ii; 1034 } 1035 1036 } 1037 catch (Exception exc) 1038 { 1039 ErrorLogger.LogException("SinglePointSVGRenderer", "RenderModifier", exc); 1040 } 1041 return null; 1042 } 1043 1044 private String makeDescTag(SVGSymbolInfo si) 1045 { 1046 StringBuilder sbDesc = new StringBuilder(); 1047 1048 if(si != null) 1049 { 1050 Rect bounds = si.getSymbolBounds(); 1051 Rect iBounds = si.getImageBounds(); 1052 sbDesc.append("<desc>").append(si.getCenterX()).append(" ").append(si.getCenterY()).append(" "); 1053 sbDesc.append(bounds.left).append(" ").append(bounds.top).append(" ").append(bounds.width()).append(" ").append(bounds.height()).append(" "); 1054 sbDesc.append(iBounds.left).append(" ").append(iBounds.top).append(" ").append(iBounds.width()).append(" ").append(iBounds.height()); 1055 sbDesc.append("</desc>\n"); 1056 } 1057 return sbDesc.toString(); 1058 } 1059 1060 private String makeMetadataTag(String symbolID, SVGSymbolInfo si) 1061 { 1062 StringBuilder sbDesc = new StringBuilder(); 1063 1064 if(si != null) 1065 { 1066 Rect bounds = si.getSymbolBounds(); 1067 Rect iBounds = si.getImageBounds(); 1068 sbDesc.append("<metadata>\n"); 1069 sbDesc.append("<symbolID>").append(symbolID).append("</symbolID>\n"); 1070 sbDesc.append("<anchor>").append(si.getCenterX()).append(" ").append(si.getCenterY()).append("</anchor>\n"); 1071 sbDesc.append("<symbolBounds>").append(bounds.left).append(" ").append(bounds.top).append(" ").append(bounds.width()).append(" ").append(bounds.height()).append("</symbolBounds>\n"); 1072 sbDesc.append("<imageBounds>").append(iBounds.left).append(" ").append(iBounds.top).append(" ").append(iBounds.width()).append(" ").append(iBounds.height()).append("</imageBounds>\n");; 1073 sbDesc.append("</metadata>\n"); 1074 } 1075 return sbDesc.toString(); 1076 } 1077 1078 public void logError(String tag, Throwable thrown) 1079 { 1080 if (tag == null || tag.equals("")) 1081 { 1082 tag = "singlePointRenderer"; 1083 } 1084 1085 String message = thrown.getMessage(); 1086 String stack = getStackTrace(thrown); 1087 if (message != null) 1088 { 1089 Log.e(tag, message); 1090 } 1091 if (stack != null) 1092 { 1093 Log.e(tag, stack); 1094 } 1095 } 1096 1097 public String getStackTrace(Throwable thrown) 1098 { 1099 try 1100 { 1101 if (thrown != null) 1102 { 1103 if (thrown.getStackTrace() != null) 1104 { 1105 String eol = System.getProperty("line.separator"); 1106 StringBuilder sb = new StringBuilder(); 1107 sb.append(thrown.toString()); 1108 sb.append(eol); 1109 for (StackTraceElement element : thrown.getStackTrace()) 1110 { 1111 sb.append(" at "); 1112 sb.append(element); 1113 sb.append(eol); 1114 } 1115 return sb.toString(); 1116 } 1117 else 1118 { 1119 return thrown.getMessage() + "- no stack trace"; 1120 } 1121 } 1122 else 1123 { 1124 return "no stack trace"; 1125 } 1126 } 1127 catch (Exception exc) 1128 { 1129 Log.e("getStackTrace", exc.getMessage()); 1130 } 1131 return thrown.getMessage(); 1132 }// 1133 1134 /* 1135 private static String PrintList(ArrayList list) 1136 { 1137 String message = ""; 1138 for(Object item : list) 1139 { 1140 1141 message += item.toString() + "\n"; 1142 } 1143 return message; 1144 }//*/ 1145 /* 1146 private static String PrintObjectMap(Map<String, Object> map) 1147 { 1148 Iterator<Object> itr = map.values().iterator(); 1149 String message = ""; 1150 String temp = null; 1151 while(itr.hasNext()) 1152 { 1153 temp = String.valueOf(itr.next()); 1154 if(temp != null) 1155 message += temp + "\n"; 1156 } 1157 //ErrorLogger.LogMessage(message); 1158 return message; 1159 }//*/ 1160 @Override 1161 public void onSettingsChanged(SettingsChangedEvent sce) 1162 { 1163 1164 if(sce != null && sce.getEventType().equals(SettingsChangedEvent.EventType_FontChanged)) 1165 { 1166 synchronized (_modifierFont) 1167 { 1168 _modifierFont = RendererSettings.getInstance().getModiferFont(); 1169 _modifierOutlineFont = RendererSettings.getInstance().getModiferFont(); 1170 FontMetrics fm = new FontMetrics(); 1171 fm = _modifierFont.getFontMetrics(); 1172 _modifierDescent = fm.descent; 1173 //_modifierFontHeight = fm.top + fm.bottom; 1174 _modifierFontHeight = fm.bottom - fm.top; 1175 1176 _modifierFont.setStrokeWidth(RendererSettings.getInstance().getTextOutlineWidth()); 1177 _modifierOutlineFont.setColor(Color.white.toInt()); 1178 _deviceDPI = RendererSettings.getInstance().getDeviceDPI(); 1179 1180 ModifierRenderer.setModifierFont(_modifierFont, _modifierFontHeight, _modifierDescent); 1181 1182 } 1183 } 1184 } 1185}