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