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