Project

General

Profile

Download (28 KB) Statistics
| Branch: | Tag: | Revision:
1
// $Id$
2
/**
3
* Copyright (C) 2007 EDIT
4
* European Distributed Institute of Taxonomy
5
* http://www.e-taxonomy.eu
6
*
7
* The contents of this file are subject to the Mozilla Public License Version 1.1
8
* See LICENSE.TXT at the top of this package for the full license terms.
9
*/
10

    
11
package eu.etaxonomy.cdm.ext.geo;
12

    
13
import java.awt.Color;
14
import java.io.UnsupportedEncodingException;
15
import java.net.URLEncoder;
16
import java.util.ArrayList;
17
import java.util.HashMap;
18
import java.util.HashSet;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Set;
22
import java.util.UUID;
23

    
24
import javax.persistence.Transient;
25

    
26
import org.apache.commons.lang.StringUtils;
27
import org.apache.log4j.Logger;
28

    
29
import eu.etaxonomy.cdm.common.CdmUtils;
30
import eu.etaxonomy.cdm.model.common.Language;
31
import eu.etaxonomy.cdm.model.common.Representation;
32
import eu.etaxonomy.cdm.model.common.TermVocabulary;
33
import eu.etaxonomy.cdm.model.description.Distribution;
34
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTermBase;
35
import eu.etaxonomy.cdm.model.description.PresenceTerm;
36
import eu.etaxonomy.cdm.model.location.NamedArea;
37
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
38
import eu.etaxonomy.cdm.model.location.Point;
39
import eu.etaxonomy.cdm.model.location.TdwgArea;
40
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
41
import eu.etaxonomy.cdm.model.occurrence.FieldObservation;
42
import eu.etaxonomy.cdm.model.occurrence.LivingBeing;
43
import eu.etaxonomy.cdm.model.occurrence.Observation;
44
import eu.etaxonomy.cdm.model.occurrence.Specimen;
45
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
46
import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
47

    
48
/**
49
 * Class implementing the business logic for creating the map service string for
50
 * a given set of distributions. See {@link EditGeoService} as API for the given functionality.
51
 * 
52
 * @see EditGeoService 
53
 * 
54
 * @author a.mueller
55
 * @created 17.11.2008
56
 * @version 1.0
57
 */
58
public class EditGeoServiceUtilities {
59
    private static final Logger logger = Logger.getLogger(EditGeoServiceUtilities.class);
60

    
61
    private static PresenceAbsenceTermBase<?> defaultStatus = PresenceTerm.PRESENT();
62

    
63
    private static IDefinedTermDao termDao;
64
   
65
    
66
    
67
    /**
68
     * @param termDao
69
     */
70
    public static void setTermDao(IDefinedTermDao termDao) {
71
        EditGeoServiceUtilities.termDao= termDao;
72
    }
73

    
74
    private static HashMap<Class<? extends SpecimenOrObservationBase>, Color> defaultSpecimenOrObservationTypeColors = null;
75

    
76
    private static HashMap<Class<? extends SpecimenOrObservationBase>, Color> getDefaultSpecimenOrObservationTypeColors() {
77
        if(defaultSpecimenOrObservationTypeColors == null){
78
            defaultSpecimenOrObservationTypeColors = new HashMap<Class<? extends SpecimenOrObservationBase>, Color>();
79
            defaultSpecimenOrObservationTypeColors.put(FieldObservation.class, Color.ORANGE);
80
            defaultSpecimenOrObservationTypeColors.put(DerivedUnit.class, Color.RED);
81
            defaultSpecimenOrObservationTypeColors.put(LivingBeing.class, Color.GREEN);
82
            defaultSpecimenOrObservationTypeColors.put(Observation.class, Color.ORANGE);
83
            defaultSpecimenOrObservationTypeColors.put(Specimen.class, Color.GRAY);
84
        }
85
        return defaultSpecimenOrObservationTypeColors;
86
    }
87

    
88

    
89
    private static HashMap<PresenceAbsenceTermBase<?>, Color> defaultPresenceAbsenceTermBaseColors = null;
90

    
91
    private static HashMap<PresenceAbsenceTermBase<?>, Color> getDefaultPresenceAbsenceTermBaseColors() {
92
        if(defaultPresenceAbsenceTermBaseColors == null){
93
            defaultPresenceAbsenceTermBaseColors = new HashMap<PresenceAbsenceTermBase<?>, Color>();
94
            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.PRESENT(), Color.decode("0x4daf4a"));
95
            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.NATIVE(), Color.decode("0x4daf4a"));
96
            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.NATIVE_DOUBTFULLY_NATIVE(), Color.decode("0x377eb8"));
97
            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.CULTIVATED(), Color.decode("0x984ea3"));
98
            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.INTRODUCED(), Color.decode("0xff7f00"));
99
            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.INTRODUCED_ADVENTITIOUS(), Color.decode("0xffff33"));
100
            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.INTRODUCED_CULTIVATED(), Color.decode("0xa65628"));
101
            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.INTRODUCED_NATURALIZED(), Color.decode("0xf781bf"));
102

    
103
            /*
104
             * and now something very hacky ...
105
             * ONLY-A-TEST is set by the Test class EditGeoServiceTest
106
             *
107
             * TODO remove according line from
108
             * EditGeoServiceTest.setUp() when the hardcoded colors for flora of
109
             * cyprus are no further needed !!
110
             */
111
            String onlyTest = System.getProperty("ONLY-A-TEST"); //
112
            if(onlyTest != null && onlyTest.equals("TRUE")){
113
                return defaultPresenceAbsenceTermBaseColors;
114
            }
115
            //special colors for flora of cyprus !!! see HACK above !!!
116
            UUID indigenousUuid = UUID.fromString("b325859b-504b-45e0-9ef0-d5c1602fcc0f");
117
            UUID indigenousQUuid = UUID.fromString("17bc601f-53eb-4997-a4bc-c03ce5bfd1d3");
118

    
119
            UUID cultivatedQUuid = UUID.fromString("4f31bfc8-3058-4d83-aea5-3a1fe9773f9f");
120

    
121
            UUID casualUuid = UUID.fromString("5e81353c-38a3-4ca6-b979-0d9abc93b877");
122
            UUID casualQUuid = UUID.fromString("73f75493-1185-4a3e-af1e-9a1f2e8dadb7");
123

    
124
            UUID naturalizedNonInvasiveUuid = UUID.fromString("1b025e8b-901a-42e8-9739-119b410c6f03");
125
            UUID naturalizedNonInvasiveQUuid = UUID.fromString("11f56e2f-c16c-4b3d-a870-bb5d3b20e624");
126

    
127
            UUID naturalizedInvasiveUuid = UUID.fromString("faf2d271-868a-4bf7-b0b8-a1c5ab309de2");
128
            UUID naturalizedInvasiveQUuid = UUID.fromString("ac429d5f-e8ad-49ae-a41c-e4779b58b96a");
129

    
130
            UUID questionablelUuid = UUID.fromString("4b48f675-a6cf-49f3-a5ba-77e2c2979eb3");
131
            UUID questionableQUuid = UUID.fromString("914e7393-1314-4632-bc45-5eff3dc1e424");
132

    
133
            UUID reportedInErrorUuid = UUID.fromString("38604788-cf05-4607-b155-86db456f7680");
134

    
135
            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(indigenousUuid), Color.decode("0x339966"));
136
            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(indigenousQUuid), Color.decode("0x339966"));
137

    
138
            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(cultivatedQUuid), Color.decode("0xbdb76b"));
139

    
140
            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(casualUuid), Color.decode("0xffff00"));
141
            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(casualQUuid), Color.decode("0xffff00"));
142

    
143
            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(naturalizedNonInvasiveUuid), Color.decode("0xff9900"));
144
            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(naturalizedNonInvasiveQUuid), Color.decode("0xff9900"));
145

    
146
            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(naturalizedInvasiveUuid), Color.decode("0xff0000"));
147
            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(naturalizedInvasiveQUuid), Color.decode("0xff0000"));
148

    
149
            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(questionablelUuid), Color.decode("0x00ccff"));
150
            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(questionableQUuid), Color.decode("0x00ccff"));
151

    
152
            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(reportedInErrorUuid), Color.decode("0xcccccc"));
153

    
154
        }
155
        return defaultPresenceAbsenceTermBaseColors;
156
    }
157

    
158

    
159

    
160
    private static final String SUBENTRY_DELIMITER = ",";
161
    private static final String ENTRY_DELIMITER = ";";
162
    static final String ID_FROM_VALUES_SEPARATOR = ":";
163
    static final String VALUE_LIST_ENTRY_SEPARATOR = "|";
164
    static final String VALUE_SUPER_LIST_ENTRY_SEPARATOR = "||";
165

    
166

    
167

    
168
    //preliminary implementation for TDWG areas
169
    /**
170
     * Returns the parameter String for the EDIT geo webservice to create a
171
     * dsitribution map.
172
     *
173
     * @param distributions
174
     *            A set of distributions that should be shown on the map
175
     * @param presenceAbsenceTermColors
176
     *            A map that defines the colors of PresenceAbsenceTerms. The
177
     *            PresenceAbsenceTerms are defined by their uuid. If a
178
     *            PresenceAbsenceTerm is not included in this map, it's default
179
     *            color is taken instead. If the map == null all terms are
180
     *            colored by their default color.
181
     * @param width
182
     *            The maps width
183
     * @param height
184
     *            The maps height
185
     * @param bbox
186
     *            The maps bounding box (e.g. "-180,-90,180,90" for the whole
187
     *            world)
188
     * @param projectToLayer
189
     *            name of a layer which is representing a specific
190
     *            {@link NamedAreaLevel} Supply this parameter if you to project
191
     *            all other distribution area levels to this layer.
192
     * @param layer
193
     *            The layer that is responsible for background borders and
194
     *            colors. Use the name for the layer. If null 'earth' is taken
195
     *            as default.
196
     * @return the parameter string or an empty string if the
197
     *         <code>distributions</code> set was null or empty.
198
     */
199
    @Transient
200
    public static String getDistributionServiceRequestParameterString(
201
            Set<Distribution> distributions,
202
            IGeoServiceAreaMapping mapping,
203
            Map<PresenceAbsenceTermBase<?>,Color> presenceAbsenceTermColors,
204
            int width,
205
            int height,
206
            String bbox,
207
            String baseLayerName,
208
            String projectToLayer,
209
            List<Language> languages){
210

    
211

    
212
        /**
213
         * generateMultipleAreaDataParameters switches between the two possible styles:
214
         * 1. ad=layername1:area-data||layername2:area-data
215
         * 2. ad=layername1:area-data&ad=layername2:area-data
216
         */
217
        boolean generateMultipleAreaDataParameters = false;
218

    
219
        List<String>  perLayerAreaData = new ArrayList<String>();
220
        List<String> areaStyles = new ArrayList<String>();
221
        List<String> legendLabels = new ArrayList<String>();
222

    
223

    
224
        String borderWidth = "0.1";
225
        String borderColorRgb = "";
226
        String borderDashingPattern = "";
227

    
228

    
229
		//handle empty set
230
		if(distributions == null || distributions.size() == 0){
231
			return "";
232
		}
233
		
234
		Map<String, Map<Integer, Set<Distribution>>> layerMap = new HashMap<String, Map<Integer, Set<Distribution>>>();
235
		List<PresenceAbsenceTermBase<?>> statusList = new ArrayList<PresenceAbsenceTermBase<?>>();
236
		
237
		groupStylesAndLayers(distributions, layerMap, statusList, mapping);
238

    
239

    
240
        presenceAbsenceTermColors = mergeMaps(getDefaultPresenceAbsenceTermBaseColors(), presenceAbsenceTermColors);
241

    
242
        Map<String, String> parameters = new HashMap<String, String>();
243

    
244
        //bbox
245
        if (bbox != null){
246
            parameters.put("bbox", bbox);
247
        }
248
        // map size
249
        String ms = compileMapSizeParameterValue(width, height);
250
        if(ms != null){
251
            parameters.put("ms", ms);
252
        }
253
        //layer
254
        if (StringUtils.isBlank(baseLayerName)){
255
            baseLayerName = "earth";
256
        }
257
        parameters.put("l", baseLayerName);
258

    
259
        //style
260
        int i = 0;
261
        for (PresenceAbsenceTermBase<?> status: statusList){
262

    
263
            char styleId = getStyleAbbrev(i);
264

    
265
            //getting the area title
266
            if (languages == null){
267
                languages = new ArrayList<Language>();
268
            }
269
            if (languages.size() == 0){
270
                languages.add(Language.DEFAULT());
271
            }
272
            Representation representation = status.getPreferredRepresentation(languages);
273
            String statusLabel = representation.getLabel();
274
            //statusLabel.replace('introduced: ', '');
275
            statusLabel = statusLabel.replace("introduced: ", "introduced, ");
276
            statusLabel = statusLabel.replace("native: ", "native,  ");
277

    
278
            //getting the area color
279
            Color statusColor = presenceAbsenceTermColors.get(status);
280
            String fillColorRgb;
281
            if (statusColor != null){
282
                fillColorRgb = Integer.toHexString(statusColor.getRGB()).substring(2);
283
            }else{
284
                if(status != null){
285
                    fillColorRgb = status.getDefaultColor(); //TODO
286
                } else {
287
                    fillColorRgb = defaultStatus.getDefaultColor();
288
                }
289
            }
290
            String styleValues = StringUtils.join(new String[]{fillColorRgb, borderColorRgb, borderWidth, borderDashingPattern}, ',');
291

    
292
            areaStyles.add(styleId + ID_FROM_VALUES_SEPARATOR + styleValues);
293
            legendLabels.add(styleId + ID_FROM_VALUES_SEPARATOR + encode(statusLabel));
294
            i++;
295
        }
296

    
297
        if(areaStyles.size() > 0){
298
            parameters.put("as", StringUtils.join(areaStyles.iterator(), VALUE_LIST_ENTRY_SEPARATOR));
299
        }
300
        if(legendLabels.size() > 0){
301
            parameters.put("title", StringUtils.join(legendLabels.iterator(), VALUE_LIST_ENTRY_SEPARATOR));
302
        }
303

    
304
		// area data
305
		List<String> stylesPerLayer;
306
		List<String> areasPerStyle;
307
		for (String layerString : layerMap.keySet()){
308
			// each layer
309
			stylesPerLayer = new ArrayList<String>();
310
			Map<Integer, Set<Distribution>> styleMap = layerMap.get(layerString);
311
			for (int style: styleMap.keySet()){
312
				// stylesPerLayer
313
				char styleChar = getStyleAbbrev(style);
314
				Set<Distribution> distributionSet = styleMap.get(style);
315
				areasPerStyle = new ArrayList<String>();
316
				for (Distribution distribution: distributionSet){
317
					// areasPerStyle
318
					areasPerStyle.add(encode(getAreaAbbrev(distribution, mapping)));
319
				}
320
				stylesPerLayer.add(styleChar + ID_FROM_VALUES_SEPARATOR + StringUtils.join(areasPerStyle.iterator(), SUBENTRY_DELIMITER));
321
			}
322
			perLayerAreaData.add(encode(layerString) + ID_FROM_VALUES_SEPARATOR + StringUtils.join(stylesPerLayer.iterator(), VALUE_LIST_ENTRY_SEPARATOR));
323
		}
324

    
325

    
326
        if(generateMultipleAreaDataParameters){
327
            // not generically possible since parameters can not contain duplicate keys with value "ad"
328
        } else {
329
            parameters.put("ad", StringUtils.join(perLayerAreaData.iterator(), VALUE_SUPER_LIST_ENTRY_SEPARATOR));
330
        }
331

    
332
        String queryString = makeQueryString(parameters);
333
        logger.debug("getDistributionServiceRequestParameterString(): " + queryString);
334

    
335
        return queryString;
336
    }
337

    
338
	/**
339
	 * Fills the layerMap and the statusList
340
	 * @param distributions
341
	 * @param layerMap
342
	 * @param statusList
343
	 */
344
	private static void groupStylesAndLayers(Set<Distribution> distributions,
345
			Map<String, Map<Integer,Set<Distribution>>> layerMap,
346
			List<PresenceAbsenceTermBase<?>> statusList,
347
			IGeoServiceAreaMapping mapping) {
348

    
349

    
350
		//iterate through distributions and group styles and layers
351
		//and collect necessary information
352
		for (Distribution distribution : distributions){
353
			//collect status
354
			PresenceAbsenceTermBase<?> status = distribution.getStatus();
355
			if(status == null){
356
				status = defaultStatus;
357
			}
358
			if (! statusList.contains(status)){
359
				statusList.add(status);
360
			}
361
			//group areas by layers and styles
362
			NamedArea area = distribution.getArea();
363

    
364

    
365
            addArea(layerMap, statusList, distribution, area, mapping);
366
        }
367
    }
368

    
369
	/**
370
	 * @param layerMap
371
	 * @param statusList
372
	 * @param distribution
373
	 * @param area
374
	 */
375
	private static void addArea(Map<String, Map<Integer, 
376
			Set<Distribution>>> layerMap, 
377
			List<PresenceAbsenceTermBase<?>> statusList,
378
			Distribution distribution, 
379
			NamedArea area,
380
			IGeoServiceAreaMapping mapping) {
381

    
382
        if (area != null){
383
            String geoLayerString = getWMSLayerName(area, mapping);
384

    
385
            if(geoLayerString == null){
386

    
387
                // if no layer is mapped this area descend into sub areas in order to project
388
                // the distribution to those
389
                for(NamedArea subArea : area.getIncludes()){
390
                    addArea(layerMap, statusList, distribution, subArea, mapping);
391
                }
392

    
393
            } else {
394

    
395
                Map<Integer, Set<Distribution>> styleMap = layerMap.get(geoLayerString);
396
                if (styleMap == null) {
397
                    styleMap = new HashMap<Integer, Set<Distribution>>();
398
                    layerMap.put(geoLayerString, styleMap);
399
                }
400
                addDistributionToMap(distribution, styleMap, statusList);
401

    
402
            }
403
        }
404
    }
405

    
406

    
407
    private static String compileMapSizeParameterValue(int width, int height) {
408

    
409
        String widthStr = "";
410
        String heightStr = "";
411

    
412
        if (width > 0) {
413
            widthStr = "" + width;
414
        }
415
        if (height > 0) {
416
            heightStr = SUBENTRY_DELIMITER + height;
417
        }
418
        String ms = widthStr + heightStr;
419
        if(ms.length() == 0){
420
            ms = null;
421
        }
422
        return ms;
423
    }
424

    
425
    /**
426
     * URI encode the given String
427
     * @param string
428
     * @return
429
     */
430
    private static String encode(String string) {
431
        String encoded = string;
432
        try {
433
            encoded = URLEncoder.encode(string, "UTF-8");
434
        } catch (UnsupportedEncodingException e) {
435
            logger.error(e);
436
        }
437
        return encoded;
438
    }
439

    
440
    /**
441
     * combine parameter into a URI query string fragment. The values will be
442
     * escaped correctly.
443
     *
444
     * @param parameters
445
     * @return a URI query string fragment
446
     */
447
    private static String makeQueryString(Map<String, String> parameters){
448
        StringBuilder queryString = new StringBuilder();
449
        for (String key : parameters.keySet()) {
450
            if(queryString.length() > 0){
451
                queryString.append('&');
452
            }
453
            if(key.equals("od") || key.equals("os") || key.equals("ms") || key.equals("ad") || key.equals("as") || key.equals("title") || key.equals("bbox")){
454
                queryString.append(key).append('=').append(parameters.get(key));
455
            } else {
456
                queryString.append(key).append('=').append(encode(parameters.get(key)));
457
            }
458
        }
459
        return queryString.toString();
460
    }
461

    
462
	private static String getAreaAbbrev(Distribution distribution, IGeoServiceAreaMapping mapping){
463
		NamedArea area = distribution.getArea();
464
		TermVocabulary<NamedArea> voc = area.getVocabulary();
465
		String result = null;
466
		if (voc !=  null && voc.getUuid().equals(TdwgArea.uuidTdwgAreaVocabulary) || voc.getUuid().equals(uuidCyprusDivisionsVocabulary) ){
467
			Representation representation = area.getRepresentation(Language.DEFAULT());
468
			result = representation.getAbbreviatedLabel();
469
			if (area.getLevel() != null && area.getLevel().equals(NamedAreaLevel.TDWG_LEVEL4())){
470
				result = result.replace("-", "");
471
			}
472
			
473
		}else{
474
			GeoServiceArea areas =mapping.valueOf(area);
475
			if ((areas != null) && areas.size()>0){
476
				//FIXME multiple layers
477
				List<String> values= areas.getAreasMap().values().iterator().next().values().iterator().next();
478
				for (String value : values){
479
					result = CdmUtils.concat(SUBENTRY_DELIMITER, result, value);
480
				}
481
			}
482
			
483
		}
484
		return CdmUtils.Nz(result, "-");
485
		
486
	}
487

    
488

    
489

    
490
    //Preliminary as long as user defined areas are not fully implemented
491
    public static final UUID uuidCyprusDivisionsVocabulary = UUID.fromString("2119f610-1f93-4d87-af28-40aeefaca100");
492

    
493
    private static List<String> projectToWMSSubLayer(NamedArea area){
494

    
495
        List<String> layerNames = new ArrayList<String>();
496
        String matchedLayerName = null;
497
        TermVocabulary<NamedArea> voc = area.getVocabulary();
498
        //TDWG areas
499
        if (voc.getUuid().equals(TdwgArea.uuidTdwgAreaVocabulary)){
500
            NamedAreaLevel level = area.getLevel();
501
            if (level != null) {
502
                //TODO integrate into CDM
503
                if (level.equals(NamedAreaLevel.TDWG_LEVEL1())) {
504
                    matchedLayerName = "tdwg1" ;
505
                } else if (level.equals(NamedAreaLevel.TDWG_LEVEL2())) {
506
                    matchedLayerName = "tdwg2";
507
                }else if (level.equals(NamedAreaLevel.TDWG_LEVEL3())) {
508
                    matchedLayerName = "tdwg3";
509
                }else if (level.equals(NamedAreaLevel.TDWG_LEVEL4())) {
510
                    matchedLayerName = "tdwg4";
511
                }
512
            }
513
            //unrecognized tdwg area
514

    
515
        }
516
        //TODO hardcoded for cyprus (as long as user defined areas are not fully implemented). Remove afterwards.
517
        if (voc.getUuid().equals(uuidCyprusDivisionsVocabulary)){
518
            matchedLayerName = "cyprusdivs:bdcode";
519
        }
520

    
521
        // check if the matched layer equals the layer to project to
522
        // if not: recurse into the sub-level in order to find the specified one.
523
        String[] matchedLayerNameTokens = StringUtils.split(matchedLayerName, ':');
524
//		if(matchedLayerNameTokens.length > 0 &&  matchedLayerNameTokens[0] != projectToLayer){
525
//			for (NamedArea subArea : area.getIncludes()){
526
//
527
//			}
528
            //
529
            // add all sub areas
530
//		}
531

    
532
        return null;
533
    }
534

    
535
	private static String getWMSLayerName(NamedArea area, IGeoServiceAreaMapping mapping){
536
		TermVocabulary<NamedArea> voc = area.getVocabulary();
537
		//TDWG areas
538
		if (voc.getUuid().equals(TdwgArea.uuidTdwgAreaVocabulary)){
539
			NamedAreaLevel level = area.getLevel();
540
			if (level != null) {
541
				//TODO integrate into CDM
542
				if (level.equals(NamedAreaLevel.TDWG_LEVEL1())) {
543
					return "tdwg1";
544
				} else if (level.equals(NamedAreaLevel.TDWG_LEVEL2())) {
545
					return "tdwg2";
546
				}else if (level.equals(NamedAreaLevel.TDWG_LEVEL3())) {
547
					return "tdwg3";
548
				}else if (level.equals(NamedAreaLevel.TDWG_LEVEL4())) {
549
					return "tdwg4";
550
				}
551
			}
552
			//unrecognized tdwg area
553
			return null;
554

    
555
		}
556
		//hardcoded for cyprus (as long as user defined areas are not fully implemented). Remove afterwards.
557
		if (voc.getUuid().equals(uuidCyprusDivisionsVocabulary)){
558
			return "cyprusdivs:bdcode";
559
		}
560
		
561
		GeoServiceArea areas = mapping.valueOf(area);
562
		if (areas != null && areas.getAreasMap().size() > 0){
563
			//FIXME multiple layers
564
			String layer = areas.getAreasMap().keySet().iterator().next();
565
			Map<String, List<String>> fields = areas.getAreasMap().get(layer);
566
			String field = fields.keySet().iterator().next();
567
			return layer + ":" + field;
568
		}
569
		
570
		return null;
571
	}
572

    
573

    
574
    private static void addDistributionToMap(Distribution distribution, Map<Integer, Set<Distribution>> styleMap,
575
            List<PresenceAbsenceTermBase<?>> statusList) {
576
        PresenceAbsenceTermBase<?> status = distribution.getStatus();
577
        if (status == null) {
578
            status = defaultStatus;
579
        }
580
        int style = statusList.indexOf(status);
581
        Set<Distribution> distributionSet = styleMap.get(style);
582
        if (distributionSet == null) {
583
            distributionSet = new HashSet<Distribution>();
584
            styleMap.put(style, distributionSet);
585
        }
586
        distributionSet.add(distribution);
587
    }
588

    
589
    /**
590
     * @param fieldObservationPoints
591
     * @param derivedUnitPoints
592
     * @param specimenOrObservationTypeColors
593
     * @param doReturnImage TODO
594
     * @param width
595
     * @param height
596
     * @param bbox
597
     * @param backLayer
598
     * @return
599
     * e.g.:
600
     * 	l=v%3Aatbi%2Ce_w_0
601
     *  &legend=0
602
     *  &image=false
603
     *  &recalculate=false
604
     *  &ms=400%2C350
605

    
606
     *  &od=1%3A44.29481%2C6.82161|44.29252%2C6.822873|44.29247%2C6.82346|44.29279%2C6.823678|44.29269%2C6.82394|44.28482%2C6.887252|44.11469%2C7.287144|44.11468%2C7.289168
607
     *  &os=1%3Ac%2FFFD700%2F10%2FAporrectodea caliginosa
608
     */
609
    public static String getOccurrenceServiceRequestParameterString(
610
            List<Point> fieldObservationPoints,
611
            List<Point> derivedUnitPoints,
612
            Map<Class<? extends SpecimenOrObservationBase>, Color> specimenOrObservationTypeColors,
613
            Boolean doReturnImage, Integer width, Integer height, String bbox, String backLayer) {
614

    
615
            specimenOrObservationTypeColors = mergeMaps(getDefaultSpecimenOrObservationTypeColors(), specimenOrObservationTypeColors);
616

    
617
            Map<String, String> parameters = new HashMap<String, String>();
618
            parameters.put("legend", "0");
619
            parameters.put("image", doReturnImage != null && doReturnImage ? "true" : "false");
620
            parameters.put("recalculate", "false"); // TODO add parameter to method
621
            if(bbox != null){
622
                parameters.put("bbox", bbox);
623
            }
624
            if(width != null || height != null){
625
                parameters.put("ms", compileMapSizeParameterValue(width, height));
626
            }
627

    
628
            Map<String, String> styleAndData = new HashMap<String, String>();
629

    
630
            addToStyleAndData(fieldObservationPoints, FieldObservation.class, specimenOrObservationTypeColors, styleAndData);
631
            addToStyleAndData(derivedUnitPoints, DerivedUnit.class, specimenOrObservationTypeColors, styleAndData);
632

    
633
            parameters.put("os", StringUtils.join(styleAndData.keySet().iterator(), "||"));
634
            parameters.put("od", StringUtils.join(styleAndData.values().iterator(), "||"));
635

    
636
            String queryString = makeQueryString(parameters);
637

    
638
            logger.info(queryString);
639

    
640
        return queryString;
641
    }
642

    
643
    /**
644
     * @param <T>
645
     * @param <S>
646
     * @param defaultMap
647
     * @param overrideMap
648
     * @return
649
     */
650
    private static <T, S> Map<T, S> mergeMaps(Map<T, S> defaultMap, Map<T, S> overrideMap) {
651
        Map<T, S> tmpMap = new HashMap<T, S>();
652
        tmpMap.putAll(defaultMap);
653
        if(overrideMap != null){
654
            tmpMap.putAll(overrideMap);
655
        }
656
        return tmpMap;
657
    }
658

    
659
    private static void addToStyleAndData(
660
            List<Point> points,
661
            Class<? extends SpecimenOrObservationBase> specimenOrObservationType,
662
            Map<Class<? extends SpecimenOrObservationBase>, Color> specimenOrObservationTypeColors, Map<String, String> styleAndData) {
663

    
664
        //TODO add markerShape and size and Label to specimenOrObservationTypeColors -> Map<Class<SpecimenOrObservationBase<?>>, MapStyle>
665

    
666
        if(points != null && points.size()>0){
667
            String style =  "c/" + Integer.toHexString(specimenOrObservationTypeColors.get(specimenOrObservationType).getRGB()).substring(2) + "/10/noLabel";
668
            StringBuilder data = new StringBuilder();
669
            for(Point point : points){
670
                if(data.length() > 0){
671
                    data.append('|');
672
                }
673
                data.append(point.getLatitude() + "," + point.getLongitude());
674
            }
675
            int index = styleAndData.size() + 1;
676
            styleAndData.put(index + ":" +style, index + ":" +data.toString());
677
        }
678
    }
679

    
680

    
681
    /**
682
     * transform an integer (style counter) into a valid character representing a style.
683
     * 0-25 => a-z<br>
684
     * 26-51 => A-Z<br>
685
     * i not in {0,...,51} is undefined
686
     * @param i
687
     * @return
688
     */
689
    private static char getStyleAbbrev(int i){
690
        i++;
691
        int ascii = 96 + i;
692
        if (i >26){
693
            ascii = 64 + i;
694
        }
695
        return (char)ascii;
696
    }
697

    
698
}
(2-2/6)