001/* 002 * Copyright (c) 2014-2015 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2018-09-06 23:20:04 +0000 (Thu, 06 Sep 2018) $' 007 * '$Revision: 34710 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029package org.kepler.gis.actor.kml; 030 031import java.io.FileWriter; 032import java.io.IOException; 033import java.io.StringWriter; 034import java.io.Writer; 035import java.util.Date; 036import java.util.HashMap; 037import java.util.LinkedList; 038import java.util.List; 039import java.util.Map; 040 041import org.geotools.data.simple.SimpleFeatureCollection; 042import org.geotools.data.simple.SimpleFeatureIterator; 043import org.geotools.geometry.jts.JTS; 044import org.geotools.referencing.CRS; 045import org.geotools.referencing.crs.DefaultGeographicCRS; 046import org.kepler.gis.data.VectorToken; 047import org.kepler.gis.util.GISUtilities; 048import org.opengis.feature.Property; 049import org.opengis.feature.simple.SimpleFeature; 050import org.opengis.feature.simple.SimpleFeatureType; 051import org.opengis.geometry.MismatchedDimensionException; 052import org.opengis.referencing.FactoryException; 053import org.opengis.referencing.crs.CoordinateReferenceSystem; 054import org.opengis.referencing.operation.MathTransform; 055import org.opengis.referencing.operation.TransformException; 056 057import com.vividsolutions.jts.geom.Coordinate; 058import com.vividsolutions.jts.geom.Geometry; 059import com.vividsolutions.jts.geom.LineString; 060import com.vividsolutions.jts.geom.MultiPolygon; 061import com.vividsolutions.jts.geom.Point; 062import com.vividsolutions.jts.geom.Polygon; 063 064import de.micromata.opengis.kml.v_2_2_0.ColorMode; 065import de.micromata.opengis.kml.v_2_2_0.Data; 066import de.micromata.opengis.kml.v_2_2_0.Document; 067import de.micromata.opengis.kml.v_2_2_0.Folder; 068import de.micromata.opengis.kml.v_2_2_0.Kml; 069import de.micromata.opengis.kml.v_2_2_0.KmlFactory; 070import de.micromata.opengis.kml.v_2_2_0.LinearRing; 071import de.micromata.opengis.kml.v_2_2_0.MultiGeometry; 072import de.micromata.opengis.kml.v_2_2_0.Placemark; 073import de.micromata.opengis.kml.v_2_2_0.Style; 074import ptolemy.actor.TypedAtomicActor; 075import ptolemy.actor.TypedIOPort; 076import ptolemy.actor.parameters.ParameterPort; 077import ptolemy.actor.parameters.PortParameter; 078import ptolemy.data.ArrayToken; 079import ptolemy.data.BooleanToken; 080import ptolemy.data.DateToken; 081import ptolemy.data.StringToken; 082import ptolemy.data.Token; 083import ptolemy.data.UnsignedByteToken; 084import ptolemy.data.expr.Parameter; 085import ptolemy.data.expr.StringParameter; 086import ptolemy.data.type.ArrayType; 087import ptolemy.data.type.BaseType; 088import ptolemy.kernel.CompositeEntity; 089import ptolemy.kernel.util.Attribute; 090import ptolemy.kernel.util.IllegalActionException; 091import ptolemy.kernel.util.NameDuplicationException; 092import ptolemy.kernel.util.SingletonAttribute; 093 094/** An actor that writes a vector/feature data set to a KML file. 095 * An input port must be added for each data set to read. The features 096 * will be placed in the KML inside a folder with the same name as 097 * the input port. 098 * <p> 099 * If a feature contains a property called "name", then the value 100 * will be used to specify the name of the KML placemark for that feature. 101 * </p> 102 * <p> 103 * If a feature contains a property called "color", then the value 104 * will be used to specify the color of the KML placemark for that feature. 105 * </p> 106 * <p> 107 * If a feature contains a property called "timestamp" and a timestamp is not 108 * supplied to the <i>date</i> port, then the value in the property will be 109 * used to specify the timestamp of the KML placemark for that feature. The 110 * format for KML timestamps is: yyyy-MM-dd'T'HH:mmXXX. 111 * </p> 112 * 113 * @author Daniel Crawl 114 * @version $Id: KMLWriter.java 34710 2018-09-06 23:20:04Z crawl $ 115 */ 116public class KMLWriter extends TypedAtomicActor { 117 118 public KMLWriter(CompositeEntity container, String name) 119 throws IllegalActionException, NameDuplicationException { 120 121 super(container, name); 122 123 outputType = new StringParameter(this, "outputType"); 124 outputType.addChoice("file"); 125 outputType.addChoice("text"); 126 outputType.addChoice("binary"); 127 outputType.setToken("file"); 128 129 filename = new PortParameter(this, "filename"); 130 filename.setTypeEquals(BaseType.STRING); 131 filename.getPort().setTypeEquals(BaseType.STRING); 132 filename.setStringMode(true); 133 new SingletonAttribute(filename.getPort(), "_showName"); 134 filename.setToken("output.kml"); 135 136 date = new TypedIOPort(this, "date", true, false); 137 date.setTypeEquals(BaseType.DATE); 138 new SingletonAttribute(date, "_showName"); 139 140 folderName = new PortParameter(this, "folderName"); 141 folderName.setTypeEquals(BaseType.STRING); 142 folderName.getPort().setTypeEquals(BaseType.STRING); 143 folderName.setStringMode(true); 144 new SingletonAttribute(folderName.getPort(), "_showName"); 145 146 fillPolygons = new Parameter(this, "fillPolygons"); 147 fillPolygons.setTypeEquals(BaseType.BOOLEAN); 148 fillPolygons.setToken(BooleanToken.TRUE); 149 150 placemarkNameProperty = new PortParameter(this, "placemarkNameProperty"); 151 placemarkNameProperty.setStringMode(true); 152 placemarkNameProperty.setTypeEquals(BaseType.STRING); 153 placemarkNameProperty.getPort().setTypeEquals(BaseType.STRING); 154 new SingletonAttribute(placemarkNameProperty.getPort(), "_showName"); 155 156 groupNameProperty = new PortParameter(this, "groupNameProperty"); 157 groupNameProperty.setStringMode(true); 158 groupNameProperty.setTypeEquals(BaseType.STRING); 159 groupNameProperty.getPort().setTypeEquals(BaseType.STRING); 160 new SingletonAttribute(groupNameProperty.getPort(), "_showName"); 161 162 output = new TypedIOPort(this, "output", false, true); 163 } 164 165 @Override 166 public void attributeChanged(Attribute attribute) throws IllegalActionException { 167 if(attribute == outputType) { 168 StringToken token = (StringToken) outputType.getToken(); 169 if(token != null) { 170 String val = ((StringToken)token).stringValue(); 171 if(val.trim().isEmpty()) { 172 _outputTypeStr = "file"; 173 } else if(_outputTypeStr != null && 174 !_outputTypeStr.equals("file") && 175 !_outputTypeStr.equals("text") && 176 !_outputTypeStr.equals("binary")) { 177 throw new IllegalActionException(this, "Unsupported type of output: " + val); 178 } else { 179 _outputTypeStr = val; 180 } 181 } 182 } else { 183 super.attributeChanged(attribute); 184 } 185 } 186 187 @Override 188 public void fire() throws IllegalActionException { 189 190 super.fire(); 191 192 filename.update(); 193 194 String filenameStr = null; 195 if(_outputTypeStr.equals("file")) { 196 filenameStr = ((StringToken)filename.getToken()).stringValue(); 197 if(filenameStr.trim().isEmpty()) { 198 throw new IllegalActionException(this, "Must specify output file."); 199 } 200 } 201 202 placemarkNameProperty.update(); 203 204 _placemarkNameProperty = null; 205 Token token = placemarkNameProperty.getToken(); 206 if(token != null) { 207 _placemarkNameProperty = ((StringToken)token).stringValue().trim(); 208 } 209 210 groupNameProperty.update(); 211 212 _groupNameProperty = null; 213 token = groupNameProperty.getToken(); 214 if(token != null) { 215 _groupNameProperty = ((StringToken)token).stringValue().trim(); 216 if(_groupNameProperty.isEmpty()) { 217 _groupNameProperty = null; 218 } 219 } 220 221 222 Writer writer = null; 223 try { 224 if(_outputTypeStr.equals("file")) { 225 writer = new FileWriter(filenameStr); 226 } else if(_outputTypeStr.equals("text") || _outputTypeStr.equals("binary")) { 227 writer = new StringWriter(); 228 } else { 229 throw new IllegalActionException(this, "Unsupported type of output: " + _outputTypeStr); 230 } 231 232 233 folderName.update(); 234 String folderNameStr = null; 235 StringToken stringToken = (StringToken) folderName.getToken(); 236 if(stringToken != null) { 237 folderNameStr = stringToken.stringValue(); 238 } 239 240 String formattedDateStr = null; 241 DateToken dateToken = null; 242 if(date.numberOfSources() > 0) { 243 dateToken = (DateToken)date.get(0); 244 245 if(dateToken == null) { 246 //System.out.println("WARNING: no timestamp."); 247 } else { 248 //String whenStr = String.format("%02d-%02d-%02d", year, mon, day); 249 250 Date dateVal = new Date(dateToken.getValue()); 251 synchronized(WriteTimestampToKML.KML_TIMESTAMP_FORMAT) { 252 formattedDateStr = WriteTimestampToKML.KML_TIMESTAMP_FORMAT.format(dateVal); 253 } 254 } 255 } 256 257 final Kml kml = new Kml(); 258 final Document doc = kml.createAndSetDocument(); 259 260 // only create a top level folder if a name is specified. 261 Folder topFolder = null; 262 if(folderNameStr != null && !folderNameStr.trim().isEmpty()) { 263 topFolder = doc.createAndAddFolder(); 264 topFolder.setName(folderNameStr); 265 } 266 267 // add the features read by each input port into a separate 268 // folder 269 for(TypedIOPort port : inputPortList()) { 270 // do not use the class-specific input ports 271 if(port != date && !(port instanceof ParameterPort)) { 272 273 SimpleFeatureCollection features = ((VectorToken)port.get(0)).getVectors(); 274 275 // create the folder with the port name. 276 Folder portFolder; 277 if(topFolder != null) { 278 portFolder = topFolder.createAndAddFolder(); 279 } else { 280 portFolder = doc.createAndAddFolder(); 281 } 282 portFolder.setName(port.getName()); 283 _addToFolder(portFolder, features, formattedDateStr); 284 } 285 } 286 287 //kml.marshal(System.out); 288 kml.marshal(writer); 289 290 if(_outputTypeStr.equals("file")) { 291 output.broadcast(new StringToken(filenameStr)); 292 } else if(_outputTypeStr.equals("text")) { 293 output.broadcast(new StringToken(writer.toString())); 294 } else if(_outputTypeStr.equals("binary")) { 295 byte[] bytes = writer.toString().getBytes("UTF-8"); 296 Token[] tokens = new Token[bytes.length]; 297 for(int i = 0; i < bytes.length; i++) { 298 tokens[i] = new UnsignedByteToken(bytes[i]); 299 } 300 output.broadcast(new ArrayToken(tokens)); 301 } 302 303 } catch(IOException e) { 304 throw new IllegalActionException(this, e, "Error writing KML."); 305 } finally { 306 if(writer != null) { 307 try { 308 writer.close(); 309 } catch(IOException e) { 310 throw new IllegalActionException(this, e, "Error writing KML."); 311 } 312 } 313 } 314 } 315 316 /** Set the input ports to vector. */ 317 @Override 318 public void preinitialize() throws IllegalActionException { 319 super.preinitialize(); 320 321 for(TypedIOPort port: inputPortList()) { 322 if(port != date && !(port instanceof ParameterPort)) { 323 port.setTypeEquals(VectorToken.VECTOR); 324 } 325 } 326 327 if(_outputTypeStr.equals("file") || _outputTypeStr.equals("text")) { 328 output.setTypeEquals(BaseType.STRING); 329 } else if(_outputTypeStr.equals("binary")) { 330 output.setTypeEquals(new ArrayType(BaseType.UNSIGNED_BYTE)); 331 } 332 333 Token token = fillPolygons.getToken(); 334 if(token == null) { 335 _fillPolygons = true; 336 } else { 337 _fillPolygons = ((BooleanToken)token).booleanValue(); 338 } 339 340 } 341 342 //////////////////////////////////////////////////////////////////////// 343 //// public fields //// 344 345 /** The output type: file, text, or binary. If type if file, 346 * KML is written to a file with the name specified in <i>filename</i>, 347 * and <i>output</i> is a true boolean token. If type is text, 348 * KML is written as a string to <i>output</i>. Otherwise if type is 349 * binary, KML is written as an unsigned byte array to <i>output</i>. 350 */ 351 public StringParameter outputType; 352 353 /** The name of the output file. Only required when <i>outputType</i> is file. */ 354 public PortParameter filename; 355 356 /** The date to use for the timestamp of each feature. */ 357 public TypedIOPort date; 358 359 /** The name of the top level folder containing the features in 360 * the output KML file. 361 */ 362 public PortParameter folderName; 363 364 /** The name of the output file is sent to this port when the 365 * data has been written. 366 */ 367 public TypedIOPort output; 368 369 /** If true, polygons are filled. */ 370 public Parameter fillPolygons; 371 372 /** The name of the property specifying the placemark name. */ 373 public PortParameter placemarkNameProperty; 374 375 /** The name of the property specifying the group name. */ 376 public PortParameter groupNameProperty; 377 378 379 //////////////////////////////////////////////////////////////////////// 380 //// private methods //// 381 382 /** Add a set of features to a folder. */ 383 private void _addToFolder(Folder folder, SimpleFeatureCollection features, 384 String formattedDateStr) throws IllegalActionException { 385 386 SimpleFeatureType currentSchema = features.getSchema(); 387 CoordinateReferenceSystem srcCRS = currentSchema.getCoordinateReferenceSystem(); 388 389 if(srcCRS == null) { 390 //throw new IllegalActionException(this, "Unknown input CRS."); 391 System.out.println("WARNING: Unknown input CRS; assuming WGS84."); 392 srcCRS = DefaultGeographicCRS.WGS84; 393 } 394 395 MathTransform transform = null; 396 if(!GISUtilities.isCRSWGS84(srcCRS)) { 397 System.out.println(getName() + ": source projection is not WGS84"); 398 try { 399 transform = CRS.findMathTransform(srcCRS, DefaultGeographicCRS.WGS84, true); 400 } catch (FactoryException e) { 401 throw new IllegalActionException(this, e, 402 "Unable to find math transform."); 403 } 404 } 405 406 // if group name is set, find the groups and create folders for each. 407 Map<String,Folder> groupFolders = new HashMap<String,Folder>(); 408 if(_groupNameProperty != null) { 409 try(SimpleFeatureIterator iterator = features.features();) { 410 411 while(iterator.hasNext()) { 412 413 SimpleFeature feature = iterator.next(); 414 Property property = feature.getProperty(_groupNameProperty); 415 if(property == null) { 416 System.err.println("WARNING: missing group " + 417 _groupNameProperty + " in property"); 418 } else if(!groupFolders.containsKey(property.getValue().toString())) { 419 Folder groupFolder = folder.createAndAddFolder(); 420 groupFolder.setName(property.getValue().toString()); 421 groupFolders.put(property.getValue().toString(), groupFolder); 422 } 423 } 424 } 425 426 } 427 428 try(SimpleFeatureIterator iterator = features.features();) { 429 430 while(iterator.hasNext()) { 431 432 SimpleFeature feature = iterator.next(); 433 434 Geometry geometry = (Geometry) feature.getDefaultGeometry(); 435 Geometry newGeometry = geometry; 436 if(transform != null) { 437 try { 438 newGeometry = JTS.transform(geometry, transform); 439 } catch (MismatchedDimensionException | TransformException e) { 440 throw new IllegalActionException(this, e, 441 "Error transforming geometry."); 442 } 443 } 444 445 Folder containerFolder = null; 446 if(_groupNameProperty != null) { 447 Property property = feature.getProperty(_groupNameProperty); 448 if(property != null) { 449 containerFolder = groupFolders.get(property.getValue().toString()); 450 } 451 } 452 453 if(containerFolder == null) { 454 containerFolder = folder; 455 } 456 457 Placemark placemark = containerFolder.createAndAddPlacemark(); 458 459 if(newGeometry instanceof MultiPolygon) { 460 461 MultiGeometry multigeometry = placemark.createAndSetMultiGeometry(); 462 463 for(int i = 0; i < newGeometry.getNumGeometries(); i++) { 464 Geometry subGeometry = newGeometry.getGeometryN(i); 465 466 if(subGeometry instanceof Polygon) { 467 _addPolygon((Polygon)subGeometry, 468 multigeometry.createAndAddPolygon()); 469 } else if(subGeometry instanceof Point) { 470 _addPoint((Point)subGeometry, 471 multigeometry.createAndAddPoint()); 472 } else if(subGeometry instanceof LineString) { 473 _addLineString((LineString)subGeometry, 474 multigeometry.createAndAddLineString()); 475 } else { 476 throw new IllegalActionException(this, 477 "Unsupported type of geometry: " + 478 subGeometry.getClass()); 479 } 480 } 481 } else if(newGeometry instanceof Polygon) { 482 _addPolygon((Polygon)newGeometry, placemark.createAndSetPolygon()); 483 } else if(newGeometry instanceof Point) { 484 _addPoint((Point)newGeometry, placemark.createAndSetPoint()); 485 } else if(newGeometry instanceof LineString) { 486 _addLineString((LineString)newGeometry, 487 placemark.createAndSetLineString()); 488 } else { 489 throw new IllegalActionException(this, 490 "Unsupported type of geometry: " + newGeometry.getClass()); 491 } 492 493 String colorStr = "ff0000ff"; 494 String curTimeStampStr = null; 495 496 // TODO sort names 497 List<Data> dataList = new LinkedList<Data>(); 498 499 for(Property property : feature.getProperties()) { 500 String propertyNameStr = property.getName().toString(); 501 Object value = property.getValue(); 502 String valueStr = value.toString(); 503 if(!(value instanceof Geometry) && value != null) { 504 if(propertyNameStr.toLowerCase().equals("name")) { 505 placemark.setName(valueStr); 506 } else { 507 dataList.add(KmlFactory.createData(valueStr) 508 .withName(propertyNameStr)); 509 } 510 } 511 512 if(propertyNameStr.toLowerCase().equals("name") || 513 (_placemarkNameProperty != null && 514 _placemarkNameProperty.equals(propertyNameStr))) { 515 placemark.setName(valueStr); 516 } 517 518 if(propertyNameStr.equals("color")) { 519 colorStr = _getColorHex(valueStr); 520 } 521 522 if(formattedDateStr == null && propertyNameStr.equals("timestamp")) { 523 curTimeStampStr = valueStr; 524 } 525 526 } 527 placemark.createAndSetExtendedData().setData(dataList); 528 529 // TODO choose time 530 /* 531 int year = _parseDateObject(feature.getAttribute("YEAR_")); 532 int mon = _parseDateObject(feature.getAttribute("MONTH_")); 533 if(mon == 0) { 534 mon = 1; 535 } 536 int day = _parseDateObject(feature.getAttribute("DAY_")); 537 if(day == 0) { 538 day = 1; 539 } 540 */ 541 542 if(formattedDateStr != null) { 543 placemark.createAndSetTimeStamp().setWhen(formattedDateStr); 544 } else if(curTimeStampStr != null) { 545 placemark.createAndSetTimeStamp().setWhen(curTimeStampStr); 546 } 547 548 Style style = placemark.createAndAddStyle(); 549 if(_fillPolygons) { 550 style.createAndSetPolyStyle() 551 .withColorMode(ColorMode.NORMAL).setColor(colorStr); 552 } else { 553 style.createAndSetPolyStyle().setFill(false); 554 style.createAndSetLineStyle().withWidth(5) 555 .withColorMode(ColorMode.NORMAL).setColor(colorStr); 556 } 557 558 } 559 } 560 } 561 562 private void _addLineString(LineString lineString, de.micromata.opengis.kml.v_2_2_0.LineString kmlLineString) { 563 for(Coordinate coordinate : lineString.getCoordinates()) { 564 kmlLineString.addToCoordinates(coordinate.x, coordinate.y, 0); 565 } 566 } 567 568 private void _addPolygon(Polygon polygon, de.micromata.opengis.kml.v_2_2_0.Polygon kmlPolygon) { 569 570 //System.out.println("add polygon"); 571 572 // convert exterior boundary 573 final LineString exterior = polygon.getExteriorRing(); 574 575 final LinearRing kmlExterior = KmlFactory.createLinearRing(); 576 577 for(Coordinate coordinate : exterior.getCoordinates()) { 578 kmlExterior.addToCoordinates(coordinate.x, coordinate.y, 0); 579 } 580 581 kmlPolygon.setOuterBoundaryIs(KmlFactory.createBoundary().withLinearRing(kmlExterior)); 582 583 // convert any interior boundaries 584 for(int j = 0; j < polygon.getNumInteriorRing(); j++) { 585 586 final LineString interior = polygon.getInteriorRingN(j); 587 588 final LinearRing kmlInterior = KmlFactory.createLinearRing(); 589 590 for(Coordinate coordinate : interior.getCoordinates()) { 591 kmlInterior.addToCoordinates(coordinate.x, coordinate.y, 0); 592 } 593 594 kmlPolygon.addToInnerBoundaryIs(KmlFactory.createBoundary().withLinearRing(kmlInterior)); 595 596 } 597 } 598 599 private void _addPoint(Point point, de.micromata.opengis.kml.v_2_2_0.Point kmlPoint) { 600 601 //System.out.println("add point"); 602 603 for(Coordinate coordinate : point.getCoordinates()) { 604 // TODO this is reversed for geojson 605 //kmlPoint.addToCoordinates(coordinate.y, coordinate.x, 0);//coordinate.z); 606 kmlPoint.addToCoordinates(coordinate.x, coordinate.y, 0);//coordinate.z); 607 } 608 609 } 610 611 /* 612 private static int _parseDateObject(Object value) { 613 if(value instanceof Double) { 614 return ((Double)value).intValue(); 615 } 616 return Integer.valueOf(value.toString()); 617 } 618 */ 619 620 /** Get the KML color string from a color name. */ 621 private static String _getColorHex(String name) { 622 int[] rgb = _colorMap.get(name); 623 if(rgb == null) { 624 return "ffffffff"; 625 } 626 return String.format("ff%02x%02x%02x", 627 rgb[ColorIndex.Blue.ordinal()], 628 rgb[ColorIndex.Green.ordinal()], 629 rgb[ColorIndex.Red.ordinal()]).toLowerCase(); 630 } 631 632 /** Type of output to write. */ 633 private String _outputTypeStr; 634 635 /** If true, polygons are filled. */ 636 private boolean _fillPolygons; 637 638 /** The name of the property specifying the placemark name. */ 639 private String _placemarkNameProperty; 640 641 /** The name of the property specifying the group name. */ 642 private String _groupNameProperty; 643 644 /** Order of colors in colorMap. */ 645 private enum ColorIndex {Red, Green, Blue}; 646 647 /** Mapping of HTML/CSS color names to RGB values. */ 648 private final static Map<String,int[]> _colorMap = new HashMap<String,int[]>(); 649 650 static { 651 // from https://gist.github.com/XiaoxiaoLi/8031146 652 _colorMap.put("AliceBlue".toLowerCase(), new int[] {0xF0, 0xF8, 0xFF}); 653 _colorMap.put("AntiqueWhite".toLowerCase(), new int[] {0xFA, 0xEB, 0xD7}); 654 _colorMap.put("Aqua".toLowerCase(), new int[] {0x00, 0xFF, 0xFF}); 655 _colorMap.put("Aquamarine".toLowerCase(), new int[] {0x7F, 0xFF, 0xD4}); 656 _colorMap.put("Azure".toLowerCase(), new int[] {0xF0, 0xFF, 0xFF}); 657 _colorMap.put("Beige".toLowerCase(), new int[] {0xF5, 0xF5, 0xDC}); 658 _colorMap.put("Bisque".toLowerCase(), new int[] {0xFF, 0xE4, 0xC4}); 659 _colorMap.put("Black".toLowerCase(), new int[] {0x00, 0x00, 0x00}); 660 _colorMap.put("BlanchedAlmond".toLowerCase(), new int[] {0xFF, 0xEB, 0xCD}); 661 _colorMap.put("Blue".toLowerCase(), new int[] {0x00, 0x00, 0xFF}); 662 _colorMap.put("BlueViolet".toLowerCase(), new int[] {0x8A, 0x2B, 0xE2}); 663 _colorMap.put("Brown".toLowerCase(), new int[] {0xA5, 0x2A, 0x2A}); 664 _colorMap.put("BurlyWood".toLowerCase(), new int[] {0xDE, 0xB8, 0x87}); 665 _colorMap.put("CadetBlue".toLowerCase(), new int[] {0x5F, 0x9E, 0xA0}); 666 _colorMap.put("Chartreuse".toLowerCase(), new int[] {0x7F, 0xFF, 0x00}); 667 _colorMap.put("Chocolate".toLowerCase(), new int[] {0xD2, 0x69, 0x1E}); 668 _colorMap.put("Coral".toLowerCase(), new int[] {0xFF, 0x7F, 0x50}); 669 _colorMap.put("CornflowerBlue".toLowerCase(), new int[] {0x64, 0x95, 0xED}); 670 _colorMap.put("Cornsilk".toLowerCase(), new int[] {0xFF, 0xF8, 0xDC}); 671 _colorMap.put("Crimson".toLowerCase(), new int[] {0xDC, 0x14, 0x3C}); 672 _colorMap.put("Cyan".toLowerCase(), new int[] {0x00, 0xFF, 0xFF}); 673 _colorMap.put("DarkBlue".toLowerCase(), new int[] {0x00, 0x00, 0x8B}); 674 _colorMap.put("DarkCyan".toLowerCase(), new int[] {0x00, 0x8B, 0x8B}); 675 _colorMap.put("DarkGoldenRod".toLowerCase(), new int[] {0xB8, 0x86, 0x0B}); 676 _colorMap.put("DarkGray".toLowerCase(), new int[] {0xA9, 0xA9, 0xA9}); 677 _colorMap.put("DarkGreen".toLowerCase(), new int[] {0x00, 0x64, 0x00}); 678 _colorMap.put("DarkKhaki".toLowerCase(), new int[] {0xBD, 0xB7, 0x6B}); 679 _colorMap.put("DarkMagenta".toLowerCase(), new int[] {0x8B, 0x00, 0x8B}); 680 _colorMap.put("DarkOliveGreen".toLowerCase(), new int[] {0x55, 0x6B, 0x2F}); 681 _colorMap.put("DarkOrange".toLowerCase(), new int[] {0xFF, 0x8C, 0x00}); 682 _colorMap.put("DarkOrchid".toLowerCase(), new int[] {0x99, 0x32, 0xCC}); 683 _colorMap.put("DarkRed".toLowerCase(), new int[] {0x8B, 0x00, 0x00}); 684 _colorMap.put("DarkSalmon".toLowerCase(), new int[] {0xE9, 0x96, 0x7A}); 685 _colorMap.put("DarkSeaGreen".toLowerCase(), new int[] {0x8F, 0xBC, 0x8F}); 686 _colorMap.put("DarkSlateBlue".toLowerCase(), new int[] {0x48, 0x3D, 0x8B}); 687 _colorMap.put("DarkSlateGray".toLowerCase(), new int[] {0x2F, 0x4F, 0x4F}); 688 _colorMap.put("DarkTurquoise".toLowerCase(), new int[] {0x00, 0xCE, 0xD1}); 689 _colorMap.put("DarkViolet".toLowerCase(), new int[] {0x94, 0x00, 0xD3}); 690 _colorMap.put("DeepPink".toLowerCase(), new int[] {0xFF, 0x14, 0x93}); 691 _colorMap.put("DeepSkyBlue".toLowerCase(), new int[] {0x00, 0xBF, 0xFF}); 692 _colorMap.put("DimGray".toLowerCase(), new int[] {0x69, 0x69, 0x69}); 693 _colorMap.put("DodgerBlue".toLowerCase(), new int[] {0x1E, 0x90, 0xFF}); 694 _colorMap.put("FireBrick".toLowerCase(), new int[] {0xB2, 0x22, 0x22}); 695 _colorMap.put("FloralWhite".toLowerCase(), new int[] {0xFF, 0xFA, 0xF0}); 696 _colorMap.put("ForestGreen".toLowerCase(), new int[] {0x22, 0x8B, 0x22}); 697 _colorMap.put("Fuchsia".toLowerCase(), new int[] {0xFF, 0x00, 0xFF}); 698 _colorMap.put("Gainsboro".toLowerCase(), new int[] {0xDC, 0xDC, 0xDC}); 699 _colorMap.put("GhostWhite".toLowerCase(), new int[] {0xF8, 0xF8, 0xFF}); 700 _colorMap.put("Gold".toLowerCase(), new int[] {0xFF, 0xD7, 0x00}); 701 _colorMap.put("GoldenRod".toLowerCase(), new int[] {0xDA, 0xA5, 0x20}); 702 _colorMap.put("Gray".toLowerCase(), new int[] {0x80, 0x80, 0x80}); 703 _colorMap.put("Green".toLowerCase(), new int[] {0x00, 0x80, 0x00}); 704 _colorMap.put("GreenYellow".toLowerCase(), new int[] {0xAD, 0xFF, 0x2F}); 705 _colorMap.put("HoneyDew".toLowerCase(), new int[] {0xF0, 0xFF, 0xF0}); 706 _colorMap.put("HotPink".toLowerCase(), new int[] {0xFF, 0x69, 0xB4}); 707 _colorMap.put("IndianRed".toLowerCase(), new int[] {0xCD, 0x5C, 0x5C}); 708 _colorMap.put("Indigo".toLowerCase(), new int[] {0x4B, 0x00, 0x82}); 709 _colorMap.put("Ivory".toLowerCase(), new int[] {0xFF, 0xFF, 0xF0}); 710 _colorMap.put("Khaki".toLowerCase(), new int[] {0xF0, 0xE6, 0x8C}); 711 _colorMap.put("Lavender".toLowerCase(), new int[] {0xE6, 0xE6, 0xFA}); 712 _colorMap.put("LavenderBlush".toLowerCase(), new int[] {0xFF, 0xF0, 0xF5}); 713 _colorMap.put("LawnGreen".toLowerCase(), new int[] {0x7C, 0xFC, 0x00}); 714 _colorMap.put("LemonChiffon".toLowerCase(), new int[] {0xFF, 0xFA, 0xCD}); 715 _colorMap.put("LightBlue".toLowerCase(), new int[] {0xAD, 0xD8, 0xE6}); 716 _colorMap.put("LightCoral".toLowerCase(), new int[] {0xF0, 0x80, 0x80}); 717 _colorMap.put("LightCyan".toLowerCase(), new int[] {0xE0, 0xFF, 0xFF}); 718 _colorMap.put("LightGoldenRodYellow".toLowerCase(), new int[] {0xFA, 0xFA, 0xD2}); 719 _colorMap.put("LightGray".toLowerCase(), new int[] {0xD3, 0xD3, 0xD3}); 720 _colorMap.put("LightGreen".toLowerCase(), new int[] {0x90, 0xEE, 0x90}); 721 _colorMap.put("LightPink".toLowerCase(), new int[] {0xFF, 0xB6, 0xC1}); 722 _colorMap.put("LightSalmon".toLowerCase(), new int[] {0xFF, 0xA0, 0x7A}); 723 _colorMap.put("LightSeaGreen".toLowerCase(), new int[] {0x20, 0xB2, 0xAA}); 724 _colorMap.put("LightSkyBlue".toLowerCase(), new int[] {0x87, 0xCE, 0xFA}); 725 _colorMap.put("LightSlateGray".toLowerCase(), new int[] {0x77, 0x88, 0x99}); 726 _colorMap.put("LightSteelBlue".toLowerCase(), new int[] {0xB0, 0xC4, 0xDE}); 727 _colorMap.put("LightYellow".toLowerCase(), new int[] {0xFF, 0xFF, 0xE0}); 728 _colorMap.put("Lime".toLowerCase(), new int[] {0x00, 0xFF, 0x00}); 729 _colorMap.put("LimeGreen".toLowerCase(), new int[] {0x32, 0xCD, 0x32}); 730 _colorMap.put("Linen".toLowerCase(), new int[] {0xFA, 0xF0, 0xE6}); 731 _colorMap.put("Magenta".toLowerCase(), new int[] {0xFF, 0x00, 0xFF}); 732 _colorMap.put("Maroon".toLowerCase(), new int[] {0x80, 0x00, 0x00}); 733 _colorMap.put("MediumAquaMarine".toLowerCase(), new int[] {0x66, 0xCD, 0xAA}); 734 _colorMap.put("MediumBlue".toLowerCase(), new int[] {0x00, 0x00, 0xCD}); 735 _colorMap.put("MediumOrchid".toLowerCase(), new int[] {0xBA, 0x55, 0xD3}); 736 _colorMap.put("MediumPurple".toLowerCase(), new int[] {0x93, 0x70, 0xDB}); 737 _colorMap.put("MediumSeaGreen".toLowerCase(), new int[] {0x3C, 0xB3, 0x71}); 738 _colorMap.put("MediumSlateBlue".toLowerCase(), new int[] {0x7B, 0x68, 0xEE}); 739 _colorMap.put("MediumSpringGreen".toLowerCase(), new int[] {0x00, 0xFA, 0x9A}); 740 _colorMap.put("MediumTurquoise".toLowerCase(), new int[] {0x48, 0xD1, 0xCC}); 741 _colorMap.put("MediumVioletRed".toLowerCase(), new int[] {0xC7, 0x15, 0x85}); 742 _colorMap.put("MidnightBlue".toLowerCase(), new int[] {0x19, 0x19, 0x70}); 743 _colorMap.put("MintCream".toLowerCase(), new int[] {0xF5, 0xFF, 0xFA}); 744 _colorMap.put("MistyRose".toLowerCase(), new int[] {0xFF, 0xE4, 0xE1}); 745 _colorMap.put("Moccasin".toLowerCase(), new int[] {0xFF, 0xE4, 0xB5}); 746 _colorMap.put("NavajoWhite".toLowerCase(), new int[] {0xFF, 0xDE, 0xAD}); 747 _colorMap.put("Navy".toLowerCase(), new int[] {0x00, 0x00, 0x80}); 748 _colorMap.put("OldLace".toLowerCase(), new int[] {0xFD, 0xF5, 0xE6}); 749 _colorMap.put("Olive".toLowerCase(), new int[] {0x80, 0x80, 0x00}); 750 _colorMap.put("OliveDrab".toLowerCase(), new int[] {0x6B, 0x8E, 0x23}); 751 _colorMap.put("Orange".toLowerCase(), new int[] {0xFF, 0xA5, 0x00}); 752 _colorMap.put("OrangeRed".toLowerCase(), new int[] {0xFF, 0x45, 0x00}); 753 _colorMap.put("Orchid".toLowerCase(), new int[] {0xDA, 0x70, 0xD6}); 754 _colorMap.put("PaleGoldenRod".toLowerCase(), new int[] {0xEE, 0xE8, 0xAA}); 755 _colorMap.put("PaleGreen".toLowerCase(), new int[] {0x98, 0xFB, 0x98}); 756 _colorMap.put("PaleTurquoise".toLowerCase(), new int[] {0xAF, 0xEE, 0xEE}); 757 _colorMap.put("PaleVioletRed".toLowerCase(), new int[] {0xDB, 0x70, 0x93}); 758 _colorMap.put("PapayaWhip".toLowerCase(), new int[] {0xFF, 0xEF, 0xD5}); 759 _colorMap.put("PeachPuff".toLowerCase(), new int[] {0xFF, 0xDA, 0xB9}); 760 _colorMap.put("Peru".toLowerCase(), new int[] {0xCD, 0x85, 0x3F}); 761 _colorMap.put("Pink".toLowerCase(), new int[] {0xFF, 0xC0, 0xCB}); 762 _colorMap.put("Plum".toLowerCase(), new int[] {0xDD, 0xA0, 0xDD}); 763 _colorMap.put("PowderBlue".toLowerCase(), new int[] {0xB0, 0xE0, 0xE6}); 764 _colorMap.put("Purple".toLowerCase(), new int[] {0x80, 0x00, 0x80}); 765 _colorMap.put("Red".toLowerCase(), new int[] {0xFF, 0x00, 0x00}); 766 _colorMap.put("RosyBrown".toLowerCase(), new int[] {0xBC, 0x8F, 0x8F}); 767 _colorMap.put("RoyalBlue".toLowerCase(), new int[] {0x41, 0x69, 0xE1}); 768 _colorMap.put("SaddleBrown".toLowerCase(), new int[] {0x8B, 0x45, 0x13}); 769 _colorMap.put("Salmon".toLowerCase(), new int[] {0xFA, 0x80, 0x72}); 770 _colorMap.put("SandyBrown".toLowerCase(), new int[] {0xF4, 0xA4, 0x60}); 771 _colorMap.put("SeaGreen".toLowerCase(), new int[] {0x2E, 0x8B, 0x57}); 772 _colorMap.put("SeaShell".toLowerCase(), new int[] {0xFF, 0xF5, 0xEE}); 773 _colorMap.put("Sienna".toLowerCase(), new int[] {0xA0, 0x52, 0x2D}); 774 _colorMap.put("Silver".toLowerCase(), new int[] {0xC0, 0xC0, 0xC0}); 775 _colorMap.put("SkyBlue".toLowerCase(), new int[] {0x87, 0xCE, 0xEB}); 776 _colorMap.put("SlateBlue".toLowerCase(), new int[] {0x6A, 0x5A, 0xCD}); 777 _colorMap.put("SlateGray".toLowerCase(), new int[] {0x70, 0x80, 0x90}); 778 _colorMap.put("Snow".toLowerCase(), new int[] {0xFF, 0xFA, 0xFA}); 779 _colorMap.put("SpringGreen".toLowerCase(), new int[] {0x00, 0xFF, 0x7F}); 780 _colorMap.put("SteelBlue".toLowerCase(), new int[] {0x46, 0x82, 0xB4}); 781 _colorMap.put("Tan".toLowerCase(), new int[] {0xD2, 0xB4, 0x8C}); 782 _colorMap.put("Teal".toLowerCase(), new int[] {0x00, 0x80, 0x80}); 783 _colorMap.put("Thistle".toLowerCase(), new int[] {0xD8, 0xBF, 0xD8}); 784 _colorMap.put("Tomato".toLowerCase(), new int[] {0xFF, 0x63, 0x47}); 785 _colorMap.put("Turquoise".toLowerCase(), new int[] {0x40, 0xE0, 0xD0}); 786 _colorMap.put("Violet".toLowerCase(), new int[] {0xEE, 0x82, 0xEE}); 787 _colorMap.put("Wheat".toLowerCase(), new int[] {0xF5, 0xDE, 0xB3}); 788 _colorMap.put("White".toLowerCase(), new int[] {0xFF, 0xFF, 0xFF}); 789 _colorMap.put("WhiteSmoke".toLowerCase(), new int[] {0xF5, 0xF5, 0xF5}); 790 _colorMap.put("Yellow".toLowerCase(), new int[] {0xFF, 0xFF, 0x00}); 791 _colorMap.put("YellowGreen".toLowerCase(), new int[] {0x9A, 0xCD, 0x32}); 792 } 793 794}