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