Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.mapcontext / org.gvsig.fmap.mapcontext.api / src / main / java / org / gvsig / fmap / mapcontext / rendering / symbols / impl / DefaultSymbolManager.java @ 43524

History | View | Annotate | Download (24.3 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License as published by the Free Software
8
 * Foundation; either version 3 of the License, or (at your option) any later
9
 * version.
10
 *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
 * details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with
17
 * this program; if not, write to the Free Software Foundation, Inc., 51
18
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
 *
20
 * For any additional information, do not hesitate to contact us at info AT
21
 * gvsig.com, or visit our website www.gvsig.com.
22
 */
23
package org.gvsig.fmap.mapcontext.rendering.symbols.impl;
24

    
25
/*
26
 * Based on portions of code from www.ColorBrewer.org
27
 *
28
 * Colors from www.ColorBrewer.org by Cynthia A. Brewer,
29
 * Geography, Pennsylvania State University.
30
 * Using groups of 4 colors from the 9 Diverging Schemes
31
 * 4 * 9 = 36 colors
32
 */
33

    
34

    
35
import java.awt.Color;
36
import java.io.File;
37
import java.io.FileFilter;
38
import java.io.FileInputStream;
39
import java.io.FileOutputStream;
40
import java.io.IOException;
41
import java.util.ArrayList;
42
import java.util.Arrays;
43
import java.util.Collection;
44
import java.util.Collections;
45
import java.util.Comparator;
46
import java.util.HashMap;
47
import java.util.Iterator;
48
import java.util.List;
49
import java.util.Map;
50
import java.util.Random;
51

    
52
import org.apache.commons.io.FileUtils;
53
import org.apache.commons.io.FilenameUtils;
54
import org.apache.commons.io.filefilter.FileFilterUtils;
55
import org.apache.commons.lang3.StringUtils;
56
import org.slf4j.Logger;
57
import org.slf4j.LoggerFactory;
58

    
59
import org.gvsig.fmap.geom.Geometry;
60
import org.gvsig.fmap.geom.GeometryLocator;
61
import org.gvsig.fmap.geom.GeometryManager;
62
import org.gvsig.fmap.geom.type.GeometryType;
63
import org.gvsig.fmap.geom.type.GeometryTypeNotSupportedException;
64
import org.gvsig.fmap.geom.type.GeometryTypeNotValidException;
65
import org.gvsig.fmap.mapcontext.MapContextRuntimeException;
66
import org.gvsig.fmap.mapcontext.impl.InvalidRegisteredClassException;
67
import org.gvsig.fmap.mapcontext.impl.RegisteredClassInstantiationException;
68
import org.gvsig.fmap.mapcontext.rendering.symbols.IMultiLayerSymbol;
69
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
70
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol_v2;
71
import org.gvsig.fmap.mapcontext.rendering.symbols.IWarningSymbol;
72
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolException;
73
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolManager;
74
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolPreferences;
75
import org.gvsig.tools.ToolsLocator;
76
import org.gvsig.tools.exception.BaseException;
77
import org.gvsig.tools.persistence.PersistenceManager;
78
import org.gvsig.tools.persistence.PersistentState;
79
import org.gvsig.tools.persistence.exception.PersistenceException;
80
import org.gvsig.tools.task.AbstractMonitorableTask;
81
import org.gvsig.tools.task.CancellableTask;
82
import org.gvsig.tools.task.SimpleTaskStatus;
83
import org.gvsig.tools.task.TaskStatusManager;
84
import org.gvsig.tools.visitor.VisitCanceledException;
85
import org.gvsig.tools.visitor.Visitor;
86

    
87
/**
88
 * Default {@link SymbolManager} implementation.
89
 *
90
 * @author gvSIG team
91
 */
92
public class DefaultSymbolManager implements SymbolManager {
93

    
94
    private static final Logger logger = LoggerFactory.getLogger(DefaultSymbolManager.class);
95

    
96
    private SymbolPreferences symbolPreferences
97
            = new DefaultSymbolPreferences();
98

    
99
    private Map symbolsByName = Collections.synchronizedMap(new HashMap());
100

    
101
    private Map symbolsByShapeType = Collections.synchronizedMap(new HashMap());
102

    
103
    private Map multiLayerSymbolsByName
104
            = Collections.synchronizedMap(new HashMap());
105

    
106
    private Map multiLayerSymbolsByShapeType
107
            = Collections.synchronizedMap(new HashMap());
108

    
109
    private IWarningSymbol warningSymbol;
110

    
111
    private Object warningSymbolLock = new Object();
112

    
113
    public ISymbol[] loadSymbols(File folder) throws SymbolException {
114
        return loadSymbols(folder, null);
115
    }
116

    
117
    public ISymbol[] loadSymbols(File folder, FileFilter fileFilter)
118
            throws SymbolException {
119
        // TODO: add symbol caching
120

    
121
        if (folder.exists()) {
122

    
123
            File[] symbolFiles = null;
124
            if (fileFilter == null) {
125
                symbolFiles = folder.listFiles();
126
            } else {
127
                symbolFiles = folder.listFiles(fileFilter);
128
            }
129

    
130
            if (symbolFiles != null) {
131
                TaskStatusManager tm = ToolsLocator.getTaskStatusManager();
132
                SimpleTaskStatus status = tm.createDefaultSimpleTaskStatus("Load symbols");
133
                status.setRangeOfValues(0, symbolFiles.length);
134
                status.setAutoremove(true);
135
                status.add();
136

    
137
                /*
138
                 * Sorting by file name before loading.
139
                 * The problem here is that some
140
                 * descriptions can be empty, so sorting using description
141
                 * can have strange behavior. Sorting by file name is not
142
                 * very elegant, though.
143
                 */
144
                status.message("sorting symbols");
145
                Arrays.sort(symbolFiles, new Comparator() {
146
                    public int compare(Object o1, Object o2) {
147
                        File f1 = (File) o1;
148
                        File f2 = (File) o2;
149
                        return f1.getName().compareTo(f2.getName());
150
                    }
151
                });
152

    
153
                ISymbol[] symbols = null;
154
                try {
155
                    symbols = new ISymbol[symbolFiles.length];
156
                    for (int i = 0; i < symbolFiles.length; i++) {
157
                        status.setCurValue(i);
158
                        File symbolFile = symbolFiles[i];
159
                        try {
160
                            symbols[i] = loadSymbol(symbolFile);
161
                        } catch(Throwable th) {
162
                            logger.warn("Can't load symbol '"+symbolFile.getAbsolutePath()+"'.",th);
163
                        }
164
                    }
165

    
166
                } finally {
167
                    status.terminate();
168
                }
169
                return symbols;
170
            }
171
        }
172

    
173
        return null;
174
    }
175

    
176
    public class SymbolsLoaderTask extends AbstractMonitorableTask {
177

    
178
        private File folder;
179
        private FileFilter filter;
180
        private Visitor visitor;
181

    
182
        public SymbolsLoaderTask(File folder, FileFilter filter, Visitor visitor) {
183
            super("Load symbols", true);
184
            this.folder = folder;
185
            this.filter = filter;
186
            this.visitor = visitor;
187
            if (folder == null) {
188
                throw new IllegalArgumentException("folder is null");
189
            }
190
            if (visitor == null) {
191
                throw new IllegalArgumentException("visitor is null");
192
            }
193
        }
194

    
195
        private File[] getFiles() {
196
            if (filter == null) {
197
                return folder.listFiles();
198
            } else {
199
                return folder.listFiles(filter);
200
            }
201
        }
202

    
203
        private String getVisitorName() {
204
            String s = visitor.toString() + "/" + visitor.getClass().getName();
205
            return s;
206
        }
207

    
208
        private void visit(final File file, final ISymbol symbol) throws VisitCanceledException {
209
            try {
210
                visitor.visit(symbol);
211
            } catch (BaseException e) {
212
                logger.warn("Can't call visit '" + getVisitorName() + "' to offer the symbol '" + file.getAbsolutePath() + "'.", e);
213
            }
214
        }
215

    
216
        public void run() {
217
            // TODO: add symbol caching
218
            try {
219
                logger.info("[SymbolsLoaderTask" + this.getId() + "] process initited.");
220
                if (folder.exists()) {
221
                    taskStatus.setAutoremove(true);
222
                    taskStatus.message("preparing");
223
                    File[] symbolFiles = getFiles();
224

    
225
                    if (symbolFiles != null) {
226
                        taskStatus.setRangeOfValues(0, symbolFiles.length);
227

    
228
                        /*
229
                         * Sorting by file name before loading. The problem here
230
                         * is that some descriptions can be empty, so sorting
231
                         * using description can have strange behavior. Sorting
232
                         * by file name is not very elegant, though.
233
                         */
234
                        taskStatus.message("sorting");
235
                        Arrays.sort(symbolFiles, new Comparator() {
236
                            public int compare(Object o1, Object o2) {
237
                                File f1 = (File) o1;
238
                                File f2 = (File) o2;
239
                                return f1.getName().compareTo(f2.getName());
240
                            }
241
                        });
242

    
243
                        taskStatus.message("loading");
244
                        for (int i = 0; i < symbolFiles.length; i++) {
245
                            taskStatus.setCurValue(i);
246
                            if (taskStatus.isCancellationRequested()) {
247
                                logger.info("[SymbolsLoaderTask" + this.getId() + "] process canceled.");
248
                                break;
249
                            }
250
                            ISymbol symbol = null;
251
                            try {
252
                                symbol = loadSymbol(symbolFiles[i]);
253
                                visit(symbolFiles[i], symbol);
254
                            } catch (VisitCanceledException e) {
255
                                break;
256
                            } catch (Throwable e) {
257
                                logger.warn("Can't load symbol '"
258
                                        + symbolFiles[i].getAbsolutePath()
259
                                        + "'.", e);
260
                            }
261
                        }
262
                        taskStatus.message("");
263

    
264
                    }
265
                }
266

    
267
            } finally {
268
                taskStatus.terminate();
269
            }
270
            logger.info("[SymbolsLoaderTask" + this.getId() + "] process terminated.");
271

    
272
        }
273
    }
274

    
275
    public CancellableTask loadSymbols(File folder, FileFilter filter, Visitor visitor) {
276
        SymbolsLoaderTask task = new SymbolsLoaderTask(folder, filter, visitor);
277
        task.start();
278
        return task;
279
    }
280

    
281
    public void saveSymbol(ISymbol symbol, String fileName, File folder)
282
            throws SymbolException {
283
        saveSymbol(symbol, fileName, folder, false);
284

    
285
    }
286

    
287
    public void saveSymbol(ISymbol symbol, String fileName, File folder,
288
            boolean overwrite) throws SymbolException {
289
        // TODO: add symbol caching
290

    
291
        PersistenceManager persistenceManager = ToolsLocator
292
                .getPersistenceManager();
293

    
294
        File symbolFile = new File(folder, fileName);
295
        if (!overwrite && symbolFile.exists()) {
296
            throw new SymbolFileAlreadyExistsException(symbolFile);
297
        }
298

    
299
        try {
300
            FileOutputStream fos = new FileOutputStream(symbolFile);
301

    
302
            persistenceManager.saveState(persistenceManager.getState(symbol),
303
                    fos);
304

    
305
            fos.flush();
306
            fos.close();
307
        } catch (PersistenceException e) {
308
            throw new SaveSymbolException(e);
309
        } catch (IOException e) {
310
            throw new SaveSymbolException(e);
311
        }
312
        if (symbol instanceof ISymbol_v2) {
313
            ISymbol_v2 symbolv2 = (ISymbol_v2) symbol;
314
            if (StringUtils.isBlank(symbolv2.getID())) {
315
                symbolv2.setID(FilenameUtils.getBaseName(fileName));
316
            }
317
        }
318
    }
319

    
320
    /**
321
     * Loads a persisted symbol from the given file.
322
     */
323
    private ISymbol loadSymbol(File file) throws SymbolException {
324
        if (file.exists()) {
325
            try {
326
                FileInputStream fis = new FileInputStream(file);
327

    
328
                PersistenceManager persistenceManager = ToolsLocator
329
                        .getPersistenceManager();
330

    
331
                PersistentState state = persistenceManager.loadState(fis);
332
                ISymbol symbol = (ISymbol) persistenceManager.create(state);
333

    
334
                fis.close();
335
                if (symbol instanceof ISymbol_v2) {
336
                    ISymbol_v2 symbolv2 = (ISymbol_v2) symbol;
337
                    symbolv2.setID(FilenameUtils.getBaseName(file.getName()));
338
                }
339
                return symbol;
340
            } catch (PersistenceException e) {
341
                throw new LoadSymbolException(e);
342
            } catch (IOException e) {
343
                throw new LoadSymbolException(e);
344
            }
345
        }
346

    
347
        return null;
348
    }
349

    
350
    public SymbolPreferences getSymbolPreferences() {
351
        return symbolPreferences;
352
    }
353

    
354
    public ISymbol createSymbol(String symbolName)
355
            throws MapContextRuntimeException {
356
        return createSymbol(symbolName, (Class) symbolsByName.get(symbolName),
357
                ISymbol.class);
358
    }
359

    
360
    public ISymbol createSymbol(int shapeType)
361
            throws MapContextRuntimeException {
362
        String symbolName = getSymbolName(symbolsByShapeType, shapeType);
363

    
364
        return symbolName == null ? null : createSymbol(symbolName);
365
    }
366

    
367
    public ISymbol createSymbol(String symbolName, Color color)
368
            throws MapContextRuntimeException {
369
        ISymbol symbol = createSymbol(symbolName);
370

    
371
        if (symbol != null) {
372
            symbol.setColor(color);
373
        }
374

    
375
        return symbol;
376
    }
377

    
378
    public ISymbol createSymbol(int shapeType, Color color)
379
            throws MapContextRuntimeException {
380
        String symbolName = getSymbolName(symbolsByShapeType, shapeType);
381

    
382
        return symbolName == null ? null : createSymbol(symbolName, color);
383
    }
384

    
385
    public IMultiLayerSymbol createMultiLayerSymbol(String symbolName)
386
            throws MapContextRuntimeException {
387
        return (IMultiLayerSymbol) createSymbol(symbolName,
388
                (Class) multiLayerSymbolsByName.get(symbolName),
389
                IMultiLayerSymbol.class);
390
    }
391

    
392
    public IMultiLayerSymbol createMultiLayerSymbol(int shapeType)
393
            throws MapContextRuntimeException {
394
        String symbolName = getSymbolName(multiLayerSymbolsByShapeType, shapeType);
395

    
396
        return symbolName == null ? null : createMultiLayerSymbol(symbolName);
397
    }
398

    
399
    /**
400
     * @param shapeType
401
     * @return
402
     */
403
    private String getSymbolName(Map symbols, int shapeType) {
404
        String symbolName
405
                = (String) symbols.get(new Integer(shapeType));
406
        if (symbolName == null) {
407
            GeometryManager geomManager = GeometryLocator.getGeometryManager();
408
            GeometryType shapeGeometryType;
409
                Iterator it = symbols.keySet().iterator();
410
                while (it.hasNext()) {
411
                    Integer geomType = (Integer) it.next();
412
                  if (geomManager.isSubtype(geomType, shapeType)) {
413
                      symbolName = (String) symbols.get(geomType);
414
                      break;
415
                  }
416
                }
417
        }
418
        return symbolName;
419
    }
420

    
421
    public void registerSymbol(String symbolName, Class symbolClass)
422
            throws MapContextRuntimeException {
423
        if (symbolClass == null || !ISymbol.class.isAssignableFrom(symbolClass)) {
424
            throw new InvalidRegisteredClassException(ISymbol.class,
425
                    symbolClass, symbolName);
426
        }
427
        symbolsByName.put(symbolName, symbolClass);
428
    }
429

    
430
    public void registerSymbol(String symbolName, int[] shapeTypes,
431
            Class symbolClass) throws MapContextRuntimeException {
432
        registerSymbol(symbolName, symbolClass);
433
        if (shapeTypes != null) {
434
            for (int i = 0; i < shapeTypes.length; i++) {
435
                symbolsByShapeType.put(new Integer(shapeTypes[i]), symbolName);
436
            }
437
        }
438
    }
439

    
440
    public void registerMultiLayerSymbol(String symbolName, Class symbolClass)
441
            throws MapContextRuntimeException {
442
        if (symbolClass == null
443
                || !IMultiLayerSymbol.class.isAssignableFrom(symbolClass)) {
444
            throw new InvalidRegisteredClassException(IMultiLayerSymbol.class,
445
                    symbolClass, symbolName);
446
        }
447

    
448
        multiLayerSymbolsByName.put(symbolName, symbolClass);
449
    }
450

    
451
    public void registerMultiLayerSymbol(String symbolName, int[] shapeTypes,
452
            Class symbolClass) throws MapContextRuntimeException {
453
        registerMultiLayerSymbol(symbolName, symbolClass);
454
        if (shapeTypes != null) {
455
            for (int i = 0; i < shapeTypes.length; i++) {
456
                multiLayerSymbolsByShapeType.put(new Integer(shapeTypes[i]),
457
                        symbolName);
458
            }
459
        }
460
    }
461

    
462
    public IWarningSymbol getWarningSymbol(String message, String symbolDesc,
463
            int symbolDrawExceptionType) throws MapContextRuntimeException {
464
        synchronized (warningSymbolLock) {
465
            if (warningSymbol == null) {
466
                warningSymbol = (IWarningSymbol) createSymbol("warning");
467
            }
468
        }
469

    
470
        // TODO: set those values as parameter values in the draw method.
471
        warningSymbol.setDescription(symbolDesc);
472
        warningSymbol.setMessage(message);
473
        warningSymbol.setDrawExceptionType(symbolDrawExceptionType);
474

    
475
        return warningSymbol;
476
    }
477

    
478
    private ISymbol createSymbol(String symbolName, Class symbolClass,
479
            Class expectedType) throws MapContextRuntimeException {
480
        ISymbol symbol;
481
        try {
482
            symbol
483
                    = (ISymbol) (symbolClass == null ? null
484
                    : symbolClass.newInstance());
485
        } catch (InstantiationException e) {
486
            throw new RegisteredClassInstantiationException(expectedType,
487
                    symbolClass, symbolName, e);
488
        } catch (IllegalAccessException e) {
489
            throw new RegisteredClassInstantiationException(expectedType,
490
                    symbolClass, symbolName, e);
491
        }
492

    
493
        Color the_color = null;
494

    
495
        if (getSymbolPreferences().isDefaultSymbolFillColorAleatory()) {
496

    
497
            the_color = getRandomBrewerBasedColor();
498

    
499
        } else {
500
            // not random
501
            the_color = getSymbolPreferences().getDefaultSymbolFillColor();
502
        }
503
        symbol.setColor(the_color);
504
        // Perform this initialization into the Symbol implementation
505
        // if (symbol instanceof CartographicSupport) {
506
        // CartographicSupport cs = (CartographicSupport) symbol;
507
        // cs.setUnit(getDefaultCartographicSupportMeasureUnit());
508
        // cs
509
        // .setReferenceSystem(getDefaultCartographicSupportReferenceSystem();
510
        // }
511

    
512
        return symbol;
513
    }
514

    
515
    public void setSymbolPreferences(SymbolPreferences symbolPreferences) {
516
        this.symbolPreferences = symbolPreferences;
517
    }
518

    
519
    public List getSymbolLibraryNames() {
520
        File rootfolder = new File(this.getSymbolPreferences().getSymbolLibraryPath());
521
        Collection libraries = FileUtils.listFiles(rootfolder, FileFilterUtils.directoryFileFilter(), null);
522
        List l = new ArrayList();
523
        l.addAll(libraries);
524
        return l;
525
    }
526

    
527
    public ISymbol getSymbol(String libraryName, String symbolID) throws SymbolException {
528
        Collection symbols = null;
529
        File rootfolder = null;
530
        try {
531
            rootfolder = new File(this.getSymbolPreferences().getSymbolLibraryPath());
532
            symbols = FileUtils.listFiles(rootfolder,
533
                    FileFilterUtils.nameFileFilter(symbolID + getSymbolPreferences().getSymbolFileExtension()),
534
                    FileFilterUtils.trueFileFilter()
535
            );
536
        } catch(Exception ex) {
537
            logger.warn("Can't get symbol from symbol library (library:'"+libraryName+"', symbol:'"+symbolID+"', symbolLibraryPath'"+rootfolder+"')", ex);
538
        }
539
        if (symbols == null) {
540
            return null;
541
        }
542
        if (symbols.isEmpty()) {
543
            return null;
544
        }
545

    
546
        File f = null;
547
        try {
548
            f = (File) symbols.iterator().next();
549
            ISymbol symbol = loadSymbol(f);
550
            return symbol;
551
        } catch(Exception ex) {
552
            String fname = ((f==null)?"Null":f.getAbsolutePath());
553
            logger.warn("Can't load symbol from symbol library (library:'"+libraryName+"', symbol:'"+symbolID+"', symbolLibraryPath'"+rootfolder+"', symbolFile:'"+fname+"')", ex);
554
        }
555
        return null;
556
    }
557

    
558
    /**
559
     *
560
     * @param col
561
     * @return color 1/3 closer to black
562
     */
563
    public static Color darker(Color col) {
564
        return new Color(
565
                (2 * col.getRed()) / 3,
566
                (2 * col.getGreen()) / 3,
567
                (2 * col.getBlue()) / 3,
568
                col.getAlpha());
569
    }
570

    
571
    /**
572
     *
573
     * @param col
574
     * @return color 1/3 closer to white
575
     */
576
    public static Color lighter(Color col) {
577
        Color resp = invert(col);
578
        resp = darker(resp);
579
        return invert(resp);
580
    }
581

    
582
    /**
583
     *
584
     * @param col
585
     * @return inverted color (inverts each band, same alpha)
586
     */
587
    public static Color invert(Color col) {
588
        return new Color(
589
                255 - col.getRed(),
590
                255 - col.getGreen(),
591
                255 - col.getBlue(),
592
                col.getAlpha());
593
    }
594

    
595
    private static Color getRandomBrewerBasedColor() {
596

    
597
        int ind = rnd.nextInt(BREWER_COLOR.length);
598
        Color resp = BREWER_COLOR[ind];
599
        ind = rnd.nextInt(100);
600
        if (ind > 66) {
601
            resp = darker(resp);
602
        } else {
603
            if (ind > 33) {
604
                resp = lighter(resp);
605
            }
606
            // ...else resp remains the same
607
        }
608

    
609
        // finally add some dark noise
610
        resp = addDarkNoise(resp);
611
        return resp;
612
    }
613

    
614
    private static Color addDarkNoise(Color c) {
615
        int r = Math.max(0, c.getRed() - rnd.nextInt(30));
616
        int g = Math.max(0, c.getGreen() - rnd.nextInt(30));
617
        int b = Math.max(0, c.getBlue() - rnd.nextInt(30));
618
        return new Color(r, g, b, c.getAlpha());
619
    }
620

    
621
    private static Color[] BREWER_COLOR = new Color[36];
622
    private static final Random rnd = new Random();
623

    
624
    static {
625
        /**
626
         * Colors from www.ColorBrewer.org by Cynthia A. Brewer, Geography,
627
         * Pennsylvania State University.
628
         *
629
         * Using groups of 4 colors from the 9 Diverging Schemes 4 * 9 = 36
630
         * colors
631
         */
632
        BREWER_COLOR[0] = new Color(230, 97, 1);
633
        BREWER_COLOR[1] = new Color(253, 184, 99);
634
        BREWER_COLOR[2] = new Color(178, 171, 210);
635
        BREWER_COLOR[3] = new Color(94, 60, 153);
636
        BREWER_COLOR[4] = new Color(166, 97, 26);
637
        BREWER_COLOR[5] = new Color(223, 194, 125);
638
        BREWER_COLOR[6] = new Color(128, 205, 193);
639
        BREWER_COLOR[7] = new Color(1, 133, 113);
640
        BREWER_COLOR[8] = new Color(123, 50, 148);
641
        BREWER_COLOR[9] = new Color(194, 165, 207);
642
        BREWER_COLOR[10] = new Color(166, 219, 160);
643
        BREWER_COLOR[11] = new Color(0, 136, 55);
644
        BREWER_COLOR[12] = new Color(208, 28, 139);
645
        BREWER_COLOR[13] = new Color(241, 182, 218);
646
        BREWER_COLOR[14] = new Color(184, 225, 134);
647
        BREWER_COLOR[15] = new Color(77, 172, 38);
648
        BREWER_COLOR[16] = new Color(202, 0, 32);
649
        BREWER_COLOR[17] = new Color(244, 165, 130);
650
        BREWER_COLOR[18] = new Color(146, 197, 222);
651
        BREWER_COLOR[19] = new Color(5, 113, 176);
652
        BREWER_COLOR[20] = new Color(202, 0, 32);
653
        BREWER_COLOR[21] = new Color(244, 165, 130);
654
        BREWER_COLOR[22] = new Color(186, 186, 186);
655
        BREWER_COLOR[23] = new Color(64, 64, 64);
656
        BREWER_COLOR[24] = new Color(215, 25, 28);
657
        BREWER_COLOR[25] = new Color(253, 174, 97);
658
        BREWER_COLOR[26] = new Color(171, 217, 233);
659
        BREWER_COLOR[27] = new Color(44, 123, 182);
660
        BREWER_COLOR[28] = new Color(215, 25, 28);
661
        BREWER_COLOR[29] = new Color(253, 174, 97);
662
        BREWER_COLOR[30] = new Color(171, 221, 164);
663
        BREWER_COLOR[31] = new Color(43, 131, 186);
664
        BREWER_COLOR[32] = new Color(215, 25, 28);
665
        BREWER_COLOR[33] = new Color(253, 174, 97);
666
        BREWER_COLOR[34] = new Color(166, 217, 106);
667
        BREWER_COLOR[35] = new Color(26, 150, 65);
668
    }
669

    
670
}