add geoservice mapping functionality
[cdmlib.git] / cdmlib-ext / src / main / java / eu / etaxonomy / cdm / ext / geo / EditGeoServiceUtilities.java
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.getAreas().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.getAreas().size() > 0){
563 //FIXME multiple layers
564 String layer = areas.getAreas().keySet().iterator().next();
565 Map<String, List<String>> fields = areas.getAreas().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 }