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 @ 42023

History | View | Annotate | Download (23.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.List;
48
import java.util.Map;
49
import java.util.Random;
50
import org.apache.commons.io.FileUtils;
51
import org.apache.commons.io.FilenameUtils;
52
import org.apache.commons.io.filefilter.FileFilterUtils;
53
import org.apache.commons.lang3.StringUtils;
54
import org.gvsig.fmap.mapcontext.MapContextRuntimeException;
55
import org.gvsig.fmap.mapcontext.impl.InvalidRegisteredClassException;
56
import org.gvsig.fmap.mapcontext.impl.RegisteredClassInstantiationException;
57
import org.gvsig.fmap.mapcontext.rendering.symbols.IMultiLayerSymbol;
58
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
59
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol_v2;
60
import org.gvsig.fmap.mapcontext.rendering.symbols.IWarningSymbol;
61
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolException;
62
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolManager;
63
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolPreferences;
64
import org.gvsig.tools.ToolsLocator;
65
import org.gvsig.tools.exception.BaseException;
66
import org.gvsig.tools.persistence.PersistenceManager;
67
import org.gvsig.tools.persistence.PersistentState;
68
import org.gvsig.tools.persistence.exception.PersistenceException;
69
import org.gvsig.tools.task.AbstractMonitorableTask;
70
import org.gvsig.tools.task.CancellableTask;
71
import org.gvsig.tools.task.SimpleTaskStatus;
72
import org.gvsig.tools.task.TaskStatusManager;
73
import org.gvsig.tools.visitor.VisitCanceledException;
74
import org.gvsig.tools.visitor.Visitor;
75
import org.slf4j.Logger;
76
import org.slf4j.LoggerFactory;
77

    
78
/**
79
 * Default {@link SymbolManager} implementation.
80
 *
81
 * @author gvSIG team
82
 */
83
public class DefaultSymbolManager implements SymbolManager {
84

    
85
    private static final Logger logger = LoggerFactory.getLogger(DefaultSymbolManager.class);
86

    
87
    private SymbolPreferences symbolPreferences
88
            = new DefaultSymbolPreferences();
89

    
90
    private Map symbolsByName = Collections.synchronizedMap(new HashMap());
91

    
92
    private Map symbolsByShapeType = Collections.synchronizedMap(new HashMap());
93

    
94
    private Map multiLineSymbolsByName
95
            = Collections.synchronizedMap(new HashMap());
96

    
97
    private Map multiLineSymbolsByShapeType
98
            = Collections.synchronizedMap(new HashMap());
99

    
100
    private IWarningSymbol warningSymbol;
101

    
102
    private Object warningSymbolLock = new Object();
103

    
104
    public ISymbol[] loadSymbols(File folder) throws SymbolException {
105
        return loadSymbols(folder, null);
106
    }
107

    
108
    public ISymbol[] loadSymbols(File folder, FileFilter fileFilter)
109
            throws SymbolException {
110
        // TODO: add symbol caching
111

    
112
        if (folder.exists()) {
113

    
114
            File[] symbolFiles = null;
115
            if (fileFilter == null) {
116
                symbolFiles = folder.listFiles();
117
            } else {
118
                symbolFiles = folder.listFiles(fileFilter);
119
            }
120

    
121
            if (symbolFiles != null) {
122
                TaskStatusManager tm = ToolsLocator.getTaskStatusManager();
123
                SimpleTaskStatus status = tm.createDefaultSimpleTaskStatus("Load symbols");
124
                status.setRangeOfValues(0, symbolFiles.length);
125
                status.setAutoremove(true);
126
                status.add();
127

    
128
                /*
129
                 * Sorting by file name before loading.
130
                 * The problem here is that some
131
                 * descriptions can be empty, so sorting using description
132
                 * can have strange behavior. Sorting by file name is not
133
                 * very elegant, though.
134
                 */
135
                status.message("sorting symbols");
136
                Arrays.sort(symbolFiles, new Comparator() {
137
                    public int compare(Object o1, Object o2) {
138
                        File f1 = (File) o1;
139
                        File f2 = (File) o2;
140
                        return f1.getName().compareTo(f2.getName());
141
                    }
142
                });
143

    
144
                ISymbol[] symbols = null;
145
                try {
146
                    symbols = new ISymbol[symbolFiles.length];
147
                    for (int i = 0; i < symbolFiles.length; i++) {
148
                        status.setCurValue(i);
149
                        File symbolFile = symbolFiles[i];
150
                        try {
151
                            symbols[i] = loadSymbol(symbolFile);
152
                        } catch(Throwable th) {
153
                            logger.warn("Can't load symbol '"+symbolFile.getAbsolutePath()+"'.",th);
154
                        }
155
                    }
156

    
157
                } finally {
158
                    status.terminate();
159
                }
160
                return symbols;
161
            }
162
        }
163

    
164
        return null;
165
    }
166

    
167
    public class SymbolsLoaderTask extends AbstractMonitorableTask {
168

    
169
        private File folder;
170
        private FileFilter filter;
171
        private Visitor visitor;
172

    
173
        public SymbolsLoaderTask(File folder, FileFilter filter, Visitor visitor) {
174
            super("Load symbols", true);
175
            this.folder = folder;
176
            this.filter = filter;
177
            this.visitor = visitor;
178
            if (folder == null) {
179
                throw new IllegalArgumentException("folder is null");
180
            }
181
            if (visitor == null) {
182
                throw new IllegalArgumentException("visitor is null");
183
            }
184
        }
185

    
186
        private File[] getFiles() {
187
            if (filter == null) {
188
                return folder.listFiles();
189
            } else {
190
                return folder.listFiles(filter);
191
            }
192
        }
193

    
194
        private String getVisitorName() {
195
            String s = visitor.toString() + "/" + visitor.getClass().getName();
196
            return s;
197
        }
198

    
199
        private void visit(final File file, final ISymbol symbol) throws VisitCanceledException {
200
            try {
201
                visitor.visit(symbol);
202
            } catch (BaseException e) {
203
                logger.warn("Can't call visit '" + getVisitorName() + "' to offer the symbol '" + file.getAbsolutePath() + "'.", e);
204
            }
205
        }
206

    
207
        public void run() {
208
            // TODO: add symbol caching
209
            try {
210
                logger.info("[SymbolsLoaderTask" + this.getId() + "] process initited.");
211
                if (folder.exists()) {
212
                    taskStatus.setAutoremove(true);
213
                    taskStatus.message("preparing");
214
                    File[] symbolFiles = getFiles();
215

    
216
                    if (symbolFiles != null) {
217
                        taskStatus.setRangeOfValues(0, symbolFiles.length);
218

    
219
                        /*
220
                         * Sorting by file name before loading. The problem here
221
                         * is that some descriptions can be empty, so sorting
222
                         * using description can have strange behavior. Sorting
223
                         * by file name is not very elegant, though.
224
                         */
225
                        taskStatus.message("sorting");
226
                        Arrays.sort(symbolFiles, new Comparator() {
227
                            public int compare(Object o1, Object o2) {
228
                                File f1 = (File) o1;
229
                                File f2 = (File) o2;
230
                                return f1.getName().compareTo(f2.getName());
231
                            }
232
                        });
233

    
234
                        taskStatus.message("loading");
235
                        for (int i = 0; i < symbolFiles.length; i++) {
236
                            taskStatus.setCurValue(i);
237
                            if (taskStatus.isCancellationRequested()) {
238
                                logger.info("[SymbolsLoaderTask" + this.getId() + "] process canceled.");
239
                                break;
240
                            }
241
                            ISymbol symbol = null;
242
                            try {
243
                                symbol = loadSymbol(symbolFiles[i]);
244
                                visit(symbolFiles[i], symbol);
245
                            } catch (VisitCanceledException e) {
246
                                break;
247
                            } catch (Throwable e) {
248
                                logger.warn("Can't load symbol '"
249
                                        + symbolFiles[i].getAbsolutePath()
250
                                        + "'.", e);
251
                            }
252
                        }
253
                        taskStatus.message("");
254

    
255
                    }
256
                }
257

    
258
            } finally {
259
                taskStatus.terminate();
260
            }
261
            logger.info("[SymbolsLoaderTask" + this.getId() + "] process terminated.");
262

    
263
        }
264
    }
265

    
266
    public CancellableTask loadSymbols(File folder, FileFilter filter, Visitor visitor) {
267
        SymbolsLoaderTask task = new SymbolsLoaderTask(folder, filter, visitor);
268
        task.start();
269
        return task;
270
    }
271

    
272
    public void saveSymbol(ISymbol symbol, String fileName, File folder)
273
            throws SymbolException {
274
        saveSymbol(symbol, fileName, folder, false);
275

    
276
    }
277

    
278
    public void saveSymbol(ISymbol symbol, String fileName, File folder,
279
            boolean overwrite) throws SymbolException {
280
        // TODO: add symbol caching
281

    
282
        PersistenceManager persistenceManager = ToolsLocator
283
                .getPersistenceManager();
284

    
285
        File symbolFile = new File(folder, fileName);
286
        if (!overwrite && symbolFile.exists()) {
287
            throw new SymbolFileAlreadyExistsException(symbolFile);
288
        }
289

    
290
        try {
291
            FileOutputStream fos = new FileOutputStream(symbolFile);
292

    
293
            persistenceManager.saveState(persistenceManager.getState(symbol),
294
                    fos);
295

    
296
            fos.flush();
297
            fos.close();
298
        } catch (PersistenceException e) {
299
            throw new SaveSymbolException(e);
300
        } catch (IOException e) {
301
            throw new SaveSymbolException(e);
302
        }
303
        if (symbol instanceof ISymbol_v2) {
304
            ISymbol_v2 symbolv2 = (ISymbol_v2) symbol;
305
            if (StringUtils.isBlank(symbolv2.getID())) {
306
                symbolv2.setID(FilenameUtils.getBaseName(fileName));
307
            }
308
        }
309
    }
310

    
311
    /**
312
     * Loads a persisted symbol from the given file.
313
     */
314
    private ISymbol loadSymbol(File file) throws SymbolException {
315
        if (file.exists()) {
316
            try {
317
                FileInputStream fis = new FileInputStream(file);
318

    
319
                PersistenceManager persistenceManager = ToolsLocator
320
                        .getPersistenceManager();
321

    
322
                PersistentState state = persistenceManager.loadState(fis);
323
                ISymbol symbol = (ISymbol) persistenceManager.create(state);
324

    
325
                fis.close();
326
                if (symbol instanceof ISymbol_v2) {
327
                    ISymbol_v2 symbolv2 = (ISymbol_v2) symbol;
328
                    symbolv2.setID(FilenameUtils.getBaseName(file.getName()));
329
                }
330
                return symbol;
331
            } catch (PersistenceException e) {
332
                throw new LoadSymbolException(e);
333
            } catch (IOException e) {
334
                throw new LoadSymbolException(e);
335
            }
336
        }
337

    
338
        return null;
339
    }
340

    
341
    public SymbolPreferences getSymbolPreferences() {
342
        return symbolPreferences;
343
    }
344

    
345
    public ISymbol createSymbol(String symbolName)
346
            throws MapContextRuntimeException {
347
        return createSymbol(symbolName, (Class) symbolsByName.get(symbolName),
348
                ISymbol.class);
349
    }
350

    
351
    public ISymbol createSymbol(int shapeType)
352
            throws MapContextRuntimeException {
353
        String symbolName
354
                = (String) symbolsByShapeType.get(new Integer(shapeType));
355

    
356
        return symbolName == null ? null : createSymbol(symbolName);
357
    }
358

    
359
    public ISymbol createSymbol(String symbolName, Color color)
360
            throws MapContextRuntimeException {
361
        ISymbol symbol = createSymbol(symbolName);
362

    
363
        if (symbol != null) {
364
            symbol.setColor(color);
365
        }
366

    
367
        return symbol;
368
    }
369

    
370
    public ISymbol createSymbol(int shapeType, Color color)
371
            throws MapContextRuntimeException {
372
        String symbolName
373
                = (String) symbolsByShapeType.get(new Integer(shapeType));
374

    
375
        return symbolName == null ? null : createSymbol(symbolName, color);
376
    }
377

    
378
    public IMultiLayerSymbol createMultiLayerSymbol(String symbolName)
379
            throws MapContextRuntimeException {
380
        return (IMultiLayerSymbol) createSymbol(symbolName,
381
                (Class) multiLineSymbolsByName.get(symbolName),
382
                IMultiLayerSymbol.class);
383
    }
384

    
385
    public IMultiLayerSymbol createMultiLayerSymbol(int shapeType)
386
            throws MapContextRuntimeException {
387
        String symbolName
388
                = (String) multiLineSymbolsByShapeType.get(new Integer(shapeType));
389

    
390
        return symbolName == null ? null : createMultiLayerSymbol(symbolName);
391
    }
392

    
393
    public void registerSymbol(String symbolName, Class symbolClass)
394
            throws MapContextRuntimeException {
395
        if (symbolClass == null || !ISymbol.class.isAssignableFrom(symbolClass)) {
396
            throw new InvalidRegisteredClassException(ISymbol.class,
397
                    symbolClass, symbolName);
398
        }
399
        symbolsByName.put(symbolName, symbolClass);
400
    }
401

    
402
    public void registerSymbol(String symbolName, int[] shapeTypes,
403
            Class symbolClass) throws MapContextRuntimeException {
404
        registerSymbol(symbolName, symbolClass);
405
        if (shapeTypes != null) {
406
            for (int i = 0; i < shapeTypes.length; i++) {
407
                symbolsByShapeType.put(new Integer(shapeTypes[i]), symbolName);
408
            }
409
        }
410
    }
411

    
412
    public void registerMultiLayerSymbol(String symbolName, Class symbolClass)
413
            throws MapContextRuntimeException {
414
        if (symbolClass == null
415
                || !IMultiLayerSymbol.class.isAssignableFrom(symbolClass)) {
416
            throw new InvalidRegisteredClassException(IMultiLayerSymbol.class,
417
                    symbolClass, symbolName);
418
        }
419

    
420
        multiLineSymbolsByName.put(symbolName, symbolClass);
421
    }
422

    
423
    public void registerMultiLayerSymbol(String symbolName, int[] shapeTypes,
424
            Class symbolClass) throws MapContextRuntimeException {
425
        registerMultiLayerSymbol(symbolName, symbolClass);
426
        if (shapeTypes != null) {
427
            for (int i = 0; i < shapeTypes.length; i++) {
428
                multiLineSymbolsByShapeType.put(new Integer(shapeTypes[i]),
429
                        symbolName);
430
            }
431
        }
432
    }
433

    
434
    public IWarningSymbol getWarningSymbol(String message, String symbolDesc,
435
            int symbolDrawExceptionType) throws MapContextRuntimeException {
436
        synchronized (warningSymbolLock) {
437
            if (warningSymbol == null) {
438
                warningSymbol = (IWarningSymbol) createSymbol("warning");
439
            }
440
        }
441

    
442
        // TODO: set those values as parameter values in the draw method.
443
        warningSymbol.setDescription(symbolDesc);
444
        warningSymbol.setMessage(message);
445
        warningSymbol.setDrawExceptionType(symbolDrawExceptionType);
446

    
447
        return warningSymbol;
448
    }
449

    
450
    private ISymbol createSymbol(String symbolName, Class symbolClass,
451
            Class expectedType) throws MapContextRuntimeException {
452
        ISymbol symbol;
453
        try {
454
            symbol
455
                    = (ISymbol) (symbolClass == null ? null
456
                    : symbolClass.newInstance());
457
        } catch (InstantiationException e) {
458
            throw new RegisteredClassInstantiationException(expectedType,
459
                    symbolClass, symbolName, e);
460
        } catch (IllegalAccessException e) {
461
            throw new RegisteredClassInstantiationException(expectedType,
462
                    symbolClass, symbolName, e);
463
        }
464

    
465
        Color the_color = null;
466

    
467
        if (getSymbolPreferences().isDefaultSymbolFillColorAleatory()) {
468

    
469
            the_color = getRandomBrewerBasedColor();
470

    
471
        } else {
472
            // not random
473
            the_color = getSymbolPreferences().getDefaultSymbolFillColor();
474
        }
475
        symbol.setColor(the_color);
476
        // Perform this initialization into the Symbol implementation
477
        // if (symbol instanceof CartographicSupport) {
478
        // CartographicSupport cs = (CartographicSupport) symbol;
479
        // cs.setUnit(getDefaultCartographicSupportMeasureUnit());
480
        // cs
481
        // .setReferenceSystem(getDefaultCartographicSupportReferenceSystem();
482
        // }
483

    
484
        return symbol;
485
    }
486

    
487
    public void setSymbolPreferences(SymbolPreferences symbolPreferences) {
488
        this.symbolPreferences = symbolPreferences;
489
    }
490

    
491
    public List getSymbolLibraryNames() {
492
        File rootfolder = new File(this.getSymbolPreferences().getSymbolLibraryPath());
493
        Collection libraries = FileUtils.listFiles(rootfolder, FileFilterUtils.directoryFileFilter(), null);
494
        List l = new ArrayList();
495
        l.addAll(libraries);
496
        return l;
497
    }
498

    
499
    public ISymbol getSymbol(String libraryName, String symbolID) throws SymbolException {
500
        Collection symbols = null;
501
        File rootfolder = null;
502
        try {
503
            rootfolder = new File(this.getSymbolPreferences().getSymbolLibraryPath());
504
            symbols = FileUtils.listFiles(rootfolder,
505
                    FileFilterUtils.nameFileFilter(symbolID + getSymbolPreferences().getSymbolFileExtension()),
506
                    FileFilterUtils.trueFileFilter()
507
            );
508
        } catch(Exception ex) {
509
            logger.warn("Can't get symbol from symbol library (library:'"+libraryName+"', symbol:'"+symbolID+"', symbolLibraryPath'"+rootfolder+"')", ex);
510
        }
511
        if (symbols == null) {
512
            return null;
513
        }
514
        if (symbols.isEmpty()) {
515
            return null;
516
        }
517

    
518
        File f = null;
519
        try {
520
            f = (File) symbols.iterator().next();
521
            ISymbol symbol = loadSymbol(f);
522
            return symbol;
523
        } catch(Exception ex) {
524
            String fname = ((f==null)?"Null":f.getAbsolutePath());
525
            logger.warn("Can't load symbol from symbol library (library:'"+libraryName+"', symbol:'"+symbolID+"', symbolLibraryPath'"+rootfolder+"', symbolFile:'"+fname+"')", ex);
526
        }
527
        return null;
528
    }
529

    
530
    /**
531
     *
532
     * @param col
533
     * @return color 1/3 closer to black
534
     */
535
    public static Color darker(Color col) {
536
        return new Color(
537
                (2 * col.getRed()) / 3,
538
                (2 * col.getGreen()) / 3,
539
                (2 * col.getBlue()) / 3,
540
                col.getAlpha());
541
    }
542

    
543
    /**
544
     *
545
     * @param col
546
     * @return color 1/3 closer to white
547
     */
548
    public static Color lighter(Color col) {
549
        Color resp = invert(col);
550
        resp = darker(resp);
551
        return invert(resp);
552
    }
553

    
554
    /**
555
     *
556
     * @param col
557
     * @return inverted color (inverts each band, same alpha)
558
     */
559
    public static Color invert(Color col) {
560
        return new Color(
561
                255 - col.getRed(),
562
                255 - col.getGreen(),
563
                255 - col.getBlue(),
564
                col.getAlpha());
565
    }
566

    
567
    private static Color getRandomBrewerBasedColor() {
568

    
569
        int ind = rnd.nextInt(BREWER_COLOR.length);
570
        Color resp = BREWER_COLOR[ind];
571
        ind = rnd.nextInt(100);
572
        if (ind > 66) {
573
            resp = darker(resp);
574
        } else {
575
            if (ind > 33) {
576
                resp = lighter(resp);
577
            }
578
            // ...else resp remains the same
579
        }
580

    
581
        // finally add some dark noise 
582
        resp = addDarkNoise(resp);
583
        return resp;
584
    }
585

    
586
    private static Color addDarkNoise(Color c) {
587
        int r = Math.max(0, c.getRed() - rnd.nextInt(30));
588
        int g = Math.max(0, c.getGreen() - rnd.nextInt(30));
589
        int b = Math.max(0, c.getBlue() - rnd.nextInt(30));
590
        return new Color(r, g, b, c.getAlpha());
591
    }
592

    
593
    private static Color[] BREWER_COLOR = new Color[36];
594
    private static final Random rnd = new Random();
595

    
596
    static {
597
        /**
598
         * Colors from www.ColorBrewer.org by Cynthia A. Brewer, Geography,
599
         * Pennsylvania State University.
600
         *
601
         * Using groups of 4 colors from the 9 Diverging Schemes 4 * 9 = 36
602
         * colors
603
         */
604
        BREWER_COLOR[0] = new Color(230, 97, 1);
605
        BREWER_COLOR[1] = new Color(253, 184, 99);
606
        BREWER_COLOR[2] = new Color(178, 171, 210);
607
        BREWER_COLOR[3] = new Color(94, 60, 153);
608
        BREWER_COLOR[4] = new Color(166, 97, 26);
609
        BREWER_COLOR[5] = new Color(223, 194, 125);
610
        BREWER_COLOR[6] = new Color(128, 205, 193);
611
        BREWER_COLOR[7] = new Color(1, 133, 113);
612
        BREWER_COLOR[8] = new Color(123, 50, 148);
613
        BREWER_COLOR[9] = new Color(194, 165, 207);
614
        BREWER_COLOR[10] = new Color(166, 219, 160);
615
        BREWER_COLOR[11] = new Color(0, 136, 55);
616
        BREWER_COLOR[12] = new Color(208, 28, 139);
617
        BREWER_COLOR[13] = new Color(241, 182, 218);
618
        BREWER_COLOR[14] = new Color(184, 225, 134);
619
        BREWER_COLOR[15] = new Color(77, 172, 38);
620
        BREWER_COLOR[16] = new Color(202, 0, 32);
621
        BREWER_COLOR[17] = new Color(244, 165, 130);
622
        BREWER_COLOR[18] = new Color(146, 197, 222);
623
        BREWER_COLOR[19] = new Color(5, 113, 176);
624
        BREWER_COLOR[20] = new Color(202, 0, 32);
625
        BREWER_COLOR[21] = new Color(244, 165, 130);
626
        BREWER_COLOR[22] = new Color(186, 186, 186);
627
        BREWER_COLOR[23] = new Color(64, 64, 64);
628
        BREWER_COLOR[24] = new Color(215, 25, 28);
629
        BREWER_COLOR[25] = new Color(253, 174, 97);
630
        BREWER_COLOR[26] = new Color(171, 217, 233);
631
        BREWER_COLOR[27] = new Color(44, 123, 182);
632
        BREWER_COLOR[28] = new Color(215, 25, 28);
633
        BREWER_COLOR[29] = new Color(253, 174, 97);
634
        BREWER_COLOR[30] = new Color(171, 221, 164);
635
        BREWER_COLOR[31] = new Color(43, 131, 186);
636
        BREWER_COLOR[32] = new Color(215, 25, 28);
637
        BREWER_COLOR[33] = new Color(253, 174, 97);
638
        BREWER_COLOR[34] = new Color(166, 217, 106);
639
        BREWER_COLOR[35] = new Color(26, 150, 65);
640
    }
641

    
642
}