Project

General

Profile

« Previous | Next » 

Revision 5f5cfe6a

Added by Andreas Kohlbecker about 4 years ago

ref #6113 producing more complete kml for DerivedUnits & general controller for HTTP OPTIONS requests

View differences:

.gitignore
50 50
!cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/database/data/.settings
51 51
!cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/database/update/v35_36/.settings
52 52
workbench.xmi
53
cdmlib-ext/KmlJaxbMarshallerTest.kml
cdmlib-ext/src/main/java/eu/etaxonomy/cdm/ext/geo/EditGeoService.java
28 28
import org.springframework.stereotype.Service;
29 29
import org.springframework.transaction.annotation.Transactional;
30 30

  
31
import de.micromata.opengis.kml.v_2_2_0.Kml;
31 32
import eu.etaxonomy.cdm.api.service.DistributionTree;
32 33
import eu.etaxonomy.cdm.api.service.dto.CondensedDistribution;
33 34
import eu.etaxonomy.cdm.api.service.dto.DistributionInfoDTO;
34 35
import eu.etaxonomy.cdm.api.service.dto.DistributionInfoDTO.InfoPart;
35 36
import eu.etaxonomy.cdm.api.utility.DescriptionUtility;
36 37
import eu.etaxonomy.cdm.api.utility.DistributionOrder;
38
import eu.etaxonomy.cdm.ext.geo.kml.KMLDocumentBuilder;
37 39
import eu.etaxonomy.cdm.model.common.CdmBase;
38 40
import eu.etaxonomy.cdm.model.common.Language;
39 41
import eu.etaxonomy.cdm.model.common.MarkerType;
......
181 183
    }
182 184

  
183 185
    @Override
184
    public OccurrenceServiceRequestParameterDto getOccurrenceServiceRequestParameterString(
185
            List<SpecimenOrObservationBase> specimensOrObersvations,
186
    public OccurrenceServiceRequestParameterDto getOccurrenceServiceRequestParameters(
187
            List<SpecimenOrObservationBase> specimensOrObservations,
186 188
            Map<SpecimenOrObservationType, Color> specimenOrObservationTypeColors) {
187 189

  
188 190
        List<Point> fieldUnitPoints = new ArrayList<>();
189 191
        List<Point> derivedUnitPoints = new ArrayList<>();
190 192

  
191
        for (SpecimenOrObservationBase<?> specimenOrObservationBase : specimensOrObersvations) {
192
            SpecimenOrObservationBase<?> specimenOrObservation = occurrenceDao
193
        for (SpecimenOrObservationBase<?> specimenOrObservationBase : specimensOrObservations) {
194
            SpecimenOrObservationBase<?> specimensOrObservation = occurrenceDao
193 195
                    .load(specimenOrObservationBase.getUuid());
194 196

  
195
            if (specimenOrObservation instanceof FieldUnit) {
196
                GatheringEvent gatherEvent = ((FieldUnit) specimenOrObservation).getGatheringEvent();
197
            if (specimensOrObservation instanceof FieldUnit) {
198
                GatheringEvent gatherEvent = ((FieldUnit) specimensOrObservation).getGatheringEvent();
197 199
                if (gatherEvent != null && gatherEvent.getExactLocation() != null){
198 200
                    fieldUnitPoints.add(gatherEvent.getExactLocation());
199 201
                }
200 202
            }
201
            if (specimenOrObservation instanceof DerivedUnit) {
202
                registerDerivedUnitLocations((DerivedUnit) specimenOrObservation, derivedUnitPoints);
203
            if (specimensOrObservation instanceof DerivedUnit) {
204
                registerDerivedUnitLocations((DerivedUnit) specimensOrObservation, derivedUnitPoints);
203 205
            }
204 206
        }
205 207

  
206 208
        return EditGeoServiceUtilities.getOccurrenceServiceRequestParameterString(fieldUnitPoints,
207 209
                derivedUnitPoints, specimenOrObservationTypeColors);
208 210
    }
211
    
212
    @Override
213
    public Kml occurrencesToKML(
214
            List<SpecimenOrObservationBase> specimensOrObservations,
215
            Map<SpecimenOrObservationType, Color> specimenOrObservationTypeColors) {
216
    	
217
    		KMLDocumentBuilder builder = new KMLDocumentBuilder();
218
    		
219
    		for (SpecimenOrObservationBase<?> specimenOrObservationBase : specimensOrObservations) {
220
    			builder.addSpecimenOrObservationBase(occurrenceDao.load(specimenOrObservationBase.getUuid())); 			 
221
    		}
222
    	
223
    		Kml kml = builder.build();
224
    		
225
    		return kml;
226
    }
209 227

  
210 228
    public CondensedDistribution getCondensedDistribution(List<TaxonDescription> taxonDescriptions,
211 229
            boolean statusOrderPreference,
cdmlib-ext/src/main/java/eu/etaxonomy/cdm/ext/geo/IEditGeoService.java
22 22

  
23 23
import org.springframework.transaction.annotation.Transactional;
24 24

  
25
import de.micromata.opengis.kml.v_2_2_0.Kml;
25 26
import eu.etaxonomy.cdm.api.service.dto.CondensedDistribution;
26 27
import eu.etaxonomy.cdm.api.service.dto.DistributionInfoDTO;
27 28
import eu.etaxonomy.cdm.api.utility.DescriptionUtility;
......
131 132
            List<Language> langs);
132 133

  
133 134

  
134
    public OccurrenceServiceRequestParameterDto getOccurrenceServiceRequestParameterString(
135
    public OccurrenceServiceRequestParameterDto getOccurrenceServiceRequestParameters(
135 136
            List<SpecimenOrObservationBase> specimensOrObersvations,
136 137
            Map<SpecimenOrObservationType,Color> specimenOrObservationTypeColors
137 138
            );
......
222 223
            CondensedDistributionRecipe recipe,
223 224
            List<Language> langs);
224 225

  
226

  
227
	public Kml occurrencesToKML(List<SpecimenOrObservationBase> specimensOrObersvations,
228
			Map<SpecimenOrObservationType, Color> specimenOrObservationTypeColors);
229

  
225 230
}
cdmlib-ext/src/main/java/eu/etaxonomy/cdm/ext/geo/kml/GeometryBuilder.java
1
/**
2
 * Copyright (C) 2020 EDIT
3
 * European Distributed Institute of Taxonomy
4
 * http://www.e-taxonomy.eu
5
 *
6
 * The contents of this file are subject to the Mozilla Public License Version 1.1
7
 * See LICENSE.TXT at the top of this package for the full license terms.
8
 */
9

  
10
package eu.etaxonomy.cdm.ext.geo.kml;
11

  
12
import java.awt.geom.Point2D;
13

  
14
import javax.measure.Quantity;
15
import javax.measure.Unit;
16
import javax.measure.UnitConverter;
17
import javax.measure.quantity.Length;
18

  
19
import org.apache.log4j.Logger;
20
import org.geotools.data.DataUtilities;
21
import org.geotools.feature.SchemaException;
22
import org.geotools.feature.simple.SimpleFeatureBuilder;
23
import org.geotools.geometry.jts.JTS;
24
import org.geotools.geometry.jts.JTSFactoryFinder;
25
import org.geotools.referencing.CRS;
26
import org.geotools.referencing.GeodeticCalculator;
27
import org.geotools.referencing.crs.DefaultGeographicCRS;
28
import org.locationtech.jts.geom.Coordinate;
29
import org.locationtech.jts.geom.CoordinateSequence;
30
import org.locationtech.jts.geom.Geometry;
31
import org.locationtech.jts.geom.GeometryFactory;
32
import org.locationtech.jts.geom.Point;
33
import org.locationtech.jts.geom.Polygon;
34
import org.locationtech.jts.util.GeometricShapeFactory;
35
import org.opengis.feature.GeometryAttribute;
36
import org.opengis.feature.simple.SimpleFeature;
37
import org.opengis.feature.simple.SimpleFeatureType;
38
import org.opengis.geometry.MismatchedDimensionException;
39
import org.opengis.referencing.FactoryException;
40
import org.opengis.referencing.NoSuchAuthorityCodeException;
41
import org.opengis.referencing.crs.CoordinateReferenceSystem;
42
import org.opengis.referencing.crs.ProjectedCRS;
43
import org.opengis.referencing.operation.MathTransform;
44
import org.opengis.referencing.operation.TransformException;
45

  
46
import si.uom.SI;
47
import tec.uom.se.quantity.Quantities;
48

  
49
/**
50
 * See :
51
 * 
52
 * https://gis.stackexchange.com/questions/311272/create-dynamic-circle-polygon-from-specific-lat-long-using-geotools
53
 * https://gis.stackexchange.com/questions/283183/using-geometricshapefactory-to-create-circle-with-radius-in-miles
54
 * 
55
 * https://stackoverflow.com/questions/36481651/how-do-i-create-a-circle-with-latitude-longitude-and-radius-with-geotools#36528805
56
 * 
57
 * 
58
 * Another great library for creating shapes is https://github.com/locationtech/spatial4j
59
 * 
60
 * @author Andreas Kohlbecker
61
 * @since Apr 21, 2020
62
 */
63
public class GeometryBuilder {
64

  
65
	private final static Logger logger = Logger.getLogger(GeometryBuilder.class);
66

  
67
	public enum CircleMethod {
68
		circle, simpleCircleSmall, simpleCircle, reprojectedCircle;
69
	}
70

  
71
	/**
72
	 * Fails with javax.measure.IncommensurableException: m is not compatible with
73
	 * deg
74
	 * 
75
	 * see
76
	 * https://gis.stackexchange.com/questions/283183/using-geometricshapefactory-to-create-circle-with-radius-in-miles
77
	 * 
78
	 * @param radius
79
	 * @param latitude
80
	 * @param longitude
81
	 * @return
82
	 * @throws NoSuchAuthorityCodeException
83
	 * @throws FactoryException
84
	 */
85
	public Polygon circle(Double radius, Double latitude, Double longitude)
86
			throws NoSuchAuthorityCodeException, FactoryException {
87

  
88
		Quantity<Length> radiusMeter = Quantities.getQuantity(radius, SI.METRE);
89
		Unit<Length> origUnit = (Unit<Length>) DefaultGeographicCRS.WGS84.getCoordinateSystem().getAxis(0).getUnit(); //// (Unit<Length>)
90
																														//// ////
91
																														//// origCRS.getCoordinateSystem().getAxis(0).getUnit();
92

  
93
		GeometricShapeFactory shapeFactory = new GeometricShapeFactory();
94
		shapeFactory.setNumPoints(32);
95
		shapeFactory.setCentre(new Coordinate(latitude, longitude));
96
		shapeFactory.setSize(radiusMeter.to(origUnit).getValue().doubleValue() * 2);
97

  
98
		Polygon circlePolygon = shapeFactory.createCircle();
99
		circlePolygon.setSRID(4326);
100

  
101
		return circlePolygon;
102
	}
103

  
104
	/**
105
	 * Only suitable for small radius (> 1000 m) as the circles are heavily
106
	 * distorted otherwise.
107
	 * 
108
	 * @param radius
109
	 * @param latitude
110
	 * @param longitude
111
	 * @return
112
	 */
113
	public Geometry simpleCircleSmall(Double radius, Double latitude, Double longitude) {
114

  
115
		double diameterInMeters = radius * 2.0d;
116
		GeometricShapeFactory shapeFactory = new GeometricShapeFactory();
117
		shapeFactory.setNumPoints(64); // adjustable
118
		shapeFactory.setCentre(new Coordinate(longitude, latitude));
119
		// Length in meters of 1° of latitude = always 111.32 km
120
		shapeFactory.setWidth(diameterInMeters / 111320d);
121
		// Length in meters of 1° of longitude = 40075 km * cos( latitude ) / 360
122
		shapeFactory.setHeight(diameterInMeters / (40075000 * Math.cos(Math.toRadians(latitude)) / 360));
123

  
124
		Polygon circle = shapeFactory.createEllipse();
125
		return circle;
126
	}
127

  
128
	/**
129
	 * Creates perfect circles which are looking good but might be projected
130
	 * incorrectly for the resulting map
131
	 * 
132
	 * @param distance
133
	 * @param latitude
134
	 * @param longitude
135
	 * @return
136
	 */
137
	public Geometry simpleCircle(Quantity<Length> distance, Double latitude, Double longitude) {
138

  
139
		GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
140
		CoordinateSequence coordinateSequence = geometryFactory.getCoordinateSequenceFactory()
141
				.create(new Coordinate[] { new Coordinate(longitude, latitude) });
142
		Point point = new Point(coordinateSequence, geometryFactory);
143

  
144
		GeodeticCalculator calc = new GeodeticCalculator(DefaultGeographicCRS.WGS84);
145
		calc.setStartingGeographicPoint(longitude, latitude);
146
		UnitConverter converter = distance.getUnit().getConverterTo(SI.METRE);
147
		double d = converter.convert(distance.getValue()).doubleValue();
148
		calc.setDirection(0.0, d);
149
		Point2D p2 = calc.getDestinationGeographicPoint();
150
		calc.setDirection(90.0, d);
151
		Point2D p3 = calc.getDestinationGeographicPoint();
152

  
153
		double dy = p2.getY() - latitude;
154
		double dx = p3.getX() - longitude;
155
		double dist = (dy + dx) / 2.0;
156
		Polygon p1 = (Polygon) point.buffer(dist);
157
		return p1;
158
	}
159

  
160
	/**
161
	 * This Method should produces the best circles.
162
	 * 
163
	 * The code is based on an example published by Ian Turton on stackoverflow:
164
	 * 
165
	 * https://stackoverflow.com/questions/36481651/how-do-i-create-a-circle-with-latitude-longitude-and-radius-with-geotools#36528805
166
	 * 
167
	 * see https://gist.github.com/ianturton/973563fe5004985ba35a6e2247f7d823 and
168
	 * https://gitlab.com/snippets/17558
169
	 * 
170
	 * @param feature
171
	 * @param distance
172
	 * @return
173
	 */
174
	public Geometry bufferFeature(SimpleFeature feature, Double radius) {
175

  
176
		// replacment for Measure<Double, Length> distance:
177
		Quantity<Length> radiusMeter = Quantities.getQuantity(radius, SI.METRE);
178

  
179
		// extract the geometry
180
		GeometryAttribute gProp = feature.getDefaultGeometryProperty();
181
		CoordinateReferenceSystem origCRS = gProp.getDescriptor().getCoordinateReferenceSystem();
182

  
183
		Geometry geom = (Geometry) feature.getDefaultGeometry();
184
		Geometry pGeom = geom;
185
		MathTransform toTransform, fromTransform = null;
186

  
187
		// reproject the geometry to a local projection
188
		if (!(origCRS instanceof ProjectedCRS)) {
189

  
190
			double x = geom.getCoordinate().x;
191
			double y = geom.getCoordinate().y;
192

  
193
			String code = "AUTO:42001," + x + "," + y;
194
			// System.out.println(code);
195
			CoordinateReferenceSystem auto;
196
			try {
197
				auto = CRS.decode(code);
198
				toTransform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, auto);
199
				fromTransform = CRS.findMathTransform(auto, DefaultGeographicCRS.WGS84);
200
				pGeom = JTS.transform(geom, toTransform);
201

  
202
			} catch (MismatchedDimensionException | TransformException | FactoryException e) {
203
				logger.error(e);
204
			}
205

  
206
		}
207

  
208
		// create a buffer around the geometry, assumes the geometry is in the same
209
		// units as the distance variable.
210
		Geometry out = pGeom.buffer(radiusMeter.getValue().doubleValue());
211
		Geometry retGeom = out;
212
		// reproject the geometry to the original projection
213
		if (!(origCRS instanceof ProjectedCRS)) {
214
			try {
215
				retGeom = JTS.transform(out, fromTransform);
216

  
217
			} catch (MismatchedDimensionException | TransformException e) {
218
				logger.error(e);
219
			}
220
		}
221

  
222
		return retGeom;
223
	}
224

  
225
	public static SimpleFeature createSimplePointFeature(Double longitude, Double latitude) throws SchemaException {
226
		SimpleFeatureType schema = DataUtilities.createType("", "Location", "locations:Point:srid=4326,id:Integer" // a
227
																													// number
228
																													// attribute
229
		);
230
		SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(schema);
231

  
232
		GeometryFactory geometryFactory = new GeometryFactory();
233
		/* Longitude (= x coord) first ! */
234
		Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude));
235
		featureBuilder.add(point);
236
		SimpleFeature feature = featureBuilder.buildFeature(null);
237
		return feature;
238
	}
239

  
240
}
cdmlib-ext/src/main/java/eu/etaxonomy/cdm/ext/geo/kml/KMLDocumentBuilder.java
1
/**
2
 * Copyright (C) 2020 EDIT
3
 * European Distributed Institute of Taxonomy
4
 * http://www.e-taxonomy.eu
5
 *
6
 * The contents of this file are subject to the Mozilla Public License Version 1.1
7
 * See LICENSE.TXT at the top of this package for the full license terms.
8
 */
9

  
10
package eu.etaxonomy.cdm.ext.geo.kml;
11

  
12
import java.util.ArrayList;
13
import java.util.Arrays;
14
import java.util.Collection;
15
import java.util.HashMap;
16
import java.util.HashSet;
17
import java.util.List;
18
import java.util.Map;
19
import java.util.Optional;
20
import java.util.Set;
21

  
22
import org.apache.log4j.Logger;
23
import org.geotools.feature.SchemaException;
24
import org.locationtech.jts.geom.Coordinate;
25
import org.opengis.feature.simple.SimpleFeature;
26
import org.opengis.referencing.FactoryException;
27

  
28
import de.micromata.opengis.kml.v_2_2_0.AltitudeMode;
29
import de.micromata.opengis.kml.v_2_2_0.Document;
30
import de.micromata.opengis.kml.v_2_2_0.ExtendedData;
31
import de.micromata.opengis.kml.v_2_2_0.Feature;
32
import de.micromata.opengis.kml.v_2_2_0.Kml;
33
import de.micromata.opengis.kml.v_2_2_0.KmlFactory;
34
import de.micromata.opengis.kml.v_2_2_0.LineStyle;
35
import de.micromata.opengis.kml.v_2_2_0.LinearRing;
36
import de.micromata.opengis.kml.v_2_2_0.Placemark;
37
import de.micromata.opengis.kml.v_2_2_0.PolyStyle;
38
import de.micromata.opengis.kml.v_2_2_0.Polygon;
39
import de.micromata.opengis.kml.v_2_2_0.Style;
40
import eu.etaxonomy.cdm.model.location.Point;
41
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
42
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
43
import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
44
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
45
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
46
import eu.etaxonomy.cdm.model.term.DefinedTerm;
47
import eu.etaxonomy.cdm.model.term.TermType;
48
import si.uom.SI;
49
import tec.uom.se.quantity.Quantities;
50

  
51
/**
52
 * 
53
 * @author Andreas Kohlbecker
54
 * @since Apr 21, 2020
55
 */
56
public class KMLDocumentBuilder {
57

  
58
	private final static Logger logger = Logger.getLogger(KMLDocumentBuilder.class);
59

  
60
	private Set<SpecimenOrObservationBase> occSet = new HashSet<>();
61

  
62
	private Map<String, Style> styles = new HashMap<>();
63

  
64
	private static final DefinedTerm KIND_OF_UNIT_UNSET = DefinedTerm.NewInstance(TermType.KindOfUnit);
65

  
66
	public KMLDocumentBuilder addSpecimenOrObservationBase(SpecimenOrObservationBase occurrence) {
67
		occSet.add(occurrence);
68
		return this;
69
	}
70

  
71
	public Kml build() {
72

  
73
		Kml kml = KmlFactory.createKml();
74
		List<Feature> documentFeatures = new ArrayList<>();
75

  
76
		for (SpecimenOrObservationBase sob : occSet) {
77
			if (sob instanceof FieldUnit) {
78
				GatheringEvent gatherEvent = ((FieldUnit) sob).getGatheringEvent();
79
				if (gatherEvent != null && isValidPoint(gatherEvent.getExactLocation())) {
80
					createFieldUnitPlacemarks(documentFeatures, sob, gatherEvent, LocationType.FIELD_UNIT, sob.getKindOfUnit());
81
				}
82
			}
83
			if (sob instanceof DerivedUnit) {
84
				walkDerivationPath((DerivedUnit) sob, (DerivedUnit) sob, documentFeatures, null);
85
			}
86
		}
87

  
88
		Document doc = kml.createAndSetDocument();
89
		doc.getFeature().addAll(documentFeatures);
90
		doc.getStyleSelector().addAll(styles.values());
91
		return kml;
92
	}
93
	
94
	/**
95
	 * @param derivedUnit
96
	 * @param documentFeatures
97
	 * @param kindOfUnit
98
	 */
99
	private void walkDerivationPath(DerivedUnit derivedUnit, DerivedUnit unitOfInterest, List<Feature> documentFeatures,
100
			DefinedTerm kindOfUnit) {
101

  
102
		if (kindOfUnit == null) {
103
			if (derivedUnit.getKindOfUnit() != null) {
104
				kindOfUnit = derivedUnit.getKindOfUnit();
105
			} else {
106
				kindOfUnit = KIND_OF_UNIT_UNSET;
107
			}
108
		}
109

  
110
		Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
111
		if (originals != null) {
112
			for (SpecimenOrObservationBase<?> original : originals) {
113
				if (original instanceof FieldUnit) {
114
					GatheringEvent gatherEvent = ((FieldUnit) original).getGatheringEvent();
115
					if (gatherEvent != null && isValidPoint(gatherEvent.getExactLocation())) {
116
						createFieldUnitPlacemarks(documentFeatures, unitOfInterest, gatherEvent, LocationType.DERIVED_UNIT, kindOfUnit);
117
					}
118
				} else {
119
					walkDerivationPath((DerivedUnit) original, unitOfInterest, documentFeatures, kindOfUnit);
120
				}
121
			}
122
		}
123
	}
124

  
125
	/**
126
	 * @param documentFeatures
127
	 * @param sob
128
	 * @param gatherEvent
129
	 */
130
	private void createFieldUnitPlacemarks(List<Feature> documentFeatures, SpecimenOrObservationBase sob,
131
			GatheringEvent gatherEvent, LocationType locationType, DefinedTerm kindOfUnit) {
132
		Placemark mapMarker = fieldUnitLocationMarker(gatherEvent.getExactLocation(),
133
				gatherEvent.getAbsoluteElevation(), locationType, kindOfUnit);
134
		documentFeatures.add(mapMarker);
135
		addExtendedData(mapMarker, sob, gatherEvent);
136
		errorRadiusPlacemark(gatherEvent.getExactLocation()).ifPresent(pm -> documentFeatures.add(pm));
137
	}
138

  
139

  
140
	/**
141
	 * @param exactLocation
142
	 * @param altitude
143
	 * @param locationType
144
	 * @param kindOfUnit
145
	 * @return
146
	 */
147
	private Placemark fieldUnitLocationMarker(Point exactLocation, Integer altitude, LocationType locationType,
148
			DefinedTerm kindOfUnit) {
149

  
150
		Placemark mapMarker = KmlFactory.createPlacemark();
151
		de.micromata.opengis.kml.v_2_2_0.Point point = KmlFactory.createPoint();
152
		point.setAltitudeMode(AltitudeMode.ABSOLUTE);
153
		if (altitude != null) {
154
			point.setCoordinates(Arrays.asList(KmlFactory.createCoordinate(exactLocation.getLongitude(),
155
					exactLocation.getLatitude(), altitude.doubleValue())));
156
		} else {
157
			point.setCoordinates(Arrays
158
					.asList(KmlFactory.createCoordinate(exactLocation.getLongitude(), exactLocation.getLatitude())));
159
		}
160
		mapMarker.setGeometry(point);
161
		mapMarker.setStyleUrl(styleURL(locationType, kindOfUnit));
162

  
163
		return mapMarker;
164
	}
165
	
166
	private Optional<Placemark> errorRadiusPlacemark(Point exactLocation) {
167

  
168
		// exactLocation.setErrorRadius(25 * 1000); // METER // uncomment for debugging
169
		
170
		Placemark errorRadiusCicle = null;
171
		if (exactLocation.getErrorRadius() != null && exactLocation.getErrorRadius() > 0) {
172
			errorRadiusCicle = KmlFactory.createPlacemark();
173
			LinearRing cirle = createKMLCircle(exactLocation.getLongitude(), exactLocation.getLatitude(),
174
					exactLocation.getErrorRadius());
175
			Polygon polygon = errorRadiusCicle.createAndSetPolygon();
176
			polygon.createAndSetOuterBoundaryIs().setLinearRing(cirle);
177
			polygon.setExtrude(true);
178
			errorRadiusCicle.setStyleUrl(errorRadiusStyleURL());
179
		}
180

  
181
		return Optional.ofNullable(errorRadiusCicle);
182
	}
183
	
184
	
185
	/**
186
	 * @param longitude
187
	 * @param latitude
188
	 * @param errorRadiusMeter
189
	 * @return
190
	 */
191
	private LinearRing createKMLCircle(Double longitude, Double latitude, Integer errorRadiusMeter) {
192

  
193
		GeometryBuilder.CircleMethod method = GeometryBuilder.CircleMethod.reprojectedCircle;
194
		LinearRing lineString = KmlFactory.createLinearRing();
195
		lineString.setAltitudeMode(AltitudeMode.RELATIVE_TO_GROUND);
196

  
197
		org.locationtech.jts.geom.Geometry polygonGeom = null;
198
		GeometryBuilder gb = new GeometryBuilder();
199
		try {
200
			switch (method) {
201
			case simpleCircle:
202
				// this is the best method so far !!!!
203
				polygonGeom = gb.simpleCircle(Quantities.getQuantity(errorRadiusMeter.doubleValue(), SI.METRE),
204
						latitude, longitude);
205
				break;
206
			case simpleCircleSmall:
207
				// Only suitable for small radius (> 1000 m) as the circles are heavily distorted
208
				// otherwise.
209
				polygonGeom = gb.simpleCircleSmall(errorRadiusMeter.doubleValue(), latitude, longitude);
210
				break;
211
			case circle:
212
				// fails
213
				polygonGeom = gb.circle(errorRadiusMeter.doubleValue(), latitude, longitude);
214
				break;
215
			case reprojectedCircle:
216
				// incomplete
217
				SimpleFeature pointFeature = gb.createSimplePointFeature(longitude, latitude);
218
				polygonGeom = gb.bufferFeature(pointFeature, errorRadiusMeter.doubleValue());
219
				break;
220
			}
221
			for (Coordinate coordinate : polygonGeom.getCoordinates()) {
222
				lineString.addToCoordinates(coordinate.getX(), coordinate.getY());
223
			}
224
		} catch (FactoryException e) {
225
			logger.error("Polygon creation for error radius failed", e);
226
		} catch (SchemaException e) {
227
			logger.error("SimplePointFeature creation failed", e);
228
		}
229
		return lineString;
230
	}
231
	
232
	private void addExtendedData(Placemark mapMarker, SpecimenOrObservationBase sob, GatheringEvent gatherEvent) {
233
		
234
		String name = "";
235
		if(sob.getRecordBasis() != null) {
236
			name = sob.getRecordBasis().name();
237
		} else {
238
			name = SpecimenOrObservationType.Unknown.name();
239
		}
240
		String titleCache = sob.getTitleCache();
241
		String locationString = null;
242
		if(gatherEvent.getExactLocation() != null) {
243
			locationString = gatherEvent.getExactLocation().toSexagesimalString(false,  true);
244
			titleCache = titleCache.replace(locationString + ", ", "");
245
		}
246
		String description = "<p class=\"title-cache\">" + titleCache 
247
				+ " <a class=\"specimen-link\" href=\"${specimen-link-base-url}/" + sob.getUuid() + "\">${specimen-link-text}</a>"
248
				+ "</p>";
249
		if(locationString != null) {
250
			// see https://www.mediawiki.org/wiki/GeoHack
251
			description += "<p class=\"exact-location\"><a target=\"geohack\" href=\"https://tools.wmflabs.org/geohack/en/" + 
252
		gatherEvent.getExactLocation().getLatitude() + ";" + gatherEvent.getExactLocation().getLongitude() +"?pagename=" + name + "\">" + locationString + "</a></p>";
253
		}
254
		// mapMarker.setName(name);
255
		mapMarker.setDescription(description);
256
		
257
		ExtendedData extendedData = mapMarker.createAndSetExtendedData();
258
		extendedData.createAndAddData(sob.getTitleCache()).setName("titleCache");
259
		if(mapMarker.getGeometry() != null && mapMarker.getGeometry() instanceof de.micromata.opengis.kml.v_2_2_0.Point) {
260
			extendedData.createAndAddData(((de.micromata.opengis.kml.v_2_2_0.Point)mapMarker.getGeometry()).getCoordinates().toString()).setName("Location");
261
		}
262
		
263
	}
264

  
265

  
266
	private String styleURL(LocationType locationType, DefinedTerm kindOfUnit) {
267
		String key = "";
268
		if (locationType != null) {
269
			key += locationType.name();
270
		}
271
		if (kindOfUnit != null) {
272
			key += kindOfUnit.getUuid();
273
		}
274
		if (!styles.containsKey(key)) {
275
			Style style = KmlFactory.createStyle().withIconStyle(MapMarkerIcons.red_blank.asIconStyle());
276
			style.setId(key);
277
			styles.put(key, style);
278
		}
279
		return "#" + key;
280
	}
281

  
282
	private String errorRadiusStyleURL() {
283
		String key = "ERROR_RADIUS";
284
		if (!styles.containsKey(key)) {
285
			Style style = KmlFactory.createStyle();
286
			PolyStyle polyStyle = style.createAndSetPolyStyle();
287
			polyStyle.setColor("100000ee"); // aabbggrr, where aa=alpha (00 to ff); bb=blue (00 to ff); gg=green (00 to ff); rr=red (00 to ff).
288
			polyStyle.setFill(true);
289
			polyStyle.setOutline(true);
290
			style.setId(key);
291
			LineStyle lineStyle = style.createAndSetLineStyle();
292
			// lineStyle.setColor("ff880088"); // aabbggrr, where aa=alpha (00 to ff);
293
			// bb=blue (00 to ff); gg=green (00 to ff); rr=red (00 to ff).
294
			lineStyle.setWidth(1);
295
			styles.put(key, style);
296
		}
297
		return "#" + key;
298
	}
299

  
300

  
301
	// TODO use also for .EditGeoService.registerDerivedUnitLocations(DerivedUnit
302
	// derivedUnit, List<Point> derivedUnitPoints) !!!!!!!!!
303
	public boolean isValidPoint(Point point) {
304

  
305
		if (point == null) {
306
			return false;
307
		}
308
		// points with no longitude or latitude should not exist
309
		// see #4173 ([Rule] Longitude and Latitude in Point must not be null)
310
		if (point.getLatitude() == null || point.getLongitude() == null) {
311
			return false;
312
		}
313
		// FIXME: remove next statement after
314
		// DerivedUnitFacade or ABCD import is fixed
315
		//
316
		if (point.getLatitude() == 0.0 || point.getLongitude() == 0.0) {
317
			return false;
318
		}
319
		return true;
320
	}
321

  
322
	public enum LocationType {
323
		FIELD_UNIT, DERIVED_UNIT;
324
	}
325
}
cdmlib-ext/src/main/java/eu/etaxonomy/cdm/ext/geo/kml/MapMarkerIcons.java
1
/**
2
 * Copyright (C) 2020 EDIT
3
 * European Distributed Institute of Taxonomy
4
 * http://www.e-taxonomy.eu
5
 *
6
 * The contents of this file are subject to the Mozilla Public License Version 1.1
7
 * See LICENSE.TXT at the top of this package for the full license terms.
8
 */
9

  
10
package eu.etaxonomy.cdm.ext.geo.kml;
11

  
12
import java.net.MalformedURLException;
13
import java.net.URL;
14

  
15
import de.micromata.opengis.kml.v_2_2_0.ColorMode;
16
import de.micromata.opengis.kml.v_2_2_0.IconStyle;
17
import de.micromata.opengis.kml.v_2_2_0.KmlFactory;
18
import de.micromata.opengis.kml.v_2_2_0.Units;
19
import de.micromata.opengis.kml.v_2_2_0.Vec2;
20

  
21
/**
22
 * 
23
 * MOre icons at http://kml4earth.appspot.com/icons.html
24
 * 
25
 * @author Andreas Kohlbecker
26
 * @since Apr 21, 2020
27
 */
28
public enum MapMarkerIcons {
29
	
30
	blu_blank("http://maps.google.com/mapfiles/kml/paddle/blu-blank.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
31
	blu_circle("http://maps.google.com/mapfiles/kml/paddle/blu-circle.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
32
	blu_diamond("http://maps.google.com/mapfiles/kml/paddle/blu-diamond.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
33
	blu_square("http://maps.google.com/mapfiles/kml/paddle/blu-sqare.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
34
	blu_stars("http://maps.google.com/mapfiles/kml/paddle/blu-stars.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
35
	
36
	grn_blank("http://maps.google.com/mapfiles/kml/paddle/grn-blank.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
37
	grn_circle("http://maps.google.com/mapfiles/kml/paddle/grn-circle.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
38
	grn_diamond("http://maps.google.com/mapfiles/kml/paddle/grn-diamond.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
39
	grn_square("http://maps.google.com/mapfiles/kml/paddle/grn-sqare.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
40
	grn_stars("http://maps.google.com/mapfiles/kml/paddle/grn-stars.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
41
	
42
	ltblu_blank("http://maps.google.com/mapfiles/kml/paddle/ltblu-blank.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
43
	ltblu_circle("http://maps.google.com/mapfiles/kml/paddle/ltblu-circle.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
44
	ltblu_diamond("http://maps.google.com/mapfiles/kml/paddle/ltblu-diamond.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
45
	ltblu_square("http://maps.google.com/mapfiles/kml/paddle/ltblu-sqare.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
46
	ltblu_stars("http://maps.google.com/mapfiles/kml/paddle/ltblu-stars.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
47
	
48
	pink_blank("http://maps.google.com/mapfiles/kml/paddle/pink-blank.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
49
	pink_circle("http://maps.google.com/mapfiles/kml/paddle/pink-circle.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
50
	pink_diamond("http://maps.google.com/mapfiles/kml/paddle/pink-diamond.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
51
	pink_square("http://maps.google.com/mapfiles/kml/paddle/pink-sqare.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
52
	pink_stars("http://maps.google.com/mapfiles/kml/paddle/pink-stars.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
53
	
54
	purple_blank("http://maps.google.com/mapfiles/kml/paddle/purple-blank.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
55
	purple_circle("http://maps.google.com/mapfiles/kml/paddle/purple-circle.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
56
	purple_diamond("http://maps.google.com/mapfiles/kml/paddle/purple-diamond.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
57
	purple_square("http://maps.google.com/mapfiles/kml/paddle/purple-sqare.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
58
	purple_stars("http://maps.google.com/mapfiles/kml/paddle/purple-stars.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
59
	
60
	red_blank("http://maps.google.com/mapfiles/kml/paddle/red-blank.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
61
	red_circle("http://maps.google.com/mapfiles/kml/paddle/red-circle.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
62
	red_diamond("http://maps.google.com/mapfiles/kml/paddle/red-diamond.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
63
	red_square("http://maps.google.com/mapfiles/kml/paddle/red-sqare.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
64
	red_stars("http://maps.google.com/mapfiles/kml/paddle/red-stars.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
65
	
66
	ylw_blank("http://maps.google.com/mapfiles/kml/paddle/ylw-blank.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
67
	ylw_circle("http://maps.google.com/mapfiles/kml/paddle/ylw-circle.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
68
	ylw_diamond("http://maps.google.com/mapfiles/kml/paddle/ylw-diamond.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
69
	ylw_square("http://maps.google.com/mapfiles/kml/paddle/ylw-sqare.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
70
	ylw_stars("http://maps.google.com/mapfiles/kml/paddle/ylw-stars.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
71
	
72
	orange_blank("http://maps.google.com/mapfiles/kml/paddle/orange-blank.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
73
	orange_circle("http://maps.google.com/mapfiles/kml/paddle/orange-circle.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
74
	orange_diamond("http://maps.google.com/mapfiles/kml/paddle/orange-diamond.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
75
	orange_square("http://maps.google.com/mapfiles/kml/paddle/orange-sqare.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
76
	orange_stars("http://maps.google.com/mapfiles/kml/paddle/orange-stars.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
77
	
78
	wht_blank("http://maps.google.com/mapfiles/kml/paddle/wht-blank.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
79
	wht_circle("http://maps.google.com/mapfiles/kml/paddle/wht-circle.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
80
	wht_diamond("http://maps.google.com/mapfiles/kml/paddle/wht-diamond.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
81
	wht_square("http://maps.google.com/mapfiles/kml/paddle/wht-sqare.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
82
	wht_stars("http://maps.google.com/mapfiles/kml/paddle/wht-stars.png", KmlFactory.createVec2().withX(0.5).withY(0).withXunits(Units.FRACTION).withYunits(Units.FRACTION)),
83
	
84
	sunny("http://maps.google.com/mapfiles/kml/shapes/sunny.png", null);
85
	
86
	URL url;
87
	Vec2 hotspot;
88

  
89
	MapMarkerIcons(String ulr, Vec2 hotspot) {
90
		try {
91
			this.url = new URL(ulr);
92
			if(hotspot != null) {
93
				this.hotspot = hotspot;
94
			}
95
		} catch (MalformedURLException e) {
96
			throw new RuntimeException(e);
97
		}
98
	}
99
	
100
	@Override
101
	public String toString() {
102
		return url.toString();
103
	}
104
	
105
	public Vec2 hotSpot() {
106
		return hotspot;
107
	}
108
	
109
	public IconStyle asIconStyle() {
110
		IconStyle iconStyle = KmlFactory.createIconStyle().withIcon(
111
				KmlFactory.createIcon().withHref(this.toString())
112
				);
113
		iconStyle.setScale(1);
114
		iconStyle.setHotSpot(hotspot);
115
		return iconStyle;
116
	}
117

  
118
}
cdmlib-ext/src/test/java/eu/etaxonomy/cdm/ext/kml/KmlJaxbMarshallerTest.java
1
package eu.etaxonomy.cdm.ext.kml;
2

  
3
import static org.junit.Assert.assertTrue;
4

  
5
import java.io.File;
6
import java.io.FileWriter;
7
import java.io.IOException;
8
import java.io.StringWriter;
9

  
10
import javax.xml.bind.JAXBContext;
11
import javax.xml.bind.JAXBException;
12
import javax.xml.bind.Marshaller;
13

  
14
import org.apache.commons.io.FileUtils;
15
import org.apache.log4j.Level;
16
import org.apache.log4j.Logger;
17
import org.junit.Before;
18
import org.junit.Test;
19

  
20
import de.micromata.opengis.kml.v_2_2_0.Kml;
21
import eu.etaxonomy.cdm.ext.geo.kml.KMLDocumentBuilder;
22
import eu.etaxonomy.cdm.model.location.Point;
23
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
24
import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
25

  
26
public class KmlJaxbMarshallerTest {
27
	
28
	Kml kml;
29
	
30
	Logger logger;
31
	
32
	@Before
33
	public void makeKML() {
34
		FieldUnit fu = FieldUnit.NewInstance();
35
		fu.setGatheringEvent(GatheringEvent.NewInstance());
36
		fu.getGatheringEvent().setExactLocation(Point.NewInstance(-112.292238941097, 36.09520916122063, null, null));
37
		
38
		KMLDocumentBuilder builder = new KMLDocumentBuilder();
39
		builder.addSpecimenOrObservationBase(fu);
40
		kml = builder.build();
41
		
42
		logger = Logger.getLogger(this.getClass());
43
		logger.setLevel(Level.DEBUG);
44
	}
45
	
46
	
47
	@Test
48
	public void marshallTest() throws JAXBException, IOException {
49

  
50
		JAXBContext jaxbContext = JAXBContext.newInstance(Kml.class);
51
		Marshaller marshaller = jaxbContext.createMarshaller();
52
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
53
		StringWriter sw = new StringWriter();
54
		marshaller.marshal(kml, sw);
55
		String kml = sw.toString();
56
		if(logger.isDebugEnabled()) {
57
			logger.debug("kml:\n" + kml);
58
			FileUtils.write(new File("KmlJaxbMarshallerTest.kml"), kml);
59
		}
60
		assertTrue(kml.contains("<kml:Document>"));
61
		assertTrue(kml.contains("<kml:Point>"));
62
		assertTrue(kml.contains("<kml:coordinates>-112.292238941097,36.09520916122063</kml:coordinates>"));
63
		assertTrue(kml.contains("<kml:altitudeMode>absolute</kml:altitudeMode>"));
64
	}
65

  
66
}
cdmlib-remote-webapp/src/main/java/eu/etaxonomy/cdm/remote/config/CdmSpringMVCConfig.java
107 107

  
108 108
    }
109 109

  
110
    @Bean
111
    @DependsOn({"requestMappingHandlerAdapter"})
112
    public XmlViewResolver getOaiXmlViewResolver() {
113
        XmlViewResolver resolver = new XmlViewResolver();
114
      resolver.setOrder(1);
115
      resolver.setLocation(new ServletContextResource(servletContext,"/WEB-INF/oai-views.xml"));
116
      resolver.setCache(XML_VIEW_CACHING);
117
      return resolver;
118
    }
119

  
120

  
121

  
110
	@Bean
111
	@DependsOn({ "requestMappingHandlerAdapter" })
112
	public XmlViewResolver getOaiXmlViewResolver() {
113
		XmlViewResolver resolver = new XmlViewResolver();
114
		resolver.setOrder(1);
115
		resolver.setLocation(new ServletContextResource(servletContext, "/WEB-INF/oai-views.xml"));
116
		resolver.setCache(XML_VIEW_CACHING);
117
		return resolver;
118
	}
119

  
120
	@Bean
121
	@DependsOn({ "requestMappingHandlerAdapter" })
122
	public XmlViewResolver kmlXmlViewResolver() {
123
		XmlViewResolver resolver = new PatternViewResolver();
124
		resolver.setOrder(1);
125
		resolver.setLocation(new ServletContextResource(servletContext, "/WEB-INF/kml-views.xml"));
126
		resolver.setCache(XML_VIEW_CACHING);
127
		return resolver;
128
	}
122 129

  
123 130
    /* (non-Javadoc)
124 131
     * @see org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration#addInterceptors(org.springframework.web.servlet.config.annotation.InterceptorRegistry)
cdmlib-remote-webapp/src/main/webapp/WEB-INF/kml-views.xml
1
<?xml version="1.0" encoding="UTF-8"?>
2
<beans xmlns="http://www.springframework.org/schema/beans"
3
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
    xsi:schemaLocation="http://www.springframework.org/schema/beans
5
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
6

  
7
    <!--
8
        ===============================================================
9
        This is the configuration file for a
10
        eu.etaxonomy.cdm.remote.view.XMLViewResolver
11
        ===============================================================
12
    -->
13
    <bean name="**/kml/** **/kml/*" class="org.springframework.web.servlet.view.xml.MarshallingView">
14
        <property name="contentType" value="text/xml; charset=UTF-8"/>
15
        <property name="marshaller" ref="kmlMarshaller"/><!-- see /cdmlib-remote/src/main/resources/eu/etaxonomy/cdm/remote.xml -->
16
    </bean>
17
</beans>
cdmlib-remote/src/main/java/eu/etaxonomy/cdm/remote/controller/OptionsController.java
1
package eu.etaxonomy.cdm.remote.controller;
2

  
3
import org.springframework.http.HttpStatus;
4
import org.springframework.http.ResponseEntity;
5
import org.springframework.stereotype.Controller;
6
import org.springframework.web.bind.annotation.CrossOrigin;
7
import org.springframework.web.bind.annotation.RequestMapping;
8
import org.springframework.web.bind.annotation.RequestMethod;
9

  
10
@CrossOrigin(origins = "*", maxAge = 3600)
11
@Controller
12
public class OptionsController {
13

  
14
	@RequestMapping(
15
            value = "/**",
16
            method = RequestMethod.OPTIONS
17
    )
18
    public ResponseEntity handle() {
19
        return new ResponseEntity(HttpStatus.OK);
20
    }
21

  
22
}
cdmlib-remote/src/main/java/eu/etaxonomy/cdm/remote/controller/ext/ExternalGeoController.java
24 24

  
25 25
import org.apache.log4j.Logger;
26 26
import org.springframework.beans.factory.annotation.Autowired;
27
import org.springframework.http.HttpStatus;
28
import org.springframework.http.ResponseEntity;
27 29
import org.springframework.stereotype.Controller;
28 30
import org.springframework.web.bind.WebDataBinder;
31
import org.springframework.web.bind.annotation.CrossOrigin;
29 32
import org.springframework.web.bind.annotation.InitBinder;
30 33
import org.springframework.web.bind.annotation.PathVariable;
31 34
import org.springframework.web.bind.annotation.RequestMapping;
32 35
import org.springframework.web.bind.annotation.RequestMethod;
33 36
import org.springframework.web.bind.annotation.RequestParam;
37
import org.springframework.web.bind.annotation.RestController;
34 38
import org.springframework.web.servlet.ModelAndView;
35 39

  
36 40
import eu.etaxonomy.cdm.api.service.IDescriptionService;
......
80 84
 * @since 18.06.2009
81 85
 *
82 86
 */
87

  
83 88
@Controller
84 89
@Api(value="mapServiceParameters")
85 90
@RequestMapping(value = { "ext/edit/mapServiceParameters/" })
......
194 199
     * @throws IOException TODO write controller method documentation
195 200
     */
196 201
    @RequestMapping(value = { "taxonOccurrencesFor/{uuid}" }, method = RequestMethod.GET)
197
    public ModelAndView doGetOccurrenceMapUriParams(
202
    public OccurrenceServiceRequestParameterDto doGetOccurrenceMapUriParams(
198 203
            @PathVariable("uuid") UUID uuid,
199 204
            @RequestParam(value = "relationships", required = false) UuidList relationshipUuids,
200 205
            @RequestParam(value = "relationshipsInvers", required = false) UuidList relationshipInversUuids,
......
203 208
            HttpServletResponse response)
204 209
            throws IOException {
205 210

  
206
        Map<SpecimenOrObservationType, Color> specimenOrObservationTypeColors = null;
207 211

  
208 212
        logger.info("doGetOccurrenceMapUriParams() " + requestPathAndQuery(request));
209
        ModelAndView mv = new ModelAndView();
210 213

  
211
        Set<TaxonRelationshipEdge> includeRelationships = ControllerUtils.loadIncludeRelationships(
214
        Map<SpecimenOrObservationType, Color> specimenOrObservationTypeColors = null;
215

  
216
        List<SpecimenOrObservationBase> specimensOrObersvations = occurencesForTaxon(uuid, relationshipUuids,
217
				relationshipInversUuids, maxDepth, response);
218

  
219
        OccurrenceServiceRequestParameterDto dto = geoservice.getOccurrenceServiceRequestParameters(specimensOrObersvations,
220
                specimenOrObservationTypeColors );
221
   
222
        return dto;
223
    }
224
    
225
	private List<SpecimenOrObservationBase> occurencesForTaxon(UUID taxonUuid, UuidList relationshipUuids,
226
			UuidList relationshipInversUuids, Integer maxDepth, HttpServletResponse response) throws IOException {
227
		Set<TaxonRelationshipEdge> includeRelationships = ControllerUtils.loadIncludeRelationships(
212 228
                relationshipUuids, relationshipInversUuids, termService);
213 229

  
214
        Taxon taxon = getCdmBaseInstance(Taxon.class, uuid, response, (List<String>)null);
230
        Taxon taxon = getCdmBaseInstance(Taxon.class, taxonUuid, response, (List<String>)null);
215 231

  
216 232
        List<OrderHint> orderHints = new ArrayList<OrderHint>();
217 233
        orderHints.add(new OrderHint("titleCache", SortOrder.DESCENDING));
218 234

  
219 235
        List<SpecimenOrObservationBase> specimensOrObersvations = occurrenceService.listByAssociatedTaxon(
220 236
                null, includeRelationships, taxon, maxDepth, null, null, orderHints, null);
221

  
222
        OccurrenceServiceRequestParameterDto dto = geoservice.getOccurrenceServiceRequestParameterString(specimensOrObersvations,
223
                specimenOrObservationTypeColors );
224
        mv.addObject(dto);
225
        return mv;
226
    }
237
		return specimensOrObersvations;
238
	}
227 239

  
228 240
    /**
229 241
     * Assembles and returns URI parameter Strings for the EDIT Map Service. The distribution areas for the
cdmlib-remote/src/main/java/eu/etaxonomy/cdm/remote/controller/ext/KmlController.java
1
/**
2
 * Copyright (C) 2009 EDIT
3
 * European Distributed Institute of Taxonomy
4
 * http://www.e-taxonomy.eu
5
 *
6
 * The contents of this file are subject to the Mozilla Public License Version 1.1
7
 * See LICENSE.TXT at the top of this package for the full license terms.
8
 */
9
package eu.etaxonomy.cdm.remote.controller.ext;
10

  
11
import java.awt.Color;
12
import java.io.IOException;
13
import java.util.ArrayList;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.Set;
17
import java.util.UUID;
18

  
19
import javax.servlet.http.HttpServletRequest;
20
import javax.servlet.http.HttpServletResponse;
21

  
22
import org.apache.log4j.Logger;
23
import org.springframework.beans.factory.annotation.Autowired;
24
import org.springframework.stereotype.Controller;
25
import org.springframework.web.bind.WebDataBinder;
26
import org.springframework.web.bind.annotation.CrossOrigin;
27
import org.springframework.web.bind.annotation.InitBinder;
28
import org.springframework.web.bind.annotation.PathVariable;
29
import org.springframework.web.bind.annotation.RequestMapping;
30
import org.springframework.web.bind.annotation.RequestMethod;
31
import org.springframework.web.bind.annotation.RequestParam;
32

  
33
import de.micromata.opengis.kml.v_2_2_0.Kml;
34
import eu.etaxonomy.cdm.api.service.IOccurrenceService;
35
import eu.etaxonomy.cdm.api.service.ITaxonService;
36
import eu.etaxonomy.cdm.api.service.ITermService;
37
import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
38
import eu.etaxonomy.cdm.database.UpdatableRoutingDataSource;
39
import eu.etaxonomy.cdm.ext.geo.IEditGeoService;
40
import eu.etaxonomy.cdm.model.common.MarkerType;
41
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
42
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
43
import eu.etaxonomy.cdm.model.taxon.Taxon;
44
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
45
import eu.etaxonomy.cdm.persistence.query.OrderHint;
46
import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder;
47
import eu.etaxonomy.cdm.remote.controller.BaseController;
48
import eu.etaxonomy.cdm.remote.controller.util.ControllerUtils;
49
import eu.etaxonomy.cdm.remote.editor.DefinedTermBaseList;
50
import eu.etaxonomy.cdm.remote.editor.TermBaseListPropertyEditor;
51
import eu.etaxonomy.cdm.remote.editor.UUIDListPropertyEditor;
52
import eu.etaxonomy.cdm.remote.editor.UuidList;
53
import io.swagger.annotations.Api;
54

  
55
/**
56
 * The ExternalGeoController class is a Spring MVC Controller.
57
 * <p>
58
 * The syntax of the mapped service URIs contains the the {datasource-name} path element.
59
 * The available {datasource-name}s are defined in a configuration file which
60
 * is loaded by the {@link UpdatableRoutingDataSource}. If the
61
 * UpdatableRoutingDataSource is not being used in the actual application
62
 * context any arbitrary {datasource-name} may be used.
63
 * <p>
64
 * @author a.kohlbecker
65
 * @since 18.06.2009
66
 *
67
 */
68
@CrossOrigin(origins = "*")
69
@Controller
70
@Api(value="mapServiceParameters")
71
@RequestMapping(value = { "kml" })
72
public class KmlController extends BaseController<TaxonBase, ITaxonService> {
73

  
74
    public static final Logger logger = Logger.getLogger(KmlController.class);
75

  
76
    @Autowired
77
    private IEditGeoService geoservice;
78

  
79
    @Autowired
80
    private IOccurrenceService occurrenceService;
81

  
82
    @Autowired
83
    private ITermService termService;
84

  
85
    @InitBinder
86
    @Override
87
    public void initBinder(WebDataBinder binder) {
88
        super.initBinder(binder);
89
        binder.registerCustomEditor(UuidList.class, new UUIDListPropertyEditor());
90
        binder.registerCustomEditor(DefinedTermBaseList.class, new TermBaseListPropertyEditor<MarkerType>(termService));
91
    }
92

  
93
    @Autowired
94
    @Override
95
    public void setService(ITaxonService service) {
96
        this.service = service;
97
    }
98
    
99
    /**
100
     * Assembles and returns URI parameter Strings for the EDIT Map Service. The distribution areas for the
101
     * {@link Taxon} instance identified by the <code>{taxon-uuid}</code> are found and are translated into
102
     * an valid URI parameter String. Higher level distribution areas are expanded in order to include all
103
     * nested sub-areas.
104
     * <p>
105
     * URI: <b>&#x002F;{datasource-name}&#x002F;geo&#x002F;map&#x002F;distribution&#x002F;{taxon-uuid}</b>
106
     *
107
     * @param request
108
     * @param response
109
     * @return URI parameter Strings for the EDIT Map Service
110
     * @throws IOException TODO write controller method documentation
111
     */
112
    @RequestMapping(value = { "taxonOccurrencesFor/{uuid}" }, method = RequestMethod.GET)
113
    public Kml doGetOccurrenceKml(
114
            @PathVariable("uuid") UUID uuid,
115
            @RequestParam(value = "relationships", required = false) UuidList relationshipUuids,
116
            @RequestParam(value = "relationshipsInvers", required = false) UuidList relationshipInversUuids,
117
            @RequestParam(value = "maxDepth", required = false) Integer maxDepth,
118
            HttpServletRequest request,
119
            HttpServletResponse response)
120
            throws IOException {
121

  
122

  
123
        logger.info("doGetOccurrenceKml() " + requestPathAndQuery(request));
124

  
125
        Map<SpecimenOrObservationType, Color> specimenOrObservationTypeColors = null;
126

  
127
        List<SpecimenOrObservationBase> specimensOrObersvations = occurencesForTaxon(uuid, relationshipUuids,
128
				relationshipInversUuids, maxDepth, response);
129

  
130
        Kml kml = geoservice.occurrencesToKML(specimensOrObersvations, specimenOrObservationTypeColors);
131
   
132
        return kml;
133
    }
134

  
135
	private List<SpecimenOrObservationBase> occurencesForTaxon(UUID taxonUuid, UuidList relationshipUuids,
136
			UuidList relationshipInversUuids, Integer maxDepth, HttpServletResponse response) throws IOException {
137
		Set<TaxonRelationshipEdge> includeRelationships = ControllerUtils.loadIncludeRelationships(
138
                relationshipUuids, relationshipInversUuids, termService);
139

  
140
        Taxon taxon = getCdmBaseInstance(Taxon.class, taxonUuid, response, (List<String>)null);
141

  
142
        List<OrderHint> orderHints = new ArrayList<OrderHint>();
143
        orderHints.add(new OrderHint("titleCache", SortOrder.DESCENDING));
144

  
145
        List<SpecimenOrObservationBase> specimensOrObersvations = occurrenceService.listByAssociatedTaxon(
146
                null, includeRelationships, taxon, maxDepth, null, null, orderHints, null);
147
		return specimensOrObersvations;
148
	}
149

  
150

  
151
}
cdmlib-remote/src/main/java/eu/etaxonomy/cdm/remote/view/XmlView.java
6 6
import javax.servlet.http.HttpServletResponse;
7 7
import javax.xml.transform.stream.StreamResult;
8 8

  
9
import org.springframework.beans.factory.annotation.Autowired;
10
import org.springframework.beans.factory.annotation.Qualifier;
11 9
import org.springframework.oxm.Marshaller;
12 10
import org.springframework.web.servlet.view.AbstractView;
13 11

  
14 12
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
15 13

  
16 14
/**
17
 * View class which takes a serializes a cdm object as xml
15
 * View class which serializes objects as xml
16
 * 
18 17
 * @author ben
19 18
 * @see javax.xml.transform.Source
20 19
 * @see com.ibm.lsid.MetadataResponse
20
 * @deprecated use {@link org.springframework.web.servlet.view.xml.MarshallingView} instead
21 21
 */
22
@Deprecated
22 23
public class XmlView extends AbstractView {
23 24

  
24 25
    private Marshaller marshaller;
......
39 40

  
40 41
    }
41 42

  
42
    @Autowired
43
    @Qualifier("marshaller")
44 43
    public void setMarshaller(Marshaller marshaller) {
45 44
        this.marshaller = marshaller;
46 45
    }
47 46

  
48 47

  
49

  
50 48
    @Override
51 49
    protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response)
52 50
            throws Exception {
cdmlib-remote/src/main/resources/eu/etaxonomy/cdm/remote.xml
47 47
      </map>
48 48
    </property>
49 49
  </bean>
50
  
51
   <bean id="kmlMarshaller" name="kmlMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
52
    <property name="classesToBeBound">
53
      <list>
54
        <value>de.micromata.opengis.kml.v_2_2_0.Kml</value>
55
      </list>
56
    </property>
57
   	<property name="checkForXmlRootElement" value="false" />
58
   	<property name="marshallerProperties">
59
      <map>
60
        <entry key="jaxb.formatted.output"><value type="java.lang.Boolean">true</value></entry>
61
      </map>
62
    </property>
63
   </bean>
50 64

  
51 65
  <bean id="dozerMapper" class="eu.etaxonomy.cdm.remote.dto.assembler.DozerBeanMapperFactoryBean">
52 66
    <property name="mappingFiles">

Also available in: Unified diff