001package sec.web.render; 002// This import is if we need to call a javascript function 003// It requires that you import the plugins.jar from the jdk folder into the project libraries 004//import netscape.javascript.JSObject; 005 006import android.content.Context; 007import android.graphics.BitmapShader; 008import android.graphics.Shader; 009import android.util.SparseArray; 010import armyc2.c2sd.renderer.MilStdIconRenderer; 011import armyc2.c2sd.renderer.PatternFillRenderer; 012import armyc2.c2sd.renderer.utilities.ErrorLogger; 013import armyc2.c2sd.renderer.utilities.MilStdAttributes; 014import armyc2.c2sd.renderer.utilities.MilStdSymbol; 015import armyc2.c2sd.renderer.utilities.ModifiersTG; 016import armyc2.c2sd.renderer.utilities.RendererSettings; 017import armyc2.c2sd.renderer.utilities.ShapeInfo; 018import armyc2.c2sd.renderer.utilities.SymbolUtilities; 019import armyc2.c2sd.renderer.utilities.Color; 020import armyc2.c2sd.graphics2d.*; 021import sec.web.render.utilities.JavaRendererUtilities; 022import java.util.logging.Level; 023import sec.geo.kml.KmlOptions; 024import sec.web.json.utilities.JSONArray; 025import sec.web.json.utilities.JSONException; 026import sec.web.json.utilities.JSONObject; 027import sec.geo.utilities.StringBuilder; 028import java.util.ArrayList; 029 030/** 031 * 032 * @author Administrator 033 */ 034//@SuppressWarnings("unused") 035public final class SECWebRenderer /* extends Applet */ { 036 private static final long serialVersionUID = -2691218568602318366L; 037 038 // constants for the available shape types that can be generated into KML 039 public static final String CYLINDER = "CYLINDER-------"; 040 public static final String ORBIT = "ORBIT----------"; 041 public static final String ROUTE = "ROUTE----------"; 042 public static final String POLYGON = "POLYGON--------"; 043 public static final String RADARC = "RADARC---------"; 044 public static final String POLYARC = "POLYARC--------"; 045 public static final String CAKE = "CAKE-----------"; 046 public static final String TRACK = "TRACK----------"; 047 // Attribute names of the 3D shapes 048 public static final String ATTRIBUTES = "attributes"; 049 public static final String MIN_ALT = "minalt"; 050 public static final String MAX_ALT = "maxalt"; 051 public static final String RADIUS1 = "radius1"; 052 public static final String RADIUS2 = "radius2"; 053 public static final String LEFT_AZIMUTH = "leftAzimuth"; 054 public static final String RIGHT_AZIMUTH = "rightAzimuth"; 055 // Arbitrary default values of attributes 056 public static final double MIN_ALT_DEFAULT = 0.0D; 057 public static final double MAX_ALT_DEFAULT = 100.0D; 058 public static final double RADIUS1_DEFAULT = 50.0D; 059 public static final double RADIUS2_DEFAULT = 100.0D; 060 public static final double LEFT_AZIMUTH_DEFAULT = 0.0D; 061 public static final double RIGHT_AZIMUTH_DEFAULT = 90.0D; 062 063 public static final String ERR_ATTRIBUTES_NOT_FORMATTED = "{\"type\":\"error\"," 064 + "\"error\":\"The attribute paramaters are not formatted " 065 + "correctly"; 066 067 public static final String DEFAULT_ATTRIBUTES = "[{radius1:" 068 + RADIUS1_DEFAULT + ",radius2:" 069 + RADIUS2_DEFAULT + ",minalt:" 070 + MIN_ALT_DEFAULT + ",maxalt:" 071 + MAX_ALT_DEFAULT + ",rightAzimuth:" 072 + RIGHT_AZIMUTH_DEFAULT + ",leftAzimuth:" 073 + LEFT_AZIMUTH_DEFAULT + "}]"; 074 075 076 private static boolean _initSuccess = false; 077 078 079 public static synchronized void init(Context context, String cacheDir) { 080 081 try 082 { 083 if(_initSuccess == false) 084 { 085 MilStdIconRenderer.getInstance().init(context, cacheDir); 086 //use SECWebRenderer.setLoggingLevel() 087 088 //sets default value for single point symbology to have an outline. 089 //outline color will be automatically determined based on line color 090 //unless a color value is manually set. 091 092 //Set Renderer Settings///////////////////////////////////////////// 093 RendererSettings.getInstance().setSinglePointSymbolOutlineWidth(1); 094 RendererSettings.getInstance().setTextRenderMethod(RendererSettings.RenderMethod_NATIVE); 095 RendererSettings.getInstance().setTextBackgroundMethod( 096 RendererSettings.TextBackgroundMethod_OUTLINE_QUICK); 097 RendererSettings.getInstance().setTextOutlineWidth(2); 098 RendererSettings.getInstance().setLabelForegroundColor(Color.BLACK.toARGB()); 099 RendererSettings.getInstance().setLabelBackgroundColor(new Color(255, 255, 255, 200).toARGB()); 100 RendererSettings.getInstance().setModifierFont("arial", Font.PLAIN, 12); 101 ErrorLogger.setLevel(Level.FINE); 102 _initSuccess = true; 103 } 104 105 } 106 catch(Exception exc) 107 { 108 ErrorLogger.LogException("SECWebRenderer", "init", exc, Level.WARNING); 109 } 110 111 112 } 113 114 115 /**\ 116 * Set minimum level at which an item can be logged. 117 * In descending order: 118 * OFF = Integer.MAX_VALUE 119 * Severe = 1000 120 * Warning = 900 121 * Info = 800 122 * Config = 700 123 * Fine = 500 124 * Finer = 400 125 * Finest = 300 126 * All = Integer.MIN_VALUE 127 * Use like SECWebRenderer.setLoggingLevel(Level.INFO); 128 * or 129 * Use like SECWebRenderer.setLoggingLevel(800); 130 * @param level java.util.logging.level 131 */ 132 public static void setLoggingLevel(Level level) 133 { 134 try 135 { 136 ErrorLogger.setLevel(level,true); 137 ErrorLogger.LogMessage("SECWebRenderer", "setLoggingLevel(Level)", 138 "Logging level set to: " + ErrorLogger.getLevel().getName(), 139 Level.CONFIG); 140 } 141 catch(Exception exc) 142 { 143 ErrorLogger.LogException("SECWebRenderer", "setLoggingLevel(Level)", exc, Level.INFO); 144 } 145 } 146 147 /**\ 148 * Set minimum level at which an item can be logged. 149 * In descending order: 150 * OFF = Integer.MAX_VALUE 151 * Severe = 1000 152 * Warning = 900 153 * Info = 800 154 * Config = 700 155 * Fine = 500 156 * Finer = 400 157 * Finest = 300 158 * All = Integer.MIN_VALUE 159 * Use like SECWebRenderer.setLoggingLevel(Level.INFO); 160 * or 161 * Use like SECWebRenderer.setLoggingLevel(800); 162 * @param level int 163 */ 164 public static void setLoggingLevel(int level) 165 { 166 try 167 { 168 if(level > 1000) 169 ErrorLogger.setLevel(Level.OFF,true); 170 else if(level > 900) 171 ErrorLogger.setLevel(Level.SEVERE,true); 172 else if(level > 800) 173 ErrorLogger.setLevel(Level.WARNING,true); 174 else if(level > 700) 175 ErrorLogger.setLevel(Level.INFO,true); 176 else if(level > 500) 177 ErrorLogger.setLevel(Level.CONFIG,true); 178 else if(level > 400) 179 ErrorLogger.setLevel(Level.FINE,true); 180 else if(level > 300) 181 ErrorLogger.setLevel(Level.FINER,true); 182 else if(level > Integer.MIN_VALUE) 183 ErrorLogger.setLevel(Level.FINEST,true); 184 else 185 ErrorLogger.setLevel(Level.ALL,true); 186 187 ErrorLogger.LogMessage("SECWebRenderer", "setLoggingLevel(int)", 188 "Logging level set to: " + ErrorLogger.getLevel().getName(), 189 Level.CONFIG); 190 } 191 catch(Exception exc) 192 { 193 ErrorLogger.LogException("SECWebRenderer", "setLoggingLevel(int)", exc, Level.INFO); 194 } 195 } 196 197 /** 198 * Let's user choose between 2525Bch2 and 2525C. 199 * Ideally, set only once at startup. 200 * 2525Bch2 = 0, 2525C = 1. 201 * @param symStd 202 */ 203 public static void setDefaultSymbologyStandard(int symStd) 204 { 205 RendererSettings.getInstance().setSymbologyStandard(symStd); 206 } 207 208 /** 209 * Single Point Tactical Graphics are rendered from font files. 210 * The font size you specify here determines how big the symbols will 211 * be rendered. This should be set once at startup. 212 * @param size 213 */ 214 public static void setTacticalGraphicPointSize(int size) 215 { 216// sps.setTacticalGraphicPointSize(size); 217 } 218 219 /** 220 * Units are rendered from font files. 221 * The font size you specify here determines how big the symbols will 222 * be rendered. This should be set once at startup. 223 * @param size 224 */ 225 public static void setUnitPointSize(int size) 226 { 227// sps.setUnitPointSize(size); 228 } 229 230 /** 231 * Modifier Text Color will by default match the line color. 232 * This will override all modifier text color. 233 * @param hexColor 234 */ 235 public static void setModifierTextColor(String hexColor) 236 { 237 Color textColor = SymbolUtilities.getColorFromHexString(hexColor); 238 if(textColor==null) 239 { 240 textColor = Color.black; 241 } 242 RendererSettings.getInstance().setLabelForegroundColor(textColor.toARGB()); 243 } 244 245 246 247 248 249 /** 250 * Renders all multi-point symbols, creating KML that can be used to draw 251 * it on a Google map. Multipoint symbols cannot be draw the same 252 * at different scales. For instance, graphics with arrow heads will need to 253 * redraw arrowheads when you zoom in on it. Similarly, graphics like a 254 * Forward Line of Troops drawn with half circles can improve performance if 255 * clipped when the parts of the graphic that aren't on the screen. To help 256 * readjust graphics and increase performance, this function requires the 257 * scale and bounding box to help calculate the new locations. 258 * @param id A unique identifier used to identify the symbol by Google map. 259 * The id will be the folder name that contains the graphic. 260 * @param name a string used to display to the user as the name of the 261 * graphic being created. 262 * @param description a brief description about the graphic being made and 263 * what it represents. 264 * @param symbolCode A 15 character symbolID corresponding to one of the 265 * graphics in the MIL-STD-2525C 266 * @param controlPoints The vertices of the graphics that make up the 267 * graphic. Passed in the format of a string, using decimal degrees 268 * separating lat and lon by a comma, separating coordinates by a space. 269 * The following format shall be used "x1,y1[,z1] [xn,yn[,zn]]..." 270 * @param altitudeMode Indicates whether the symbol should interpret 271 * altitudes as above sea level or above ground level. Options are 272 * "clampToGround", "relativeToGround" (from surface of earth), "absolute" 273 * (sea level), "relativeToSeaFloor" (from the bottom of major bodies of 274 * water). 275 * @param scale A number corresponding to how many meters one meter of our 276 * map represents. A value "50000" would mean 1:50K which means for every 277 * meter of our map it represents 50000 meters of real world distance. 278 * @param bbox The viewable area of the map. Passed in the format of a 279 * string "lowerLeftX,lowerLeftY,upperRightX,upperRightY." Not required 280 * but can speed up rendering in some cases. 281 * example: "-50.4,23.6,-42.2,24.2" 282 * @param modifiers SparseArray<String>, keyed using constants from ModifiersTG. 283 * Pass in comma delimited String for modifiers with multiple values like AM, AN & X 284 * @param attributes SparseArray<String>, keyed using constants from MilStdAttributes. 285 * @param format An enumeration: 0 for KML, 1 for JSON. 286 * @param symStd An enumeration: 0 for 2525Bch2, 1 for 2525C. 287 * @return A JSON string representation of the graphic. 288 */ 289 public static String RenderSymbol(String id, String name, String description, 290 String symbolCode, String controlPoints, String altitudeMode, 291 double scale, String bbox, SparseArray<String> modifiers, SparseArray<String> attributes, int format, int symStd) { 292 293 String output = ""; 294 try { 295 296 JavaRendererUtilities.addAltModeToModifiersString(attributes,altitudeMode); 297 298 //if (JavaRendererUtilities.is3dSymbol(symbolCode, modifiers)) 299 if (altitudeMode.equals("clampToGround") == false && format == 0 && JavaRendererUtilities.is3dSymbol(symbolCode, modifiers)) 300 { 301 if (altitudeMode.isEmpty()) 302 altitudeMode = "absolute"; 303 304 output = RenderMilStd3dSymbol(name, id, symbolCode, description, altitudeMode, controlPoints, 305 modifiers, attributes); 306 307 //get modifiers///////////////////////////////////////////////// 308 String modifierKML = MultiPointHandler.getModififerKML(id, name, description, symbolCode, controlPoints, 309 scale, bbox, modifiers, attributes, format,symStd); 310 311 modifierKML += "</Folder>"; 312 313 output = output.replaceFirst("</Folder>", modifierKML); 314 315 //////////////////////////////////////////////////////////////// 316 317 318 319 // Check the output of the 3D Symbol Drawing. If this returned an error 320 // it should either be "" or it should be a JSON string starting with "{". 321 // This really is not a good solution, but was up to 13.0.6 and had to make 322 // this bug fix in quick turnaround. More consistent error handling should 323 // be done through code. 324 325 if (output.equals("") || output.startsWith("{")) { 326 output = MultiPointHandler.RenderSymbol(id, name, description, symbolCode, controlPoints, 327 scale, bbox, modifiers, attributes, format,symStd); 328 } 329 } 330 else 331 { 332 output = MultiPointHandler.RenderSymbol(id, name, description, symbolCode, controlPoints, 333 scale, bbox, modifiers, attributes, format,symStd); 334 335 //DEBUGGING 336 if(ErrorLogger.getLevel().intValue() <= Level.FINER.intValue()) 337 { 338 System.out.println(""); 339 StringBuilder sb = new StringBuilder(); 340 sb.append("\nID: " + id + "\n"); 341 sb.append("Name: " + name + "\n"); 342 sb.append("Description: " + description + "\n"); 343 sb.append("SymbolID: " + symbolCode + "\n"); 344 sb.append("SymStd: " + String.valueOf(symStd) + "\n"); 345 sb.append("Scale: " + String.valueOf(scale) + "\n"); 346 sb.append("BBox: " + bbox + "\n"); 347 sb.append("Coords: " + controlPoints + "\n"); 348 sb.append("Modifiers: " + modifiers + "\n"); 349 ErrorLogger.LogMessage("SECWebRenderer", "RenderSymbol", sb.toString(),Level.FINER); 350 } 351 if(ErrorLogger.getLevel().intValue() <= Level.FINEST.intValue()) 352 { 353 String briefOutput = output.replaceAll("</Placemark>", "</Placemark>\n"); 354 briefOutput = output.replaceAll("(?s)<description[^>]*>.*?</description>", "<description></description>"); 355 ErrorLogger.LogMessage("SECWebRenderer", "RenderSymbol", "Output:\n" + briefOutput,Level.FINEST); 356 } 357 } 358 359 360 } catch (Exception ea) { 361 362 output = "{\"type\":'error',error:'There was an error creating the MilStdSymbol - " + ea.toString() + "'}"; 363 ErrorLogger.LogException("SECWebRenderer", "RenderSymbol", ea, Level.WARNING); 364 } 365 366 return output; 367 } 368 369 370 371 372 373 /** 374 * Renders all multi-point symbols, creating KML or JSON for the user to 375 * parse and render as they like. 376 * This function requires the bounding box to help calculate the new 377 * locations. 378 * @param id A unique identifier used to identify the symbol by Google map. 379 * The id will be the folder name that contains the graphic. 380 * @param name a string used to display to the user as the name of the 381 * graphic being created. 382 * @param description a brief description about the graphic being made and 383 * what it represents. 384 * @param symbolCode A 15 character symbolID corresponding to one of the 385 * graphics in the MIL-STD-2525C 386 * @param controlPoints The vertices of the graphics that make up the 387 * graphic. Passed in the format of a string, using decimal degrees 388 * separating lat and lon by a comma, separating coordinates by a space. 389 * The following format shall be used "x1,y1 [xn,yn]..." 390 * @param pixelWidth pixel dimensions of the viewable map area 391 * @param pixelHeight pixel dimensions of the viewable map area 392 * @param bbox The viewable area of the map. Passed in the format of a 393 * string "lowerLeftX,lowerLeftY,upperRightX,upperRightY." 394 * example: "-50.4,23.6,-42.2,24.2" 395 * @param modifiers SparseArray<String>, keyed using constants from ModifiersTG. 396 * Pass in comma delimited String for modifiers with multiple values like AM, AN & X 397 * @param attributes SparseArray<String>, keyed using constants from MilStdAttributes. 398 * @param format An enumeration: 0 for KML, 1 for JSON. 399 * @param symStd An enumeration: 0 for 2525Bch2, 1 for 2525C. 400 * @return A JSON (1) or KML (0) string representation of the graphic. 401 */ 402 public static String RenderSymbol2D(String id, String name, String description, String symbolCode, String controlPoints, 403 int pixelWidth, int pixelHeight, String bbox, SparseArray<String> modifiers, 404 SparseArray<String> attributes, int format, int symStd) 405 { 406 String output = ""; 407 try 408 { 409 output = MultiPointHandler.RenderSymbol2D(id, name, description, 410 symbolCode, controlPoints, pixelWidth, pixelHeight, bbox, 411 modifiers, attributes, format, symStd); 412 } 413 catch(Exception exc) 414 { 415 output = "{\"type\":'error',error:'There was an error creating the MilStdSymbol: " + symbolCode + " - " + exc.toString() + "'}"; 416 } 417 return output; 418 } 419 420 /** 421 * Creates a 3D symbol to be displayed on some 3D globe surface. Generates 422 * Keyhole Markup Language (KML) to return that specifies the points and format of 423 * the rendering. 424 * <br/> 425 * Control points should be of the format of: 426 * <tr><code>"x,y,z [x,y,z]..."</code></tr> 427 * Attributes should be passed in as a JSON array. If more than one set of 428 * parameters are passed in as an array or more than one item, they will map 429 * to the vertex specified in the control points. The attributes are 430 * of the format: 431 * <tr><code>{"attributes":[{"<i>attribute1</i>":<i>value</i>,...},{<i>[optional]</i>]}</code></tr> 432 * 433 * @param name The user displayed name for the symbol. Users will use this 434 * to identify with the symbol. 435 * @param id An internally used unique id that developers can use to 436 * uniquely distinguish this symbol from others. 437 * @param shapeType A 15 character ID of the type of symbol to draw. 438 * @param description A brief description of what the symbol represents. 439 * Generic text that does not require any format. 440 * @param fillColor The fill color of the graphic 441 * @param altitudeMode Indicates whether the symbol should interpret 442 * altitudes as above sea level or above ground level. Options are 443 * "relativeToGround" (from surface of earth), "absolute" (sea level), 444 * "relativeToSeaFloor" (from the bottom of major bodies of water). 445 * @param controlPoints The vertices of the shape. The number of required 446 * vertices varies based on the shapeType of the symbol. The simplest shape 447 * requires at least one point. Shapes that require more points than 448 * required will ignore extra points. Format for numbers is as follows: 449 * <br/><br/> 450 * "x,y,z [x,y,z ]..." 451 * @param attributes A JSON array holding the parameters for the 452 * shape. Attributes should be of the following format: <br/><br/> 453 * <tr><code>{"attributes":[{"<i>attribute1</i>":<i>value</i>,...},{<i>[optional]</i>]}</code></tr> 454 * @return A KML string that represents a placemark for the 3D shape 455 */ 456 public static String Render3dSymbol(String name, String id, String shapeType, 457 String description, String lineColor, String fillColor, String altitudeMode, 458 String controlPoints, 459 String attributes) { 460 461 String returnValue = ""; 462 463 try { 464 465 StringBuilder output = new StringBuilder(); 466 SymbolModifiers modifiers = new SymbolModifiers(); 467 JSONObject attributesJSON; 468 469 // Retrieve the attributes from the attributes object 470 // Attributes should be a JSON array string of this format 471 // { "attributes":[{"radius1":50, "minalt":0, "maxalt:100"}]} 472 // There should only be one item in the JSON array, but if 473 // there is more items this will ignore them. 474 //attributes="{attributes:[{radius1:5000, minalt:0, maxalt:100}]}"; 475 attributesJSON = new JSONObject(attributes); 476 477 // If no attributes passed in or attributes set to null 478 // default to an empty string. 479 if (attributesJSON == null || attributes.equals("")) { 480 attributesJSON = new JSONObject(DEFAULT_ATTRIBUTES); 481 } 482 483 JSONArray attributesArray = attributesJSON.getJSONArray(ATTRIBUTES); 484 int attributesArrayLength = attributesArray.length(); 485 if (attributesArrayLength> 0) { 486 487 for(int i = 0; i < attributesArrayLength; i++) { 488 489 // get the first item in the array and use those parameters. 490 // if any of the parameters don't exist, it will use defaults. 491 // Defaults are arbitrary, no reason not to change them. 492 JSONObject currentAttributeSet = attributesArray.getJSONObject(i); 493 494 if (currentAttributeSet.has(RADIUS1)) { 495 modifiers.AM_DISTANCE.add(currentAttributeSet.getDouble(RADIUS1)); 496 } 497 498 if (currentAttributeSet.has(RADIUS2)) { 499 modifiers.AM_DISTANCE.add(currentAttributeSet.getDouble(RADIUS2)); 500 } 501 502 if (currentAttributeSet.has(MIN_ALT)) { 503 modifiers.X_ALTITUDE_DEPTH.add(currentAttributeSet.getDouble(MIN_ALT)); 504 } 505 506 if (currentAttributeSet.has(MAX_ALT)) { 507 modifiers.X_ALTITUDE_DEPTH.add(currentAttributeSet.getDouble(MAX_ALT)); 508 } 509 510 if (currentAttributeSet.has(LEFT_AZIMUTH)) { 511 modifiers.AN_AZIMUTH.add(currentAttributeSet.getDouble(LEFT_AZIMUTH)); 512 } 513 514 if (currentAttributeSet.has(RIGHT_AZIMUTH)) { 515 modifiers.AN_AZIMUTH.add(currentAttributeSet.getDouble(RIGHT_AZIMUTH)); 516 } 517 } 518 } 519 520 // Send to the 3D renderer for generating the 3D point and creating 521 // the KML to return. 522 returnValue = Shape3DHandler.render3dSymbol(name, id, shapeType, 523 description, lineColor, fillColor, altitudeMode, controlPoints, modifiers); 524 } 525 catch (JSONException je) { 526 527 return ERR_ATTRIBUTES_NOT_FORMATTED; 528 } 529 catch (Exception ea) { 530 ErrorLogger.LogException("SECWebRenderer", "Render3dSymbol()", ea); 531 return ""; 532 } 533 534 return returnValue; 535 } 536 537 /** 538 * Creates a 3D symbol from the MilStd2525B USAS or MIL-STD-2525C to be 539 * displayed on a 3D globe surface. Only certain symbols from the MIL-STD 540 * can be displayed in 3D. Most of these are graphics that fall under Fire 541 * Support. Any graphic that has an X modifier (altitude/depth) should 542 * have a 3D representation. Generates 543 * Keyhole Markup Language (KML) to return that 544 * specifies the points and format of 545 * the rendering. 546 * <br/> 547 * Control points should be of the format of: 548 * <tr><code>"x,y,z [x,y,z]..."</code></tr> 549 * 550 * 551 * @param name The user displayed name for the symbol. Users will use this 552 * to identify with the symbol. 553 * @param id An internally used unique id that developers can use to 554 * uniquely distinguish this symbol from others. 555 * @param symbolCode A 15 character ID of the type of symbol to draw. Only 556 * symbols with an X modifier from the standard will draw. 557 * @param description A brief description of what the symbol represents. 558 * Generic text that does not require any format. 559 * @param altitudeMode Indicates whether the symbol should interpret 560 * altitudes as above sea level or above ground level. Options are 561 * "relativeToGround" (from surface of earth), "absolute" (sea level), 562 * "relativeToSeaFloor" (from the bottom of major bodies of water). 563 * @param controlPoints The vertices of the shape. The number of required 564 * vertices varies based on the shapeType of the symbol. The simplest shape 565 * requires at least one point. Shapes that require more points than 566 * required will ignore extra points. Format for numbers is as follows: 567 * <br/><br/> 568 * "x,y,z [x,y,z ]..." 569 * @saModifiers SparseArray<String> keyed on ModifiersTG. Only using values AM, AN and X. 570 * Strings should be comma delimited for multiple values. 571 * @saAttributes SparseArray<String> keyed on MilStdAttributes. Only using value FillColor. 572 * @return A KML string that represents a placemark for the 3D shape 573 */ 574 private static String RenderMilStd3dSymbol(String name, String id, String symbolCode, 575 String description, 576 String altitudeMode, 577 String controlPoints, 578 SparseArray<String> saModifiers, 579 SparseArray<String> saAttributes) { 580 581 String symbolId = symbolCode.substring(4,10); 582 SymbolModifiers attributes = new SymbolModifiers(); 583 String output = ""; 584 585 KmlOptions.AltitudeMode convertedAltitudeMode = KmlOptions.AltitudeMode.RELATIVE_TO_GROUND; 586 587 // Convert altitude mode to an enum that we understand. If it does not 588 // understand or is "", then convert to ALTITUDE_RELATIVE_TO_GROUND. 589 if (!altitudeMode.equals("")) 590 { 591 convertedAltitudeMode = KmlOptions.AltitudeMode.fromString(altitudeMode); 592 } 593 594 try 595 { 596 597 598 String[] altitudeDepth = null; 599 String[] distance = null; 600 String[] azimuth = null; 601 int altitudeDepthLength = 0; 602 int distanceLength = 0; 603 int azimuthLength = 0; 604 String lineColor = ""; 605 String fillColor = ""; 606 607 if (saModifiers.indexOfKey(ModifiersTG.X_ALTITUDE_DEPTH)>=0) 608 { 609 altitudeDepth = saModifiers.get(ModifiersTG.X_ALTITUDE_DEPTH).split(","); 610 altitudeDepthLength = altitudeDepth.length; 611 } 612 613 if (saModifiers.indexOfKey(ModifiersTG.AN_AZIMUTH)>=0) 614 { 615 azimuth = saModifiers.get(ModifiersTG.AN_AZIMUTH).split(","); 616 azimuthLength = azimuth.length; 617 } 618 619 if (saModifiers.indexOfKey(ModifiersTG.AM_DISTANCE)>=0) 620 { 621 distance = saModifiers.get(ModifiersTG.AM_DISTANCE).split(","); 622 distanceLength = distance.length; 623 } 624 625 if (saAttributes.indexOfKey(MilStdAttributes.LineColor)>=0) 626 { 627 lineColor = saAttributes.get(MilStdAttributes.LineColor); 628 } 629 else 630 { 631 Color c = SymbolUtilities.getFillColorOfAffiliation(symbolCode); 632 lineColor = c.toHexString(); 633 // ensure that some color is selected. If no color can be 634 // found, use black. 635 if (lineColor == null) 636 { 637 lineColor = "FF000000"; 638 } 639 } 640 641 if (saAttributes.indexOfKey(MilStdAttributes.FillColor)>=0) 642 { 643 fillColor = saAttributes.get(MilStdAttributes.FillColor); 644 } 645 else 646 { 647 Color c = SymbolUtilities.getFillColorOfAffiliation(symbolCode); 648 fillColor = c.toHexString(); 649 // ensure that some color is selected. If no color can be 650 // found, use black. 651 if (fillColor == null) 652 { 653 fillColor = "AA000000"; 654 } 655 } 656 657 lineColor = JavaRendererUtilities.ARGBtoABGR(lineColor); 658 fillColor = JavaRendererUtilities.ARGBtoABGR(fillColor); 659 660 for (int i=0; i < altitudeDepthLength; i++) 661 { 662 // if it's a killbox, need to set minimum alt to 0. 663 if (symbolId.startsWith("AJP")) 664 { 665 attributes.X_ALTITUDE_DEPTH.add(0D); 666 i++; 667 } 668 attributes.X_ALTITUDE_DEPTH.add(Double.parseDouble(altitudeDepth[i])); 669 } 670 for (int i=0; i < distanceLength; i++) 671 { 672 // If this is a 'track' type graphic, then we need to take the distance 673 // and divide it by half, than add it twice. This is due 674 // to the TAIS requirement that Tracks must have a left width 675 // and a right width. 676 if (symbolId.equals("ACAR--") || // ACA - rectangular 677 symbolId.equals("AKPR--") || // Killbox - rectangular 678 symbolId.equals("ALC---") || // air corricor 679 symbolId.equals("ALM---") || // MRR 680 symbolId.equals("ALS---") || // SAAFR 681 symbolId.equals("ALU---") || // unmanned aircraft 682 symbolId.equals("ALL---")) { // LLTR) { 683 double width = Double.parseDouble(distance[i]) / 2; 684 attributes.AM_DISTANCE.add(width); 685 attributes.AM_DISTANCE.add(width); 686 } else { 687 attributes.AM_DISTANCE.add(Double.parseDouble(distance[i])); 688 } 689 } 690 691 if (symbolId.equals("ACAI--") || // ACA - irregular 692 symbolId.equals("AKPI--") || // Killbox - irregular 693 symbolId.equals("AAR---") || // ROZ 694 symbolId.equals("AAF---") || // SHORADEZ 695 symbolId.equals("AAH---") || // HIDACZ 696 symbolId.equals("AAM---") || // MEZ 697 symbolId.equals("AAML--") || // LOMEZ 698 symbolId.equals("AAMH--")) // HIMEZ 699 { 700 output = Shape3DHandler.buildPolygon(controlPoints, id, name, 701 description, lineColor, fillColor, convertedAltitudeMode, attributes); 702 } 703 else if (symbolId.equals("ACAR--") || // ACA - rectangular 704 symbolId.equals("AKPR--") || // Killbox - rectangular 705 symbolId.equals("ALC---") || // air corricor 706 symbolId.equals("ALM---") || // MRR 707 symbolId.equals("ALS---") || // SAAFR 708 symbolId.equals("ALU---") || // unmanned aircraft 709 symbolId.equals("ALL---")) // LLTR 710 { 711 output = Shape3DHandler.buildTrack(controlPoints, id, name, 712 description, lineColor, fillColor, convertedAltitudeMode, attributes); 713 } 714 else if (symbolId.equals("ACAC--") || // ACA - circular 715 symbolId.equals("AKPC--")) // Killbox - circular 716 { 717 output = Shape3DHandler.buildCylinder(controlPoints, id, name, 718 description, lineColor, fillColor, convertedAltitudeMode, attributes); 719 720 } 721 722 723 } 724 catch (Exception exc) 725 { 726 output = ""; 727 } 728 return output; 729 } 730 731 /** 732 * Renders all MilStd 2525 multi-point symbols, creating MilStdSymbol that contains the 733 * information needed to draw the symbol on the map. 734 * DOES NOT support RADARC, CAKE, TRACK etc... 735 * ArrayList<Point2D> milStdSymbol.getSymbolShapes.get(index).getPolylines() 736 * and 737 * ShapeInfo = milStdSymbol.getModifierShapes.get(index). 738 * 739 * 740 * @param id 741 * A unique identifier used to identify the symbol by Google map. 742 * The id will be the folder name that contains the graphic. 743 * @param name 744 * a string used to display to the user as the name of the 745 * graphic being created. 746 * @param description 747 * a brief description about the graphic being made and what it 748 * represents. 749 * @param symbolCode 750 * A 15 character symbolID corresponding to one of the graphics 751 * in the MIL-STD-2525C 752 * @param controlPoints 753 * The vertices of the graphics that make up the graphic. Passed 754 * in the format of a string, using decimal degrees separating 755 * lat and lon by a comma, separating coordinates by a space. The 756 * following format shall be used "x1,y1[,z1] [xn,yn[,zn]]..." 757 * @param altitudeMode 758 * Indicates whether the symbol should interpret altitudes as 759 * above sea level or above ground level. Options are 760 * "clampToGround", "relativeToGround" (from surface of earth), 761 * "absolute" (sea level), "relativeToSeaFloor" (from the bottom 762 * of major bodies of water). 763 * @param scale 764 * A number corresponding to how many meters one meter of our map 765 * represents. A value "50000" would mean 1:50K which means for 766 * every meter of our map it represents 50000 meters of real 767 * world distance. 768 * @param bbox 769 * The viewable area of the map. Passed in the format of a string 770 * "lowerLeftX,lowerLeftY,upperRightX,upperRightY." Not required 771 * but can speed up rendering in some cases. example: 772 * "-50.4,23.6,-42.2,24.2" 773 * @param modifiers 774 * Used like: 775 * modifiers.put(ModifiersTG.T_UNIQUE_DESIGNATION_1, "T"); 776 * Or 777 * modifiers.put(ModifiersTG.AM_DISTANCE, "1000,2000,3000"); 778 * @param attributes 779 * Used like: 780 * attributes.put(MilStdAttributes.LineWidth, "3"); 781 * Or 782 * attributes.put(MilStdAttributes.LineColor, "#00FF00"); 783 * @param symStd 784 * An enumeration: 0 for 2525Bch2, 1 for 2525C. 785 * @return MilStdSymbol 786 */ 787 public static MilStdSymbol RenderMultiPointAsMilStdSymbol(String id, String name, String description, String symbolCode, 788 String controlPoints, String altitudeMode, double scale, String bbox, SparseArray<String> modifiers, SparseArray<String> attributes, int symStd) 789 { 790 MilStdSymbol mSymbol = null; 791 try 792 { 793 mSymbol = MultiPointHandler.RenderSymbolAsMilStdSymbol(id, name, description, symbolCode, 794 controlPoints, scale, bbox, modifiers, attributes, symStd); 795 796 String basicID = SymbolUtilities.getBasicSymbolID(symbolCode); 797 if(basicID.charAt(0)=='G' && ((basicID.charAt(2)=='G' && basicID.substring(4,7).equals("PC-")) || (basicID.charAt(2)=='M' && basicID.substring(4,7).equals("OFD")))) 798 { 799 String A = "G*MPOMU---****X";//unspecified mine, default if not specified 800 if(modifiers.indexOfKey(ModifiersTG.A_SYMBOL_ICON) >= 0) 801 A = modifiers.get(ModifiersTG.A_SYMBOL_ICON); 802 803 //test 804 //A = "G*MPOMW---****X,G*MPOMD---****X,G*MPOME---****X"; 805 //A = "G*MPOMEXXX****X"; 806 int size = RendererSettings.getInstance().getDefaultPixelSize(); 807 808 ArrayList<ShapeInfo> shapes = mSymbol.getSymbolShapes(); 809 if(shapes.size() > 0){ 810 ShapeInfo shape = shapes.get(0); 811 shape.setPatternFillImage(PatternFillRenderer.MakeSymbolPatternFill(A,size)); 812 if(shape.getPatternFillImage() != null) 813 shape.setShader(new BitmapShader(shape.getPatternFillImage(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)); 814 } 815 } 816 else if(basicID.charAt(0) == 'W') 817 { 818 ArrayList<ShapeInfo> shapes = mSymbol.getSymbolShapes(); 819 if(shapes.size() > 0) 820 { 821 ShapeInfo shape = shapes.get(0); 822 shape.setPatternFillImage(PatternFillRenderer.MakeMetocPatternFill(symbolCode)); 823 if(shape.getPatternFillImage() != null) 824 shape.setShader(new BitmapShader(shape.getPatternFillImage(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)); 825 } 826 } 827 828 } 829 catch (Exception ea) 830 { 831 mSymbol=null; 832 ErrorLogger.LogException("SECRenderer", "RenderMultiPointAsMilStdSymbol" + " - " + symbolCode, ea, Level.WARNING); 833 } 834 835 //System.out.println("RenderMultiPointAsMilStdSymbol exit"); 836 return mSymbol; 837 } 838 839 840 841 /** 842 * Given a symbol code meant for a single point symbol, returns the 843 * anchor point at which to display that image based off the image returned 844 * from the URL of the SinglePointServer. 845 * 846 * @param symbolID - the 15 character symbolID of a single point MilStd2525 847 * symbol. 848 * @return A pixel coordinate of the format "x,y". 849 * Returns an empty string if an error occurs. 850 * @deprecated 851 */ 852 public String getSinglePointAnchor(String symbolID) { 853 String anchorPoint = ""; 854 Point2D anchor = new Point2D.Double(); 855 anchorPoint = anchor.getX() + "," + anchor.getY(); 856 return anchorPoint; 857 } 858 859 /** 860 * Given a symbol code meant for a single point symbol, returns the 861 * anchor point at which to display that image based off the image returned 862 * from the URL of the SinglePointServer. 863 * 864 * @param symbolID - the 15 character symbolID of a single point MilStd2525 865 * symbol. 866 * @return A pixel coordinate of the format "anchorX,anchorY,SymbolBoundsX, 867 * SymbolBoundsY,SymbolBoundsWidth,SymbolBoundsHeight,IconWidth,IconHeight". 868 * Anchor, represents the center point of the core symbol within the image. 869 * The image should be centered on this point. 870 * Symbol bounds represents the bounding rectangle of the core symbol within 871 * the image. 872 * IconWidth/Height represents the height and width of the image in its 873 * entirety. 874 * Returns an empty string if an error occurs. 875 */ 876 public static String getSinglePointInfo(String symbolID) 877 { 878 String info = ""; 879 Point2D anchor = new Point2D.Double(); 880 Rectangle2D symbolBounds = new Rectangle2D.Double(); 881 return info; 882 } 883 884 /** 885 * Returns true if we recommend clipping a particular symbol. 886 * Would return false for and Ambush but would return true for a Line of 887 * Contact due to the decoration on the line. 888 * @param symbolID 889 * @return 890 */ 891 public static String ShouldClipMultipointSymbol(String symbolID) 892 { 893 if(MultiPointHandler.ShouldClipSymbol(symbolID)) 894 return "true"; 895 else 896 return "false"; 897 } 898 899 /** 900 * Given a symbol code meant for a single point symbol, returns the 901 * symbol as a byte array. 902 * 903 * @param symbolID - the 15 character symbolID of a single point MilStd2525 904 * symbol. 905 * @return byte array. 906 */ 907 public static byte[] getSinglePointByteArray(String symbolID) 908 { 909 //return sps.getSinglePointByteArray(symbolID); 910 return null; 911 } 912 /** 913 * Put this here rather than in multipointhandler so that I could get the 914 * port info from the single point server. 915 * @param modifiers 916 * @param clip 917 * @return 918 */ 919 public static String GenerateSymbolLineFillUrl(SparseArray<String> modifiers, ArrayList<Point2D> pixels, Rectangle clip) 920 { 921 int shapeType = 0; 922 String url = ""; 923 String symbolFillIDs=null; 924 String symbolLineIDs=null; 925 String strClip=null; 926 //int symbolSize = AreaSymbolFill.DEFAULT_SYMBOL_SIZE; 927 int symbolSize = 25; 928 int imageoffset = 0; 929 ArrayList<ArrayList<Point2D>> lines = null; 930 ArrayList<Point2D> points = null; 931 Point2D point = null; 932 933 Shape shape = null; 934 //PathIterator itr = null; 935 double height = 0; 936 double width = 0; 937 int offsetX = 0; 938 int offsetY = 0; 939 int x = 0; 940 int y = 0; 941 //Rectangle2D bounds = null; 942 Rectangle bounds = null; 943 try 944 { 945 //Path2D path = new GeneralPath(); 946 GeneralPath path = new GeneralPath(); 947 Point2D temp = null; 948 //Get bounds of the polygon/polyline path 949 for(int i=0; i<pixels.size();i++) 950 { 951 temp = pixels.get(i); 952 if(i>0) 953 { 954 path.lineTo(temp.getX(), temp.getY()); 955 } 956 else if(i==0) 957 { 958 path.moveTo(temp.getX(), temp.getY()); 959 } 960 } 961 962 bounds = path.getBounds(); 963 height = bounds.getHeight(); 964 width = bounds.getWidth(); 965 966// System.out.println("bounds: "+ bounds.toString()); 967// System.out.println("height: "+ String.valueOf(height)); 968// System.out.println("width: "+ String.valueOf(width)); 969 970 //pixels may be in negative space so get offsets to put everything 971 //in the positive 972 if(bounds.getX()<0) 973 { 974 offsetX = (int)(bounds.getX()*-1); 975 } 976 else if((bounds.getX()+bounds.getWidth()) > width) 977 { 978 offsetX = (int)((bounds.getX()+bounds.getWidth())-width)*-1; 979 } 980 981 if(bounds.getY()<0) 982 { 983 offsetY = (int)(bounds.getY()*-1); 984 } 985 else if((bounds.getY()+bounds.getHeight()) > height) 986 { 987 offsetY = (int)((bounds.getY()+bounds.getHeight())-height)*-1; 988 } 989 990 //build clip string 991 if(clip!=null) 992 { 993 StringBuilder sbClip = new StringBuilder(); 994 sbClip.append("&clip="); 995 sbClip.append(Integer.toString((int)clip.getX())); 996 sbClip.append(","); 997 sbClip.append(Integer.toString((int)clip.getY())); 998 sbClip.append(","); 999 sbClip.append(Integer.toString((int)clip.getWidth())); 1000 sbClip.append(","); 1001 sbClip.append(Integer.toString((int)clip.getHeight())); 1002 strClip=sbClip.toString(); 1003 } 1004 1005 1006 StringBuilder sbCoords = new StringBuilder(); 1007 StringBuilder sbUrl = new StringBuilder(); 1008 sbCoords.append("coords="); 1009 1010 if (modifiers.indexOfKey(ModifiersTG.SYMBOL_FILL_IDS) >= 0) { 1011 symbolFillIDs = (String) modifiers.get(ModifiersTG.SYMBOL_FILL_IDS); 1012 } 1013 1014 //build coordinate string 1015 for(int i = 0; i< pixels.size(); i++) 1016 { 1017 if(i>0) 1018 { 1019 sbCoords.append(","); 1020 } 1021 point = pixels.get(i); 1022 x = (int)(point.getX() + offsetX); 1023 y = (int)(point.getY() + offsetY); 1024 sbCoords.append(Integer.toString(x)); 1025 sbCoords.append(","); 1026 sbCoords.append(Integer.toString(y)); 1027 } 1028 1029 //build image url 1030 sbUrl.append("http://127.0.0.1:"); 1031 //sbUrl.append(String.valueOf(spsPortNumber)); 1032 sbUrl.append("6789"); 1033 sbUrl.append("/AREASYMBOLFILL?"); 1034 sbUrl.append("renderer=AreaSymbolFillRenderer&"); 1035 sbUrl.append(sbCoords.toString()); 1036 if(symbolFillIDs != null) 1037 { 1038 sbUrl.append("&symbolFillIds="); 1039 sbUrl.append(symbolFillIDs); 1040 } 1041 if(symbolLineIDs != null) 1042 { 1043 sbUrl.append("&symbolLineIds="); 1044 sbUrl.append(symbolLineIDs); 1045 } 1046 if(symbolSize>0) 1047 { 1048 sbUrl.append("&symbolFillIconSize="); sbUrl.append(Integer.toString(symbolSize)); 1049 } 1050 if(strClip!=null) 1051 { 1052 sbUrl.append(strClip); 1053 } 1054 1055 1056 1057 sbUrl.append("&height="); 1058 //sbUrl.append(Integer.valueOf((int)height)); 1059 sbUrl.append(Integer.toString((int)height)); 1060 sbUrl.append("&width="); 1061 //sbUrl.append(Integer.valueOf((int)width)); 1062 sbUrl.append(Integer.toString((int)width)); 1063 1064 url = sbUrl.toString(); 1065 1066 } 1067 catch(Exception exc) 1068 { 1069 System.out.println(exc.getMessage()); 1070 exc.printStackTrace(); 1071 } 1072 return url; 1073 } 1074 1075}