Merge branch 'develop' into ticket-5066
[cdmlib.git] / cdmlib-ext / src / main / java / eu / etaxonomy / cdm / ext / geo / EditGeoService.java
1 // $Id$
2 /**
3 * Copyright (C) 2009 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 package eu.etaxonomy.cdm.ext.geo;
11
12 import java.awt.Color;
13 import java.io.IOException;
14 import java.io.Reader;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.EnumSet;
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.EntityNotFoundException;
25
26 import org.apache.log4j.Logger;
27 import org.springframework.beans.factory.annotation.Autowired;
28 import org.springframework.stereotype.Service;
29 import org.springframework.transaction.annotation.Transactional;
30
31 import eu.etaxonomy.cdm.api.service.IDescriptionService;
32 import eu.etaxonomy.cdm.api.service.dto.CondensedDistribution;
33 import eu.etaxonomy.cdm.api.service.dto.DistributionInfoDTO;
34 import eu.etaxonomy.cdm.api.service.dto.DistributionInfoDTO.InfoPart;
35 import eu.etaxonomy.cdm.api.utility.DescriptionUtility;
36 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
37 import eu.etaxonomy.cdm.model.common.Language;
38 import eu.etaxonomy.cdm.model.common.MarkerType;
39 import eu.etaxonomy.cdm.model.common.TermVocabulary;
40 import eu.etaxonomy.cdm.model.description.Distribution;
41 import eu.etaxonomy.cdm.model.description.Feature;
42 import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
43 import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
44 import eu.etaxonomy.cdm.model.description.TaxonDescription;
45 import eu.etaxonomy.cdm.model.location.NamedArea;
46 import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
47 import eu.etaxonomy.cdm.model.location.Point;
48 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
49 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
50 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
51 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
52 import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
53 import eu.etaxonomy.cdm.persistence.dao.common.ITermVocabularyDao;
54 import eu.etaxonomy.cdm.persistence.dao.description.IDescriptionDao;
55 import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
56
57 /**
58 * @author a.kohlbecker
59 * @date 18.06.2009
60 *
61 */
62 @Service
63 @Transactional(readOnly = true)
64 public class EditGeoService implements IEditGeoService {
65 public static final Logger logger = Logger.getLogger(EditGeoService.class);
66
67 @Autowired
68 private IDescriptionDao dao;
69
70 @Autowired
71 private IGeoServiceAreaMapping areaMapping;
72
73 @Autowired
74 private IDescriptionService descriptionService;
75
76 @Autowired
77 private ITermVocabularyDao vocabDao;
78
79 private IDefinedTermDao termDao;
80 @Autowired
81 public void setTermDao(IDefinedTermDao termDao) {
82 this.termDao = termDao;
83 EditGeoServiceUtilities.setTermDao(termDao);
84 }
85
86 @Autowired
87 private IOccurrenceDao occurrenceDao;
88
89 private Set<Feature> getDistributionFeatures() {
90 Set<Feature> distributionFeature = new HashSet<Feature>();
91 Feature feature = (Feature) termDao.findByUuid(Feature.DISTRIBUTION().getUuid());
92 distributionFeature.add(feature);
93 return distributionFeature;
94 }
95
96 /**
97 * @param taxonDescriptions
98 * @return
99 */
100 private Set<Distribution> getDistributionsOf(List<TaxonDescription> taxonDescriptions) {
101 Set<Distribution> distributions = new HashSet<Distribution>();
102 for (TaxonDescription taxonDescription : taxonDescriptions) {
103 List<Distribution> result = (List) dao.getDescriptionElements(
104 taxonDescription,
105 null,
106 getDistributionFeatures(),
107 Distribution.class,
108 null,
109 null,
110 null);
111 distributions.addAll(result);
112 }
113 return distributions;
114 }
115
116 /**
117 * {@inheritDoc}
118 */
119 @Override
120 public String getDistributionServiceRequestParameterString(List<TaxonDescription> taxonDescriptions,
121 boolean subAreaPreference,
122 boolean statusOrderPreference,
123 Set<MarkerType> hideMarkedAreas,
124 Map<PresenceAbsenceTerm, Color> presenceAbsenceTermColors,
125 List<Language> langs) {
126
127 Set<Distribution> distributions = getDistributionsOf(taxonDescriptions);
128
129 String uriParams = getDistributionServiceRequestParameterString(distributions,
130 subAreaPreference,
131 statusOrderPreference,
132 hideMarkedAreas,
133 presenceAbsenceTermColors,
134 langs);
135
136 return uriParams;
137 }
138
139 /**
140 * {@inheritDoc}
141 */
142 @Override
143 public String getDistributionServiceRequestParameterString(
144 Set<Distribution> distributions,
145 boolean subAreaPreference,
146 boolean statusOrderPreference,
147 Set<MarkerType> hideMarkedAreas,
148 Map<PresenceAbsenceTerm, Color> presenceAbsenceTermColors,
149 List<Language> langs) {
150
151 Collection<Distribution> filteredDistributions = DescriptionUtility.filterDistributions(distributions,
152 hideMarkedAreas, true, statusOrderPreference, subAreaPreference);
153
154
155 String uriParams = EditGeoServiceUtilities.getDistributionServiceRequestParameterString(
156 filteredDistributions,
157 areaMapping,
158 presenceAbsenceTermColors,
159 null, langs);
160 return uriParams;
161 }
162
163 /**
164 * {@inheritDoc}
165 */
166 @Override
167 @Deprecated
168 public String getDistributionServiceRequestParameterString(TaxonDescription taxonDescription,
169 boolean subAreaPreference,
170 boolean statusOrderPreference,
171 Set<MarkerType> hideMarkedAreas,
172 Map<PresenceAbsenceTerm, Color> presenceAbsenceTermColors,
173 List<Language> langs) {
174
175 List<TaxonDescription> taxonDescriptions = new ArrayList<TaxonDescription>();
176 taxonDescriptions.add(taxonDescription);
177
178 return getDistributionServiceRequestParameterString(taxonDescriptions,
179 subAreaPreference,
180 statusOrderPreference,
181 hideMarkedAreas,
182 presenceAbsenceTermColors,
183 langs);
184 }
185
186 /**
187 * {@inheritDoc}
188 */
189 @Override
190 public OccurrenceServiceRequestParameterDto getOccurrenceServiceRequestParameterString(List<SpecimenOrObservationBase> specimensOrObersvations,
191 Map<SpecimenOrObservationType, Color> specimenOrObservationTypeColors) {
192
193 List<Point> fieldUnitPoints = new ArrayList<Point>();
194 List<Point> derivedUnitPoints = new ArrayList<Point>();
195
196 IndividualsAssociation individualsAssociation;
197 DerivedUnit derivedUnit;
198
199 for (SpecimenOrObservationBase specimenOrObservationBase : specimensOrObersvations) {
200 SpecimenOrObservationBase<?> specimenOrObservation = occurrenceDao
201 .load(specimenOrObservationBase.getUuid());
202
203 if (specimenOrObservation instanceof FieldUnit) {
204 fieldUnitPoints.add(((FieldUnit) specimenOrObservation).getGatheringEvent()
205 .getExactLocation());
206 }
207 if (specimenOrObservation instanceof DerivedUnit) {
208 registerDerivedUnitLocations((DerivedUnit) specimenOrObservation, derivedUnitPoints);
209 }
210 }
211
212 return EditGeoServiceUtilities.getOccurrenceServiceRequestParameterString(fieldUnitPoints,
213 derivedUnitPoints, specimenOrObservationTypeColors);
214
215 }
216
217 public CondensedDistribution getCondensedDistribution(List<TaxonDescription> taxonDescriptions,
218 boolean statusOrderPreference,
219 Set<MarkerType> hideMarkedAreas,
220 MarkerType fallbackAreaMarkerType,
221 CondensedDistributionRecipe recipe,
222 List<Language> langs) {
223 Set<Distribution> distributions = getDistributionsOf(taxonDescriptions);
224 return getCondensedDistribution(distributions, statusOrderPreference,
225 hideMarkedAreas, fallbackAreaMarkerType, recipe, langs);
226 }
227
228 public CondensedDistribution getCondensedDistribution(Set<Distribution> distributions,
229 boolean statusOrderPreference,
230 Set<MarkerType> hideMarkedAreas,
231 MarkerType fallbackAreaMarkerType,
232 CondensedDistributionRecipe recipe,
233 List<Language> langs) {
234
235 Collection<Distribution> filteredDistributions = DescriptionUtility.filterDistributions(
236 distributions, hideMarkedAreas, true, statusOrderPreference, false);
237
238
239
240 CondensedDistribution condensedDistribution = EditGeoServiceUtilities.getCondensedDistribution(
241 filteredDistributions,
242 recipe,
243 langs);
244
245 return condensedDistribution;
246 }
247
248 /**
249 * @param derivedUnit
250 * @param derivedUnitPoints
251 */
252 private void registerDerivedUnitLocations(DerivedUnit derivedUnit, List<Point> derivedUnitPoints) {
253
254 Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
255 if (originals != null) {
256 for (SpecimenOrObservationBase original : originals) {
257 if (original instanceof FieldUnit) {
258 if (((FieldUnit) original).getGatheringEvent() != null) {
259 Point point = ((FieldUnit) original).getGatheringEvent().getExactLocation();
260 if (point != null) {
261 // points with no longitude or latitude should not exist
262 // see #4173 ([Rule] Longitude and Latitude in Point must not be null)
263 if (point.getLatitude() == null || point.getLongitude() == null){
264 continue;
265 }
266 // FIXME: remove next statement after
267 // DerivedUnitFacade or ABCD import is fixed
268 //
269 if(point.getLatitude() == 0.0 || point.getLongitude() == 0.0) {
270 continue;
271 }
272 derivedUnitPoints.add(point);
273 }
274 }
275 } else {
276 registerDerivedUnitLocations((DerivedUnit) original, derivedUnitPoints);
277 }
278 }
279 }
280
281 }
282
283
284 /**
285 * {@inheritDoc}
286 */
287 @Override
288 public void setMapping(NamedArea area, GeoServiceArea geoServiceArea) {
289 areaMapping.set(area, geoServiceArea);
290
291 }
292
293 /**
294 * {@inheritDoc}
295 */
296 @Override
297 @Transactional(readOnly=false)
298 public Map<NamedArea, String> mapShapeFileToNamedAreas(Reader csvReader,
299 List<String> idSearchFields, String wmsLayerName, UUID areaVocabularyUuid,
300 Set<UUID> namedAreaUuids) throws IOException {
301
302 Set<NamedArea> areas = new HashSet<NamedArea>();
303
304 if(areaVocabularyUuid != null){
305 TermVocabulary<NamedArea> areaVocabulary = vocabDao.load(areaVocabularyUuid);
306 if(areaVocabulary == null){
307 throw new EntityNotFoundException("No Vocabulary found for uuid " + areaVocabularyUuid);
308 }
309 areas.addAll(areaVocabulary.getTerms());
310 }
311 if(namedAreaUuids != null && !namedAreaUuids.isEmpty()){
312 for(DefinedTermBase dtb : termDao.list(namedAreaUuids, null, null, null, null)){
313 areas.add((NamedArea)dtb);
314 }
315 }
316
317 ShpAttributesToNamedAreaMapper mapper = new ShpAttributesToNamedAreaMapper(areas, areaMapping);
318 Map<NamedArea, String> resultMap = mapper.readCsv(csvReader, idSearchFields, wmsLayerName);
319 termDao.saveOrUpdateAll((Collection)areas);
320 return resultMap;
321 }
322
323 /**
324 * {@inheritDoc}
325 */
326 @Override
327 public DistributionInfoDTO composeDistributionInfoFor(EnumSet<DistributionInfoDTO.InfoPart> parts, UUID taxonUUID,
328 boolean subAreaPreference, boolean statusOrderPreference, Set<MarkerType> hiddenAreaMarkerTypes,
329 Set<NamedAreaLevel> omitLevels, Map<PresenceAbsenceTerm, Color> presenceAbsenceTermColors,
330 List<Language> languages, List<String> propertyPaths, CondensedDistributionRecipe recipe){
331
332 DistributionInfoDTO dto = new DistributionInfoDTO();
333
334 // Adding default initStrategies to improve the performance of this method
335 // adding 'status' and 'area' has a good positive effect:
336 // filterDistributions() only takes 21% of the total method time (before it was 46%)
337 // at the same time the cost of the getDescriptionElementForTaxon is not increased at all!
338 //
339 // adding 'markers.markerType' is not improving the performance since it only
340 // moved the load from the filter method to the getDescriptionElementForTaxon()
341 // method.
342 // overall improvement by this means is by 42% (from 77,711 ms to 44,868 ms)
343 ArrayList<String> initStrategy = new ArrayList<String>(propertyPaths);
344 if(!initStrategy.contains("status")) {
345 initStrategy.add("status");
346 }
347 if(!initStrategy.contains("area")) {
348 initStrategy.add("area");
349 }
350 if(!initStrategy.contains("markers.markerType")) {
351 initStrategy.add("markers.markerType");
352 }
353 if(omitLevels == null) {
354 omitLevels = new HashSet<NamedAreaLevel>(0);
355 }
356
357 List<Distribution> distributions = dao.getDescriptionElementForTaxon(taxonUUID, null, Distribution.class, null, null, initStrategy);
358
359 // Apply the rules statusOrderPreference and hideMarkedAreas for textual distribution info
360 Set<Distribution> filteredDistributions = DescriptionUtility.filterDistributions(distributions, hiddenAreaMarkerTypes,
361 true, statusOrderPreference, false);
362
363 if(parts.contains(InfoPart.elements)) {
364 dto.setElements(filteredDistributions);
365 }
366
367 if(parts.contains(InfoPart.tree)) {
368 dto.setTree(DescriptionUtility.orderDistributions(termDao, omitLevels, filteredDistributions, hiddenAreaMarkerTypes));
369 }
370
371 if(parts.contains(InfoPart.condensedDistribution)) {
372 dto.setCondensedDistribution(EditGeoServiceUtilities.getCondensedDistribution(filteredDistributions, recipe, languages));
373 }
374
375 if (parts.contains(InfoPart.mapUriParams)) {
376 // only apply the subAreaPreference rule for the maps
377 Set<Distribution> filteredMapDistributions = DescriptionUtility.filterDistributions(filteredDistributions, null, false, false, subAreaPreference);
378
379 dto.setMapUriParams(EditGeoServiceUtilities.getDistributionServiceRequestParameterString(filteredMapDistributions,
380 areaMapping,
381 presenceAbsenceTermColors,
382 null, languages));
383 }
384
385 return dto;
386 }
387 }