3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
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.
11 package eu
.etaxonomy
.cdm
.ext
.geo
;
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
;
22 import java
.util
.UUID
;
24 import javax
.persistence
.Transient
;
26 import org
.apache
.commons
.lang
.StringUtils
;
27 import org
.apache
.log4j
.Logger
;
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
;
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.
58 public class EditGeoServiceUtilities
{
59 private static final Logger logger
= Logger
.getLogger(EditGeoServiceUtilities
.class);
61 private static PresenceAbsenceTermBase
<?
> defaultStatus
= PresenceTerm
.PRESENT();
63 private static IDefinedTermDao termDao
;
70 public static void setTermDao(IDefinedTermDao termDao
) {
71 EditGeoServiceUtilities
.termDao
= termDao
;
74 private static HashMap
<Class
<?
extends SpecimenOrObservationBase
>, Color
> defaultSpecimenOrObservationTypeColors
= null;
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
);
85 return defaultSpecimenOrObservationTypeColors
;
89 private static HashMap
<PresenceAbsenceTermBase
<?
>, Color
> defaultPresenceAbsenceTermBaseColors
= null;
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"));
104 * and now something very hacky ...
105 * ONLY-A-TEST is set by the Test class EditGeoServiceTest
107 * TODO remove according line from
108 * EditGeoServiceTest.setUp() when the hardcoded colors for flora of
109 * cyprus are no further needed !!
111 String onlyTest
= System
.getProperty("ONLY-A-TEST"); //
112 if(onlyTest
!= null && onlyTest
.equals("TRUE")){
113 return defaultPresenceAbsenceTermBaseColors
;
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");
119 UUID cultivatedQUuid
= UUID
.fromString("4f31bfc8-3058-4d83-aea5-3a1fe9773f9f");
121 UUID casualUuid
= UUID
.fromString("5e81353c-38a3-4ca6-b979-0d9abc93b877");
122 UUID casualQUuid
= UUID
.fromString("73f75493-1185-4a3e-af1e-9a1f2e8dadb7");
124 UUID naturalizedNonInvasiveUuid
= UUID
.fromString("1b025e8b-901a-42e8-9739-119b410c6f03");
125 UUID naturalizedNonInvasiveQUuid
= UUID
.fromString("11f56e2f-c16c-4b3d-a870-bb5d3b20e624");
127 UUID naturalizedInvasiveUuid
= UUID
.fromString("faf2d271-868a-4bf7-b0b8-a1c5ab309de2");
128 UUID naturalizedInvasiveQUuid
= UUID
.fromString("ac429d5f-e8ad-49ae-a41c-e4779b58b96a");
130 UUID questionablelUuid
= UUID
.fromString("4b48f675-a6cf-49f3-a5ba-77e2c2979eb3");
131 UUID questionableQUuid
= UUID
.fromString("914e7393-1314-4632-bc45-5eff3dc1e424");
133 UUID reportedInErrorUuid
= UUID
.fromString("38604788-cf05-4607-b155-86db456f7680");
135 defaultPresenceAbsenceTermBaseColors
.put((PresenceAbsenceTermBase
<?
>) termDao
.load(indigenousUuid
), Color
.decode("0x339966"));
136 defaultPresenceAbsenceTermBaseColors
.put((PresenceAbsenceTermBase
<?
>) termDao
.load(indigenousQUuid
), Color
.decode("0x339966"));
138 defaultPresenceAbsenceTermBaseColors
.put((PresenceAbsenceTermBase
<?
>) termDao
.load(cultivatedQUuid
), Color
.decode("0xbdb76b"));
140 defaultPresenceAbsenceTermBaseColors
.put((PresenceAbsenceTermBase
<?
>) termDao
.load(casualUuid
), Color
.decode("0xffff00"));
141 defaultPresenceAbsenceTermBaseColors
.put((PresenceAbsenceTermBase
<?
>) termDao
.load(casualQUuid
), Color
.decode("0xffff00"));
143 defaultPresenceAbsenceTermBaseColors
.put((PresenceAbsenceTermBase
<?
>) termDao
.load(naturalizedNonInvasiveUuid
), Color
.decode("0xff9900"));
144 defaultPresenceAbsenceTermBaseColors
.put((PresenceAbsenceTermBase
<?
>) termDao
.load(naturalizedNonInvasiveQUuid
), Color
.decode("0xff9900"));
146 defaultPresenceAbsenceTermBaseColors
.put((PresenceAbsenceTermBase
<?
>) termDao
.load(naturalizedInvasiveUuid
), Color
.decode("0xff0000"));
147 defaultPresenceAbsenceTermBaseColors
.put((PresenceAbsenceTermBase
<?
>) termDao
.load(naturalizedInvasiveQUuid
), Color
.decode("0xff0000"));
149 defaultPresenceAbsenceTermBaseColors
.put((PresenceAbsenceTermBase
<?
>) termDao
.load(questionablelUuid
), Color
.decode("0x00ccff"));
150 defaultPresenceAbsenceTermBaseColors
.put((PresenceAbsenceTermBase
<?
>) termDao
.load(questionableQUuid
), Color
.decode("0x00ccff"));
152 defaultPresenceAbsenceTermBaseColors
.put((PresenceAbsenceTermBase
<?
>) termDao
.load(reportedInErrorUuid
), Color
.decode("0xcccccc"));
155 return defaultPresenceAbsenceTermBaseColors
;
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
= "||";
168 //preliminary implementation for TDWG areas
170 * Returns the parameter String for the EDIT geo webservice to create a
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.
186 * The maps bounding box (e.g. "-180,-90,180,90" for the whole
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.
193 * The layer that is responsible for background borders and
194 * colors. Use the name for the layer. If null 'earth' is taken
196 * @return the parameter string or an empty string if the
197 * <code>distributions</code> set was null or empty.
200 public static String
getDistributionServiceRequestParameterString(
201 Set
<Distribution
> distributions
,
202 IGeoServiceAreaMapping mapping
,
203 Map
<PresenceAbsenceTermBase
<?
>,Color
> presenceAbsenceTermColors
,
207 String baseLayerName
,
208 String projectToLayer
,
209 List
<Language
> languages
){
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
217 boolean generateMultipleAreaDataParameters
= false;
219 List
<String
> perLayerAreaData
= new ArrayList
<String
>();
220 List
<String
> areaStyles
= new ArrayList
<String
>();
221 List
<String
> legendLabels
= new ArrayList
<String
>();
224 String borderWidth
= "0.1";
225 String borderColorRgb
= "";
226 String borderDashingPattern
= "";
230 if(distributions
== null || distributions
.size() == 0){
234 Map
<String
, Map
<Integer
, Set
<Distribution
>>> layerMap
= new HashMap
<String
, Map
<Integer
, Set
<Distribution
>>>();
235 List
<PresenceAbsenceTermBase
<?
>> statusList
= new ArrayList
<PresenceAbsenceTermBase
<?
>>();
237 groupStylesAndLayers(distributions
, layerMap
, statusList
, mapping
);
240 presenceAbsenceTermColors
= mergeMaps(getDefaultPresenceAbsenceTermBaseColors(), presenceAbsenceTermColors
);
242 Map
<String
, String
> parameters
= new HashMap
<String
, String
>();
246 parameters
.put("bbox", bbox
);
249 String ms
= compileMapSizeParameterValue(width
, height
);
251 parameters
.put("ms", ms
);
254 if (StringUtils
.isBlank(baseLayerName
)){
255 baseLayerName
= "earth";
257 parameters
.put("l", baseLayerName
);
261 for (PresenceAbsenceTermBase
<?
> status
: statusList
){
263 char styleId
= getStyleAbbrev(i
);
265 //getting the area title
266 if (languages
== null){
267 languages
= new ArrayList
<Language
>();
269 if (languages
.size() == 0){
270 languages
.add(Language
.DEFAULT());
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, ");
278 //getting the area color
279 Color statusColor
= presenceAbsenceTermColors
.get(status
);
281 if (statusColor
!= null){
282 fillColorRgb
= Integer
.toHexString(statusColor
.getRGB()).substring(2);
285 fillColorRgb
= status
.getDefaultColor(); //TODO
287 fillColorRgb
= defaultStatus
.getDefaultColor();
290 String styleValues
= StringUtils
.join(new String
[]{fillColorRgb
, borderColorRgb
, borderWidth
, borderDashingPattern
}, ',');
292 areaStyles
.add(styleId
+ ID_FROM_VALUES_SEPARATOR
+ styleValues
);
293 legendLabels
.add(styleId
+ ID_FROM_VALUES_SEPARATOR
+ encode(statusLabel
));
297 if(areaStyles
.size() > 0){
298 parameters
.put("as", StringUtils
.join(areaStyles
.iterator(), VALUE_LIST_ENTRY_SEPARATOR
));
300 if(legendLabels
.size() > 0){
301 parameters
.put("title", StringUtils
.join(legendLabels
.iterator(), VALUE_LIST_ENTRY_SEPARATOR
));
305 List
<String
> stylesPerLayer
;
306 List
<String
> areasPerStyle
;
307 for (String layerString
: layerMap
.keySet()){
309 stylesPerLayer
= new ArrayList
<String
>();
310 Map
<Integer
, Set
<Distribution
>> styleMap
= layerMap
.get(layerString
);
311 for (int style
: styleMap
.keySet()){
313 char styleChar
= getStyleAbbrev(style
);
314 Set
<Distribution
> distributionSet
= styleMap
.get(style
);
315 areasPerStyle
= new ArrayList
<String
>();
316 for (Distribution distribution
: distributionSet
){
318 areasPerStyle
.add(encode(getAreaAbbrev(distribution
, mapping
)));
320 stylesPerLayer
.add(styleChar
+ ID_FROM_VALUES_SEPARATOR
+ StringUtils
.join(areasPerStyle
.iterator(), SUBENTRY_DELIMITER
));
322 perLayerAreaData
.add(encode(layerString
) + ID_FROM_VALUES_SEPARATOR
+ StringUtils
.join(stylesPerLayer
.iterator(), VALUE_LIST_ENTRY_SEPARATOR
));
326 if(generateMultipleAreaDataParameters
){
327 // not generically possible since parameters can not contain duplicate keys with value "ad"
329 parameters
.put("ad", StringUtils
.join(perLayerAreaData
.iterator(), VALUE_SUPER_LIST_ENTRY_SEPARATOR
));
332 String queryString
= makeQueryString(parameters
);
333 logger
.debug("getDistributionServiceRequestParameterString(): " + queryString
);
339 * Fills the layerMap and the statusList
340 * @param distributions
344 private static void groupStylesAndLayers(Set
<Distribution
> distributions
,
345 Map
<String
, Map
<Integer
,Set
<Distribution
>>> layerMap
,
346 List
<PresenceAbsenceTermBase
<?
>> statusList
,
347 IGeoServiceAreaMapping mapping
) {
350 //iterate through distributions and group styles and layers
351 //and collect necessary information
352 for (Distribution distribution
: distributions
){
354 PresenceAbsenceTermBase
<?
> status
= distribution
.getStatus();
356 status
= defaultStatus
;
358 if (! statusList
.contains(status
)){
359 statusList
.add(status
);
361 //group areas by layers and styles
362 NamedArea area
= distribution
.getArea();
365 addArea(layerMap
, statusList
, distribution
, area
, mapping
);
372 * @param distribution
375 private static void addArea(Map
<String
, Map
<Integer
,
376 Set
<Distribution
>>> layerMap
,
377 List
<PresenceAbsenceTermBase
<?
>> statusList
,
378 Distribution distribution
,
380 IGeoServiceAreaMapping mapping
) {
383 String geoLayerString
= getWMSLayerName(area
, mapping
);
385 if(geoLayerString
== null){
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
);
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
);
400 addDistributionToMap(distribution
, styleMap
, statusList
);
407 private static String
compileMapSizeParameterValue(int width
, int height
) {
409 String widthStr
= "";
410 String heightStr
= "";
413 widthStr
= "" + width
;
416 heightStr
= SUBENTRY_DELIMITER
+ height
;
418 String ms
= widthStr
+ heightStr
;
419 if(ms
.length() == 0){
426 * URI encode the given String
430 private static String
encode(String string
) {
431 String encoded
= string
;
433 encoded
= URLEncoder
.encode(string
, "UTF-8");
434 } catch (UnsupportedEncodingException e
) {
441 * combine parameter into a URI query string fragment. The values will be
445 * @return a URI query string fragment
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('&');
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
));
456 queryString
.append(key
).append('=').append(encode(parameters
.get(key
)));
459 return queryString
.toString();
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("-", "");
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
);
484 return CdmUtils
.Nz(result
, "-");
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");
493 private static List
<String
> projectToWMSSubLayer(NamedArea area
){
495 List
<String
> layerNames
= new ArrayList
<String
>();
496 String matchedLayerName
= null;
497 TermVocabulary
<NamedArea
> voc
= area
.getVocabulary();
499 if (voc
.getUuid().equals(TdwgArea
.uuidTdwgAreaVocabulary
)){
500 NamedAreaLevel level
= area
.getLevel();
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";
513 //unrecognized tdwg area
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";
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()){
535 private static String
getWMSLayerName(NamedArea area
, IGeoServiceAreaMapping mapping
){
536 TermVocabulary
<NamedArea
> voc
= area
.getVocabulary();
538 if (voc
.getUuid().equals(TdwgArea
.uuidTdwgAreaVocabulary
)){
539 NamedAreaLevel level
= area
.getLevel();
541 //TODO integrate into CDM
542 if (level
.equals(NamedAreaLevel
.TDWG_LEVEL1())) {
544 } else if (level
.equals(NamedAreaLevel
.TDWG_LEVEL2())) {
546 }else if (level
.equals(NamedAreaLevel
.TDWG_LEVEL3())) {
548 }else if (level
.equals(NamedAreaLevel
.TDWG_LEVEL4())) {
552 //unrecognized tdwg area
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";
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
;
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
;
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
);
586 distributionSet
.add(distribution
);
590 * @param fieldObservationPoints
591 * @param derivedUnitPoints
592 * @param specimenOrObservationTypeColors
593 * @param doReturnImage TODO
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
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
) {
615 specimenOrObservationTypeColors
= mergeMaps(getDefaultSpecimenOrObservationTypeColors(), specimenOrObservationTypeColors
);
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
622 parameters
.put("bbox", bbox
);
624 if(width
!= null || height
!= null){
625 parameters
.put("ms", compileMapSizeParameterValue(width
, height
));
628 Map
<String
, String
> styleAndData
= new HashMap
<String
, String
>();
630 addToStyleAndData(fieldObservationPoints
, FieldObservation
.class, specimenOrObservationTypeColors
, styleAndData
);
631 addToStyleAndData(derivedUnitPoints
, DerivedUnit
.class, specimenOrObservationTypeColors
, styleAndData
);
633 parameters
.put("os", StringUtils
.join(styleAndData
.keySet().iterator(), "||"));
634 parameters
.put("od", StringUtils
.join(styleAndData
.values().iterator(), "||"));
636 String queryString
= makeQueryString(parameters
);
638 logger
.info(queryString
);
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
);
659 private static void addToStyleAndData(
661 Class
<?
extends SpecimenOrObservationBase
> specimenOrObservationType
,
662 Map
<Class
<?
extends SpecimenOrObservationBase
>, Color
> specimenOrObservationTypeColors
, Map
<String
, String
> styleAndData
) {
664 //TODO add markerShape and size and Label to specimenOrObservationTypeColors -> Map<Class<SpecimenOrObservationBase<?>>, MapStyle>
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){
673 data
.append(point
.getLatitude() + "," + point
.getLongitude());
675 int index
= styleAndData
.size() + 1;
676 styleAndData
.put(index
+ ":" +style
, index
+ ":" +data
.toString());
682 * transform an integer (style counter) into a valid character representing a style.
685 * i not in {0,...,51} is undefined
689 private static char getStyleAbbrev(int i
){