some bugfixes in CondensedDistribution composition code - #3907
[cdmlib.git] / cdmlib-ext / src / main / java / eu / etaxonomy / cdm / ext / geo / EditGeoServiceUtilities.java
index 2546bc4b0f81b25b10018af47ae9482b4c87f3eb..47d01471c75dee9c97190a9b1c980f1a9e2b9d73 100644 (file)
@@ -11,6 +11,7 @@
 package eu.etaxonomy.cdm.ext.geo;\r
 \r
 import java.awt.Color;\r
+import java.io.IOException;\r
 import java.io.UnsupportedEncodingException;\r
 import java.net.URLEncoder;\r
 import java.util.ArrayList;\r
@@ -25,19 +26,26 @@ import java.util.UUID;
 \r
 import javax.persistence.Transient;\r
 \r
+import org.apache.commons.collections.CollectionUtils;\r
+import org.apache.commons.collections.Transformer;\r
 import org.apache.commons.lang.StringUtils;\r
 import org.apache.log4j.Logger;\r
-\r
+import org.codehaus.jackson.JsonParseException;\r
+import org.codehaus.jackson.map.JsonMappingException;\r
+import org.codehaus.jackson.map.ObjectMapper;\r
+import org.codehaus.jackson.map.type.MapType;\r
+import org.codehaus.jackson.map.type.TypeFactory;\r
+\r
+import eu.etaxonomy.cdm.api.service.ITermService;\r
+import eu.etaxonomy.cdm.api.service.dto.CondensedDistribution;\r
 import eu.etaxonomy.cdm.api.utility.DescriptionUtility;\r
 import eu.etaxonomy.cdm.common.CdmUtils;\r
-import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;\r
-import eu.etaxonomy.cdm.model.common.DefinedTermBase;\r
 import eu.etaxonomy.cdm.model.common.Language;\r
 import eu.etaxonomy.cdm.model.common.Representation;\r
 import eu.etaxonomy.cdm.model.common.TermVocabulary;\r
 import eu.etaxonomy.cdm.model.description.Distribution;\r
-import eu.etaxonomy.cdm.model.description.PresenceAbsenceTermBase;\r
-import eu.etaxonomy.cdm.model.description.PresenceTerm;\r
+import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;\r
+import eu.etaxonomy.cdm.model.location.Country;\r
 import eu.etaxonomy.cdm.model.location.NamedArea;\r
 import eu.etaxonomy.cdm.model.location.NamedAreaLevel;\r
 import eu.etaxonomy.cdm.model.location.Point;\r
@@ -56,7 +64,9 @@ import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
 public class EditGeoServiceUtilities {\r
     private static final Logger logger = Logger.getLogger(EditGeoServiceUtilities.class);\r
 \r
-    private static PresenceAbsenceTermBase<?> defaultStatus = PresenceTerm.PRESENT();\r
+    private static final int INT_MAX_LENGTH = String.valueOf(Integer.MAX_VALUE).length();\r
+\r
+    private static PresenceAbsenceTerm defaultStatus = PresenceAbsenceTerm.PRESENT();\r
 \r
     private static IDefinedTermDao termDao;\r
 \r
@@ -69,6 +79,7 @@ public class EditGeoServiceUtilities {
         EditGeoServiceUtilities.termDao= termDao;\r
     }\r
 \r
+\r
     private static HashMap<SpecimenOrObservationType, Color> defaultSpecimenOrObservationTypeColors = null;\r
 \r
     private static HashMap<SpecimenOrObservationType, Color> getDefaultSpecimenOrObservationTypeColors() {\r
@@ -85,27 +96,27 @@ public class EditGeoServiceUtilities {
     }\r
 \r
 \r
-    private static HashMap<PresenceAbsenceTermBase<?>, Color> defaultPresenceAbsenceTermBaseColors = null;\r
+    private static HashMap<PresenceAbsenceTerm, Color> defaultPresenceAbsenceTermBaseColors = null;\r
 \r
-    private static HashMap<PresenceAbsenceTermBase<?>, Color> getDefaultPresenceAbsenceTermBaseColors() {\r
+    private static HashMap<PresenceAbsenceTerm, Color> getDefaultPresenceAbsenceTermBaseColors() {\r
         if(defaultPresenceAbsenceTermBaseColors == null){\r
-            defaultPresenceAbsenceTermBaseColors = new HashMap<PresenceAbsenceTermBase<?>, Color>();\r
-            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.PRESENT(), Color.decode("0x4daf4a"));\r
-            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.NATIVE(), Color.decode("0x4daf4a"));\r
-            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.NATIVE_DOUBTFULLY_NATIVE(), Color.decode("0x377eb8"));\r
-            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.CULTIVATED(), Color.decode("0x984ea3"));\r
-            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.INTRODUCED(), Color.decode("0xff7f00"));\r
-            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.INTRODUCED_ADVENTITIOUS(), Color.decode("0xffff33"));\r
-            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.INTRODUCED_CULTIVATED(), Color.decode("0xa65628"));\r
-            defaultPresenceAbsenceTermBaseColors.put(PresenceTerm.INTRODUCED_NATURALIZED(), Color.decode("0xf781bf"));\r
+            defaultPresenceAbsenceTermBaseColors = new HashMap<PresenceAbsenceTerm, Color>();\r
+            defaultPresenceAbsenceTermBaseColors.put(PresenceAbsenceTerm.PRESENT(), Color.decode("0x4daf4a"));\r
+            defaultPresenceAbsenceTermBaseColors.put(PresenceAbsenceTerm.NATIVE(), Color.decode("0x4daf4a"));\r
+            defaultPresenceAbsenceTermBaseColors.put(PresenceAbsenceTerm.NATIVE_DOUBTFULLY_NATIVE(), Color.decode("0x377eb8"));\r
+            defaultPresenceAbsenceTermBaseColors.put(PresenceAbsenceTerm.CULTIVATED(), Color.decode("0x984ea3"));\r
+            defaultPresenceAbsenceTermBaseColors.put(PresenceAbsenceTerm.INTRODUCED(), Color.decode("0xff7f00"));\r
+            defaultPresenceAbsenceTermBaseColors.put(PresenceAbsenceTerm.INTRODUCED_ADVENTITIOUS(), Color.decode("0xffff33"));\r
+            defaultPresenceAbsenceTermBaseColors.put(PresenceAbsenceTerm.INTRODUCED_CULTIVATED(), Color.decode("0xa65628"));\r
+            defaultPresenceAbsenceTermBaseColors.put(PresenceAbsenceTerm.INTRODUCED_NATURALIZED(), Color.decode("0xf781bf"));\r
 \r
             /*\r
              * and now something very hacky ...\r
              * ONLY-A-TEST is set by the Test class EditGeoServiceTest\r
              *\r
-             * TODO remove according line from\r
-             * EditGeoServiceTest.setUp() when the hardcoded colors for flora of\r
-             * cyprus are no further needed !!\r
+             * FIXME remove according line from\r
+             * EditGeoServiceTest.setUp() since the hardcoded colors for flora of\r
+             * cyprus should no longer be needed : #4268 (allow defining custom presence and absence term colors for EditGeoServiceUtilities)\r
              */\r
             String onlyTest = System.getProperty("ONLY-A-TEST"); //\r
             if(onlyTest != null && onlyTest.equals("TRUE")){\r
@@ -131,24 +142,24 @@ public class EditGeoServiceUtilities {
 \r
             UUID reportedInErrorUuid = UUID.fromString("38604788-cf05-4607-b155-86db456f7680");\r
 \r
-            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(indigenousUuid), Color.decode("0x339966"));\r
-            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(indigenousQUuid), Color.decode("0x339966"));\r
+            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTerm) termDao.load(indigenousUuid), Color.decode("0x339966"));\r
+            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTerm) termDao.load(indigenousQUuid), Color.decode("0x339966"));\r
 \r
-            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(cultivatedQUuid), Color.decode("0xbdb76b"));\r
+            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTerm) termDao.load(cultivatedQUuid), Color.decode("0xbdb76b"));\r
 \r
-            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(casualUuid), Color.decode("0xffff00"));\r
-            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(casualQUuid), Color.decode("0xffff00"));\r
+            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTerm) termDao.load(casualUuid), Color.decode("0xffff00"));\r
+            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTerm) termDao.load(casualQUuid), Color.decode("0xffff00"));\r
 \r
-            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(naturalizedNonInvasiveUuid), Color.decode("0xff9900"));\r
-            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(naturalizedNonInvasiveQUuid), Color.decode("0xff9900"));\r
+            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTerm) termDao.load(naturalizedNonInvasiveUuid), Color.decode("0xff9900"));\r
+            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTerm) termDao.load(naturalizedNonInvasiveQUuid), Color.decode("0xff9900"));\r
 \r
-            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(naturalizedInvasiveUuid), Color.decode("0xff0000"));\r
-            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(naturalizedInvasiveQUuid), Color.decode("0xff0000"));\r
+            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTerm) termDao.load(naturalizedInvasiveUuid), Color.decode("0xff0000"));\r
+            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTerm) termDao.load(naturalizedInvasiveQUuid), Color.decode("0xff0000"));\r
 \r
-            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(questionablelUuid), Color.decode("0x00ccff"));\r
-            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(questionableQUuid), Color.decode("0x00ccff"));\r
+            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTerm) termDao.load(questionablelUuid), Color.decode("0x00ccff"));\r
+            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTerm) termDao.load(questionableQUuid), Color.decode("0x00ccff"));\r
 \r
-            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTermBase<?>) termDao.load(reportedInErrorUuid), Color.decode("0xcccccc"));\r
+            defaultPresenceAbsenceTermBaseColors.put((PresenceAbsenceTerm) termDao.load(reportedInErrorUuid), Color.decode("0xcccccc"));\r
 \r
         }\r
         return defaultPresenceAbsenceTermBaseColors;\r
@@ -163,68 +174,66 @@ public class EditGeoServiceUtilities {
     static final String VALUE_SUPER_LIST_ENTRY_SEPARATOR = "||";\r
 \r
 \r
-\r
-    //preliminary implementation for TDWG areas\r
     /**\r
      * Returns the parameter String for the EDIT geo webservice to create a\r
-     * dsitribution map.\r
+     * distribution map.\r
      *\r
      * @param distributions\r
      *            A set of distributions that should be shown on the map\r
+     *            The {@link DescriptionUtility} class provides a method for\r
+     *            filtering a set of Distributions :\r
+     *\r
+     *            {@code\r
+     *            Collection<Distribution> filteredDistributions =\r
+     *            DescriptionUtility.filterDistributions(distributions,\r
+     *            subAreaPreference, statusOrderPreference, hideMarkedAreas);\r
+     *            }\r
+     * @param mapping\r
+     *            Data regarding the mapping of NamedAreas to shape file\r
+     *            attribute tables\r
      * @param presenceAbsenceTermColors\r
      *            A map that defines the colors of PresenceAbsenceTerms. The\r
      *            PresenceAbsenceTerms are defined by their uuid. If a\r
      *            PresenceAbsenceTerm is not included in this map, it's default\r
      *            color is taken instead. If the map == null all terms are\r
      *            colored by their default color.\r
-     * @param width\r
-     *            The maps width\r
-     * @param height\r
-     *            The maps height\r
-     * @param bbox\r
-     *            The maps bounding box (e.g. "-180,-90,180,90" for the whole\r
-     *            world)\r
      * @param projectToLayer\r
      *            name of a layer which is representing a specific\r
      *            {@link NamedAreaLevel} Supply this parameter if you to project\r
      *            all other distribution area levels to this layer.\r
-     * @param layer\r
-     *            The layer that is responsible for background borders and\r
-     *            colors. Use the name for the layer. If null 'earth' is taken\r
-     *            as default.\r
+     * @param languages\r
+     *\r
      * @return the parameter string or an empty string if the\r
      *         <code>distributions</code> set was null or empty.\r
      */\r
     @Transient\r
     public static String getDistributionServiceRequestParameterString(\r
-            Set<Distribution> distributions,\r
+            Collection<Distribution> filteredDistributions,\r
             IGeoServiceAreaMapping mapping,\r
-            Map<PresenceAbsenceTermBase<?>,Color> presenceAbsenceTermColors,\r
-            int width,\r
-            int height,\r
-            String bbox,\r
-            String baseLayerName,\r
+            Map<PresenceAbsenceTerm,Color> presenceAbsenceTermColors,\r
             String projectToLayer,\r
             List<Language> languages){\r
 \r
 \r
-        /**\r
+        /*\r
          * generateMultipleAreaDataParameters switches between the two possible styles:\r
          * 1. ad=layername1:area-data||layername2:area-data\r
          * 2. ad=layername1:area-data&ad=layername2:area-data\r
          */\r
         boolean generateMultipleAreaDataParameters = false;\r
 \r
-        /**\r
+        /*\r
          * doNotReuseStyles is a workaround for a problem in the EDIT MapService,\r
          * see https://dev.e-taxonomy.eu/trac/ticket/2707#comment:24\r
+         *\r
+         * a.kohlbecker 2014-07-02 :This bug in the map service has been\r
+         * fixed now so reusing styles is now possible setting this flag to false.\r
          */\r
-        boolean doNotReuseStyles = true;\r
+        boolean doNotReuseStyles = false;\r
 \r
         List<String>  perLayerAreaData = new ArrayList<String>();\r
-       Map<Integer, String> areaStyles = new HashMap<Integer, String>();\r
-        List<String> legendLabels = new ArrayList<String>();\r
-\r
+        Map<Integer, String> areaStyles = new HashMap<Integer, String>();\r
+        List<String> legendSortList = new ArrayList<String>();\r
 \r
         String borderWidth = "0.1";\r
         String borderColorRgb = "";\r
@@ -232,42 +241,22 @@ public class EditGeoServiceUtilities {
 \r
 \r
         //handle empty set\r
-        if(distributions == null || distributions.size() == 0){\r
+        if(filteredDistributions == null || filteredDistributions.size() == 0){\r
             return "";\r
         }\r
 \r
-        Collection<Distribution> filteredDistributions = DescriptionUtility.filterDistributions(distributions);\r
+        presenceAbsenceTermColors = mergeMaps(getDefaultPresenceAbsenceTermBaseColors(), presenceAbsenceTermColors);\r
 \r
         Map<String, Map<Integer, Set<Distribution>>> layerMap = new HashMap<String, Map<Integer, Set<Distribution>>>();\r
-        List<PresenceAbsenceTermBase<?>> statusList = new ArrayList<PresenceAbsenceTermBase<?>>();\r
+        List<PresenceAbsenceTerm> statusList = new ArrayList<PresenceAbsenceTerm>();\r
 \r
-        // TODO this step seems to be taking too much time\r
         groupStylesAndLayers(filteredDistributions, layerMap, statusList, mapping);\r
 \r
-        presenceAbsenceTermColors = mergeMaps(getDefaultPresenceAbsenceTermBaseColors(), presenceAbsenceTermColors);\r
-\r
         Map<String, String> parameters = new HashMap<String, String>();\r
 \r
-        //bbox\r
-        if (bbox != null){\r
-            parameters.put("bbox", bbox);\r
-        }\r
-        // map size\r
-        String ms = compileMapSizeParameterValue(width, height);\r
-        if(ms != null){\r
-            parameters.put("ms", ms);\r
-        }\r
-        //layer\r
-//        if (StringUtils.isBlank(baseLayerName)){\r
-//            baseLayerName = "earth";\r
-//        }\r
-        if (!StringUtils.isBlank(baseLayerName)){\r
-            parameters.put("l", baseLayerName);\r
-        }\r
-\r
         //style\r
         int styleCounter = 0;\r
-        for (PresenceAbsenceTermBase<?> status: statusList){\r
+        for (PresenceAbsenceTerm status: statusList){\r
 \r
             char styleCode = getStyleAbbrev(styleCounter);\r
 \r
@@ -278,11 +267,7 @@ public class EditGeoServiceUtilities {
             if (languages.size() == 0){\r
                 languages.add(Language.DEFAULT());\r
             }\r
-            Representation representation = status.getPreferredRepresentation(languages);\r
-            String statusLabel = representation.getLabel();\r
-            //statusLabel.replace('introduced: ', '');\r
-            statusLabel = statusLabel.replace("introduced: ", "introduced, ");\r
-            statusLabel = statusLabel.replace("native: ", "native,  ");\r
+            Representation statusRepresentation = status.getPreferredRepresentation(languages);\r
 \r
             //getting the area color\r
             Color statusColor = presenceAbsenceTermColors.get(status);\r
@@ -299,7 +284,9 @@ public class EditGeoServiceUtilities {
             String styleValues = StringUtils.join(new String[]{fillColorRgb, borderColorRgb, borderWidth, borderDashingPattern}, ',');\r
 \r
             areaStyles.put(styleCounter, styleValues);\r
-            legendLabels.add(styleCode + ID_FROM_VALUES_SEPARATOR + encode(statusLabel));\r
+\r
+            String legendEntry = styleCode + ID_FROM_VALUES_SEPARATOR + encode(statusRepresentation.getLabel());\r
+            legendSortList.add(StringUtils.leftPad(String.valueOf(status.getOrderIndex()), INT_MAX_LENGTH, '0') + legendEntry );\r
             styleCounter++;\r
         }\r
 \r
@@ -307,12 +294,15 @@ public class EditGeoServiceUtilities {
         List<String> styledAreasPerLayer;\r
         List<String> areasPerStyle;\r
         /**\r
+         * Map<Integer, Integer> styleUsage\r
+         *\r
          * Used to avoid reusing styles in multiple layers\r
          *\r
          * key: the style id\r
          * value: the count of how often the style has been used for different layers, starts with 0 for first time use\r
          */\r
         Map<Integer, Integer> styleUsage = new HashMap<Integer, Integer>();\r
+\r
         char styleChar;\r
         for (String layerString : layerMap.keySet()){\r
             // each layer\r
@@ -331,6 +321,8 @@ public class EditGeoServiceUtilities {
                     if(styleIncrement > 0){\r
                         // style code has been used before!\r
                         styleChar = getStyleAbbrev(style + styleIncrement + styleCounter);\r
+                        //for debugging sometimes failing test  #3831\r
+                        logger.warn("style: " + style + ", styleIncrement: " +  styleIncrement + ", styleCounter: " + styleCounter);\r
                         areaStyles.put(style + styleIncrement + styleCounter, areaStyles.get(style));\r
                     } else {\r
                         styleChar = getStyleAbbrev(style);\r
@@ -352,7 +344,7 @@ public class EditGeoServiceUtilities {
         if(areaStyles.size() > 0){\r
             ArrayList<Integer> styleIds = new ArrayList<Integer>(areaStyles.size());\r
             styleIds.addAll(areaStyles.keySet());\r
-            Collections.sort(styleIds);\r
+            Collections.sort(styleIds); // why is it necessary to sort here?\r
             StringBuilder db = new StringBuilder();\r
             for(Integer sid : styleIds){\r
                 if(db.length() > 0){\r
@@ -362,10 +354,27 @@ public class EditGeoServiceUtilities {
             }\r
             parameters.put("as", db.toString());\r
         }\r
-        if(legendLabels.size() > 0){\r
-            parameters.put("title", StringUtils.join(legendLabels.iterator(), VALUE_LIST_ENTRY_SEPARATOR));\r
-        }\r
+        if(legendSortList.size() > 0){\r
+            // sort the label entries after the status terms\r
+            Collections.sort(legendSortList);\r
+            // since the status terms are have an inverse natural order\r
+            // (as all other ordered term, see OrderedTermBase.performCompareTo(T orderedTerm, boolean skipVocabularyCheck)\r
+            // the sorted list must be reverted\r
+//            Collections.reverse(legendSortList);\r
+            // remove the prepended order index (like 000000000000001 ) from the legend entries\r
+            @SuppressWarnings("unchecked")\r
+            Collection<String> legendEntries = CollectionUtils.collect(legendSortList, new Transformer()\r
+            {\r
+                @Override\r
+                public String transform(Object o)\r
+                {\r
+                  String s = ((String) o);\r
+                  return s.substring(INT_MAX_LENGTH, s.length());\r
+                }\r
+              });\r
 \r
+            parameters.put("title", StringUtils.join(legendEntries.iterator(), VALUE_LIST_ENTRY_SEPARATOR));\r
+        }\r
 \r
         if(generateMultipleAreaDataParameters){\r
             // not generically possible since parameters can not contain duplicate keys with value "ad"\r
@@ -389,7 +398,7 @@ public class EditGeoServiceUtilities {
      */\r
     private static void groupStylesAndLayers(Collection<Distribution> distributions,\r
             Map<String, Map<Integer,Set<Distribution>>> layerMap,\r
-            List<PresenceAbsenceTermBase<?>> statusList,\r
+            List<PresenceAbsenceTerm> statusList,\r
             IGeoServiceAreaMapping mapping) {\r
 \r
 \r
@@ -397,7 +406,7 @@ public class EditGeoServiceUtilities {
         //and collect necessary information\r
         for (Distribution distribution : distributions){\r
             //collect status\r
-            PresenceAbsenceTermBase<?> status = distribution.getStatus();\r
+            PresenceAbsenceTerm status = distribution.getStatus();\r
             if(status == null){\r
                 status = defaultStatus;\r
             }\r
@@ -412,6 +421,9 @@ public class EditGeoServiceUtilities {
     }\r
 \r
     /**\r
+     * Adds the areas to the layer map. Areas which do not have layer information\r
+     * mapped to them are ignored.\r
+     * <p>\r
      * A layer map holds the following information:\r
      *\r
      * <ul>\r
@@ -433,60 +445,28 @@ public class EditGeoServiceUtilities {
      */\r
     private static void addAreaToLayerMap(Map<String, Map<Integer,\r
             Set<Distribution>>> layerMap,\r
-            List<PresenceAbsenceTermBase<?>> statusList,\r
+            List<PresenceAbsenceTerm> statusList,\r
             Distribution distribution,\r
             NamedArea area,\r
             IGeoServiceAreaMapping mapping) {\r
 \r
         if (area != null){\r
-            String geoLayerString = getWMSLayerName(area, mapping);\r
-\r
-            if(geoLayerString == null){\r
-\r
-                // if no layer is mapped this area descend into sub areas in order to project\r
-                // the distribution to those\r
-                /**\r
-                 * FIXME ClassCaseException!!!\r
-                 * getIncludes():  Hibernate returns this as a collection of CGLibProxy$$DefinedTermBase objects\r
-                 * which can't be cast to instances of T - can we explicitly initialize these terms using\r
-                 * Hibernate.initialize(), does this imply a distinct load, and find methods in the dao?\r
-                 */\r
-                for(DefinedTermBase<?> dtb : area.getIncludes()){\r
-                    NamedArea subArea = HibernateProxyHelper.deproxy(dtb, NamedArea.class);\r
-                    addAreaToLayerMap(layerMap, statusList, distribution, subArea, mapping);\r
-                }\r
+            String geoLayerName = getWMSLayerName(area, mapping);\r
 \r
+            if(geoLayerName == null){\r
+               /* IGNORE areas for which no layer is mapped */\r
             } else {\r
-\r
-                Map<Integer, Set<Distribution>> styleMap = layerMap.get(geoLayerString);\r
+                Map<Integer, Set<Distribution>> styleMap = layerMap.get(geoLayerName);\r
                 if (styleMap == null) {\r
                     styleMap = new HashMap<Integer, Set<Distribution>>();\r
-                    layerMap.put(geoLayerString, styleMap);\r
+                    layerMap.put(geoLayerName, styleMap);\r
                 }\r
                 addDistributionToStyleMap(distribution, styleMap, statusList);\r
-\r
             }\r
         }\r
     }\r
 \r
 \r
-    private static String compileMapSizeParameterValue(int width, int height) {\r
-\r
-        String widthStr = "";\r
-        String heightStr = "";\r
-\r
-        if (width > 0) {\r
-            widthStr = "" + width;\r
-        }\r
-        if (height > 0) {\r
-            heightStr = SUBENTRY_DELIMITER + height;\r
-        }\r
-        String ms = widthStr + heightStr;\r
-        if(ms.length() == 0){\r
-            ms = null;\r
-        }\r
-        return ms;\r
-    }\r
 \r
     /**\r
      * URI encode the given String\r
@@ -530,9 +510,8 @@ public class EditGeoServiceUtilities {
         TermVocabulary<NamedArea> voc = area.getVocabulary();\r
         String result = null;\r
 \r
-        if (voc != null && voc.getUuid().equals(NamedArea.uuidTdwgAreaVocabulary)\r
-                || voc.getUuid().equals(uuidCyprusDivisionsVocabulary)) {\r
-            // TDWG or Cyprus\r
+        if (voc != null && voc.getUuid().equals(NamedArea.uuidTdwgAreaVocabulary) ||  voc.getUuid().equals(Country.uuidCountryVocabulary)) {\r
+            // TDWG or Country\r
             result = area.getIdInVocabulary();\r
             if (area.getLevel() != null && area.getLevel().equals(NamedAreaLevel.TDWG_LEVEL4())) {\r
                 result = result.replace("-", "");\r
@@ -555,11 +534,6 @@ public class EditGeoServiceUtilities {
 \r
     }\r
 \r
-\r
-\r
-    //Preliminary as long as user defined areas are not fully implemented\r
-    public static final UUID uuidCyprusDivisionsVocabulary = UUID.fromString("2119f610-1f93-4d87-af28-40aeefaca100");\r
-\r
     private static List<String> projectToWMSSubLayer(NamedArea area){\r
 \r
         List<String> layerNames = new ArrayList<String>();\r
@@ -583,10 +557,7 @@ public class EditGeoServiceUtilities {
             //unrecognized tdwg area\r
 \r
         }\r
-        //TODO hardcoded for cyprus (as long as user defined areas are not fully implemented). Remove afterwards.\r
-        if (voc.getUuid().equals(uuidCyprusDivisionsVocabulary)){\r
-            matchedLayerName = "cyprusdivs:bdcode";\r
-        }\r
+        //TODO countries\r
 \r
         // check if the matched layer equals the layer to project to\r
         // if not: recurse into the sub-level in order to find the specified one.\r
@@ -622,10 +593,8 @@ public class EditGeoServiceUtilities {
             //unrecognized tdwg area\r
             return null;\r
 \r
-        }\r
-        //hardcoded for cyprus (as long as user defined areas are not fully implemented). Remove afterwards.\r
-        if (voc.getUuid().equals(uuidCyprusDivisionsVocabulary)){\r
-            return "cyprusdivs:bdcode";\r
+        }else if (voc.getUuid().equals(Country.uuidCountryVocabulary)){\r
+            return "country_earth:gmi_cntry";\r
         }\r
 \r
         GeoServiceArea areas = mapping.valueOf(area);\r
@@ -643,8 +612,8 @@ public class EditGeoServiceUtilities {
 \r
 \r
     private static void addDistributionToStyleMap(Distribution distribution, Map<Integer, Set<Distribution>> styleMap,\r
-            List<PresenceAbsenceTermBase<?>> statusList) {\r
-        PresenceAbsenceTermBase<?> status = distribution.getStatus();\r
+            List<PresenceAbsenceTerm> statusList) {\r
+        PresenceAbsenceTerm status = distribution.getStatus();\r
         if (status == null) {\r
             status = defaultStatus;\r
         }\r
@@ -661,7 +630,6 @@ public class EditGeoServiceUtilities {
      * @param fieldUnitPoints\r
      * @param derivedUnitPoints\r
      * @param specimenOrObservationTypeColors\r
-     * @param doReturnImage TODO\r
      * @param width\r
      * @param height\r
      * @param bbox\r
@@ -677,38 +645,35 @@ public class EditGeoServiceUtilities {
      *  &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\r
      *  &os=1%3Ac%2FFFD700%2F10%2FAporrectodea caliginosa\r
      */\r
-    public static String getOccurrenceServiceRequestParameterString(\r
-                List<Point> fieldUnitPoints,\r
-                List<Point> derivedUnitPoints,\r
-                Map<SpecimenOrObservationType, Color> specimenOrObservationTypeColors,\r
-                Boolean doReturnImage, Integer width, Integer height, String bbox, String backLayer) {\r
-\r
-            specimenOrObservationTypeColors = mergeMaps(getDefaultSpecimenOrObservationTypeColors(), specimenOrObservationTypeColors);\r
-\r
-            Map<String, String> parameters = new HashMap<String, String>();\r
-            parameters.put("legend", "0");\r
-            parameters.put("image", doReturnImage != null && doReturnImage ? "true" : "false");\r
-            parameters.put("recalculate", "false"); // TODO add parameter to method\r
-            if(bbox != null){\r
-                parameters.put("bbox", bbox);\r
-            }\r
-            if(width != null || height != null){\r
-                parameters.put("ms", compileMapSizeParameterValue(width, height));\r
-            }\r
+    public static OccurrenceServiceRequestParameterDto getOccurrenceServiceRequestParameterString(\r
+            List<Point> fieldUnitPoints,\r
+            List<Point> derivedUnitPoints,\r
+            Map<SpecimenOrObservationType, Color> specimenOrObservationTypeColors) {\r
+        OccurrenceServiceRequestParameterDto dto = new OccurrenceServiceRequestParameterDto();\r
 \r
-            Map<String, String> styleAndData = new HashMap<String, String>();\r
 \r
-            addToStyleAndData(fieldUnitPoints, SpecimenOrObservationType.FieldUnit, specimenOrObservationTypeColors, styleAndData);\r
-            addToStyleAndData(derivedUnitPoints, SpecimenOrObservationType.DerivedUnit, specimenOrObservationTypeColors, styleAndData);\r
+        specimenOrObservationTypeColors = mergeMaps(getDefaultSpecimenOrObservationTypeColors(), specimenOrObservationTypeColors);\r
 \r
-            parameters.put("os", StringUtils.join(styleAndData.keySet().iterator(), "||"));\r
-            parameters.put("od", StringUtils.join(styleAndData.values().iterator(), "||"));\r
+        Map<String, String> parameters = new HashMap<String, String>();\r
+        parameters.put("legend", "0");\r
 \r
-            String queryString = makeQueryString(parameters);\r
+        Map<String, String> styleAndData = new HashMap<String, String>();\r
 \r
-            logger.info(queryString);\r
+        addToStyleAndData(fieldUnitPoints, SpecimenOrObservationType.FieldUnit, specimenOrObservationTypeColors, styleAndData);\r
+        addToStyleAndData(derivedUnitPoints, SpecimenOrObservationType.DerivedUnit, specimenOrObservationTypeColors, styleAndData);\r
 \r
-        return queryString;\r
+        parameters.put("os", StringUtils.join(styleAndData.keySet().iterator(), "||"));\r
+        parameters.put("od", StringUtils.join(styleAndData.values().iterator(), "||"));\r
+\r
+        String queryString = makeQueryString(parameters);\r
+\r
+        dto.setFieldUnitPoints(fieldUnitPoints);\r
+        dto.setDerivedUnitPoints(derivedUnitPoints);\r
+        dto.setOccurrenceQuery(queryString);\r
+\r
+        logger.info(queryString);\r
+\r
+        return dto;\r
     }\r
 \r
     /**\r
@@ -766,4 +731,68 @@ public class EditGeoServiceUtilities {
         return (char)ascii;\r
     }\r
 \r
+    /**\r
+     * @param statusColorJson for example: {@code {"n":"#ff0000","p":"#ffff00"}}\r
+     * @return\r
+     * @throws IOException\r
+     * @throws JsonParseException\r
+     * @throws JsonMappingException\r
+     */\r
+    public static Map<PresenceAbsenceTerm, Color> buildStatusColorMap(String statusColorJson, ITermService termService) throws IOException, JsonParseException,\r
+            JsonMappingException {\r
+\r
+        Map<PresenceAbsenceTerm, Color> presenceAbsenceTermColors = null;\r
+        if(StringUtils.isNotEmpty(statusColorJson)){\r
+\r
+            ObjectMapper mapper = new ObjectMapper();\r
+            // TODO cache the color maps to speed this up?\r
+\r
+            TypeFactory typeFactory = mapper.getTypeFactory();\r
+            MapType mapType = typeFactory.constructMapType(HashMap.class, String.class, String.class);\r
+\r
+            Map<String,String> statusColorMap = mapper.readValue(statusColorJson, mapType);\r
+            UUID presenceTermVocabUuid = PresenceAbsenceTerm.NATIVE().getVocabulary().getUuid();\r
+            presenceAbsenceTermColors = new HashMap<PresenceAbsenceTerm, Color>();\r
+            PresenceAbsenceTerm paTerm = null;\r
+            for(String statusId : statusColorMap.keySet()){\r
+                try {\r
+                    Color color = Color.decode(statusColorMap.get(statusId));\r
+                    paTerm = termService.findByIdInVocabulary(statusId, presenceTermVocabUuid, PresenceAbsenceTerm.class);\r
+                    if(paTerm != null){\r
+                        presenceAbsenceTermColors.put(paTerm, color);\r
+                    }\r
+                } catch (NumberFormatException e){\r
+                    logger.error("Cannot decode color", e);\r
+                }\r
+            }\r
+        }\r
+        return presenceAbsenceTermColors;\r
+    }\r
+\r
+\r
+    /**\r
+     * @param filteredDistributions\r
+     * @param recipe\r
+     * @param hideMarkedAreas\r
+     * @param langs\r
+     * @return\r
+     */\r
+    public static CondensedDistribution getCondensedDistribution(Collection<Distribution> filteredDistributions,\r
+            CondensedDistributionRecipe recipe, List<Language> langs) {\r
+        ICondensedDistributionComposer composer;\r
+        if(recipe == null) {\r
+            throw new NullPointerException("parameter recipe must not be null");\r
+        }\r
+        try {\r
+            composer = recipe.newCondensedDistributionComposerInstance();\r
+        } catch (InstantiationException e) {\r
+            throw new RuntimeException(e);\r
+        } catch (IllegalAccessException e) {\r
+            throw new RuntimeException(e);\r
+        }\r
+        CondensedDistribution condensedDistribution = composer.createCondensedDistribution(\r
+                filteredDistributions,  langs);\r
+        return condensedDistribution;\r
+    }\r
+\r
 }\r