View Javadoc
1   package fr.ifremer.dali.ui.swing.content.home.map;
2   
3   /*-
4    * #%L
5    * Dali :: UI
6    * %%
7    * Copyright (C) 2014 - 2017 Ifremer
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Affero General Public License as published by
11   * the Free Software Foundation, either version 3 of the License, or
12   * (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   *
19   * You should have received a copy of the GNU Affero General Public License
20   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21   * #L%
22   */
23  
24  import com.vividsolutions.jts.geom.Geometry;
25  import com.vividsolutions.jts.geom.Point;
26  import fr.ifremer.dali.config.DaliConfiguration;
27  import fr.ifremer.dali.dao.technical.Geometries;
28  import fr.ifremer.dali.dto.data.sampling.SamplingOperationDTO;
29  import fr.ifremer.dali.dto.data.survey.SurveyDTO;
30  import fr.ifremer.dali.dto.referential.LocationDTO;
31  import fr.ifremer.dali.map.*;
32  import fr.ifremer.dali.service.DaliTechnicalException;
33  import fr.ifremer.dali.ui.swing.util.map.MapBuilder;
34  import fr.ifremer.dali.ui.swing.util.map.layer.*;
35  import fr.ifremer.dali.ui.swing.util.map.layer.tile.MapTileLayer;
36  import fr.ifremer.dali.ui.swing.util.map.layer.wms.GraticuleWMSLayer;
37  import fr.ifremer.dali.ui.swing.util.map.layer.wms.WMSMapLayer;
38  import fr.ifremer.quadrige3.core.dao.technical.Assert;
39  import fr.ifremer.quadrige3.core.dao.technical.Dates;
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  import org.geotools.data.FileDataStore;
43  import org.geotools.data.FileDataStoreFinder;
44  import org.geotools.data.collection.CollectionFeatureSource;
45  import org.geotools.data.ows.CRSEnvelope;
46  import org.geotools.data.ows.WMSCapabilities;
47  import org.geotools.data.simple.SimpleFeatureSource;
48  import org.geotools.data.wms.WMSUtils;
49  import org.geotools.data.wmts.client.WMTSTileService;
50  import org.geotools.data.wmts.model.WMTSCapabilities;
51  import org.geotools.data.wmts.model.WMTSLayer;
52  import org.geotools.factory.CommonFactoryFinder;
53  import org.geotools.feature.DefaultFeatureCollection;
54  import org.geotools.feature.simple.SimpleFeatureBuilder;
55  import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
56  import org.geotools.geometry.jts.ReferencedEnvelope;
57  import org.geotools.map.Layer;
58  import org.geotools.map.MapContent;
59  import org.geotools.map.WMSLayer;
60  import org.geotools.ows.ServiceException;
61  import org.geotools.referencing.CRS;
62  import org.geotools.referencing.crs.DefaultGeographicCRS;
63  import org.geotools.styling.*;
64  import org.opengis.feature.simple.SimpleFeature;
65  import org.opengis.feature.simple.SimpleFeatureType;
66  import org.opengis.filter.Filter;
67  import org.opengis.filter.FilterFactory2;
68  import org.opengis.geometry.BoundingBox;
69  import org.opengis.referencing.FactoryException;
70  import org.opengis.referencing.crs.CRSAuthorityFactory;
71  import org.opengis.referencing.crs.CoordinateReferenceSystem;
72  
73  import javax.swing.UIManager;
74  import java.awt.Color;
75  import java.io.IOException;
76  import java.net.URL;
77  import java.util.*;
78  
79  import static org.nuiton.i18n.I18n.t;
80  
81  /**
82   * Surveys Map builder.
83   * Initialise map content, behaviors, etc...
84   *
85   * @author peck7 on 13/09/2017.
86   */
87  public class SurveysMapBuilder implements MapBuilder {
88  
89      private static final Log LOG = LogFactory.getLog(SurveysMapBuilder.class);
90  
91      // configuration
92      private final DaliConfiguration configuration;
93  
94      private MapMode currentMapMode;
95      private CoordinateReferenceSystem targetCRS;
96  
97      // constants
98      private static final String GEOMETRY = "geometry";
99      private static final String LABEL = "label";
100     private static final String POINT_TYPE = "pointType";
101     private static final String POINT_TYPE_START = "pointTypeStart";
102     private static final String POINT_TYPE_END = "pointTypeEnd";
103     private static final String INTERNAL = "internal";
104     private static final String LOCATION_LAYER_NAME_PREFIX = "location_";
105     private static final String SURVEY_LAYER_NAME_PREFIX = "survey_";
106     private static final String OPERATION_LAYER_NAME_PREFIX = "operation_";
107     private static final String LAYER_NAME_SUFFIX = "_Layer";
108     private static final String POINT_LAYER_NAME_SUFFIX = "_PointLayer";
109     private static final String LINE_LAYER_NAME_SUFFIX = "_LineLayer";
110 
111     // shape mode constants
112     private static final String SHAPE_FILE_EXTENSION = ".shp";
113     private static final String SHAPE_RESOURCE_DIRECTORY = "shapes";
114 
115     // wms mode constants
116 
117     // wmts mode constants
118 
119     // osm mode constants
120 
121     // builders & factories
122     private static StyleBuilder SB = new StyleBuilder();
123     private static StyleFactory SF = CommonFactoryFinder.getStyleFactory(null);
124     private static FilterFactory2 FF = CommonFactoryFinder.getFilterFactory2(null);
125 
126     // Default style, color, ...
127     private SimpleFeatureType locationFeatureType;
128     private SimpleFeatureType surveyFeatureType;
129     private SimpleFeatureType operationFeatureType;
130 
131     private static Color colorEarth = Color.decode("#8C8C8C");
132     private static Color colorWater = Color.decode("#B6EDF0");
133 
134     private static Fill fillBlack = SF.createFill(FF.literal(Color.BLACK));
135     private static Stroke strokeBlack = SF.createStroke(FF.literal(Color.BLACK), FF.literal(1.0f));
136     private static Halo haloWhite = SB.createHalo(Color.WHITE, 3);
137 
138     private static PointPlacement middleTopPlacement = SB.createPointPlacement(SB.createAnchorPoint(0.5, 0), SB.createDisplacement(0, 10), SB.literalExpression(0));
139     private static LinePlacement labelLinePlacement = SB.createLinePlacement(16);
140 
141     private Style styleLocationPoint;
142     private Style styleLocationLine;
143     private Style styleLocationPolygon;
144     private Style styleSurveyPoint;
145     private Style styleSurveyLine;
146     private Style styleSurveyLinePoints;
147     private Style styleOperationPoint;
148 
149     public SurveysMapBuilder(DaliConfiguration configuration) {
150         this.configuration = configuration;
151         init();
152     }
153 
154     public static void main(String... args) {
155         // TEST
156         SurveysMapBuilder mapBuilder = new SurveysMapBuilder(null);
157         mapBuilder.buildNewMapContent(MapMode.EMBEDDED_SHAPE_MAP_MODE);
158     }
159 
160     private void init() {
161 
162         // Initialize types
163         {
164             // - location
165             SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
166             typeBuilder.setName("locationFeatureType");
167             typeBuilder.setCRS(DefaultGeographicCRS.WGS84);
168             typeBuilder.setDefaultGeometry(GEOMETRY);
169             typeBuilder.add(GEOMETRY, Geometry.class);
170             typeBuilder.add(LABEL, String.class);
171             locationFeatureType = typeBuilder.buildFeatureType();
172         }
173         {
174             // - survey
175             SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
176             typeBuilder.setName("surveyFeatureType");
177             typeBuilder.setCRS(DefaultGeographicCRS.WGS84);
178             typeBuilder.setDefaultGeometry(GEOMETRY);
179             typeBuilder.add(GEOMETRY, Geometry.class);
180             typeBuilder.add(LABEL, String.class);
181             typeBuilder.add(POINT_TYPE, String.class);
182             surveyFeatureType = typeBuilder.buildFeatureType();
183         }
184         {
185             // - operation
186             SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
187             typeBuilder.setName("operationFeatureType");
188             typeBuilder.setCRS(DefaultGeographicCRS.WGS84);
189             typeBuilder.setDefaultGeometry(GEOMETRY);
190             typeBuilder.add(GEOMETRY, Geometry.class);
191             typeBuilder.add(LABEL, String.class);
192             operationFeatureType = typeBuilder.buildFeatureType();
193         }
194 
195         // Initialize font
196         java.awt.Font uiFont = UIManager.getFont("Label.font");
197         Font font;
198         if (uiFont != null) {
199             font = SF.createFont(FF.literal(uiFont.getFamily()), FF.literal("normal"), FF.literal("bold"), FF.literal(uiFont.getSize()));
200         } else {
201             font = SF.getDefaultFont();
202             font.setSize(FF.literal(14));
203         }
204 
205         // Initialize graphic styles
206         {
207             // location style
208             Color color = Color.BLUE;
209             Stroke stroke = SF.createStroke(FF.literal(color), FF.literal(1.0f));
210             Fill fill = SF.createFill(FF.literal(color.brighter().brighter()), FF.literal(.5f));
211 
212             Mark mark = SF.createMark(FF.literal(StyleBuilder.MARK_CIRCLE), stroke, fill, null, null);
213             Graphic graphic = SF.createDefaultGraphic();
214             graphic.graphicalSymbols().clear();
215             graphic.graphicalSymbols().add(mark);
216             graphic.setSize(FF.literal(10));
217 
218             // location Point style
219             TextSymbolizer textPointSymbolizer = SF.createTextSymbolizer(fillBlack, new Font[]{font}, haloWhite, FF.property(LABEL), middleTopPlacement, null);
220             textPointSymbolizer.setPriority(FF.literal(1));
221             styleLocationPoint = createStyle(
222                     "styleLocationPoint",
223                     createPointSymbolizer("symbolizerLocationPoint", graphic),
224                     textPointSymbolizer);
225 
226             // location line style
227             TextSymbolizer textLineSymbolizer = SF.createTextSymbolizer(fillBlack, new Font[]{font}, haloWhite, FF.property(LABEL), labelLinePlacement, null);
228             textLineSymbolizer.setPriority(FF.literal(1));
229             // Invert both symbolizer to conserve text rendering on too small line (Mantis #51902)
230             styleLocationLine = createStyle(
231                     "styleLocationLine",
232                 textLineSymbolizer,
233                 createLineSymbolizer("symbolizerLocationLine", stroke));
234 
235             // location Polygon style
236             TextSymbolizer textPolygonSymbolizer = SF.createTextSymbolizer(fillBlack, new Font[]{font}, haloWhite, FF.property(LABEL), middleTopPlacement, null);
237             textPolygonSymbolizer.setPriority(FF.literal(1));
238             textPolygonSymbolizer.getOptions().put(TextSymbolizer.GOODNESS_OF_FIT_KEY, String.valueOf(0.0d));
239             styleLocationPolygon = createStyle(
240                     "styleLocationPolygon",
241                     createPolygonSymbolizer("symbolizerLocationPolygon", stroke, fill),
242                     textPolygonSymbolizer);
243 
244         }
245 
246         {
247             // survey style
248             Color colorLine = Color.GREEN;
249             Color colorPoint = Color.BLUE;
250             Color colorStart = Color.GREEN;
251             Color colorEnd = Color.RED;
252             Stroke stroke = SF.createStroke(FF.literal(colorLine), FF.literal(3));
253             Fill fillPoint = SF.createFill(FF.literal(colorPoint), FF.literal(1.0f));
254             Fill fillStart = SF.createFill(FF.literal(colorStart), FF.literal(1.0f));
255             Fill fillEnd = SF.createFill(FF.literal(colorEnd), FF.literal(1.0f));
256 
257             // survey Line style
258             TextSymbolizer textSymbolizer = SF.createTextSymbolizer(fillBlack, new Font[]{font}, haloWhite, FF.property(LABEL), labelLinePlacement, null);
259             textSymbolizer.setPriority(FF.literal(2));
260             styleSurveyLine = createStyle(
261                     "styleSurveyLine",
262                     createLineSymbolizer("symbolizerSurveyLine", stroke),
263                     textSymbolizer);
264 
265             // survey Line Points style
266 
267             // start point of the line
268             Mark markStart = SF.createMark(FF.literal(StyleBuilder.MARK_TRIANGLE), strokeBlack, fillStart, null, null);
269             Graphic graphicStart = SF.createDefaultGraphic();
270             graphicStart.graphicalSymbols().clear();
271             graphicStart.graphicalSymbols().add(markStart);
272             graphicStart.setSize(FF.literal(10));
273             Symbolizer pointSymbolizerStart = createPointSymbolizer("symbolizerSurveyPointStart", graphicStart);
274 
275             // end point of the line
276             Mark markEnd = SF.createMark(FF.literal(StyleBuilder.MARK_X), strokeBlack, fillEnd, null, null);
277             Graphic graphicEnd = SF.createDefaultGraphic();
278             graphicEnd.graphicalSymbols().clear();
279             graphicEnd.graphicalSymbols().add(markEnd);
280             graphicEnd.setSize(FF.literal(10));
281             Symbolizer pointSymbolizerEnd = createPointSymbolizer("symbolizerSurveyPointEnd", graphicEnd);
282 
283             // Create start/end rule
284             Rule ruleStart = SB.createRule(pointSymbolizerStart);
285             Filter filterStart = SB.getFilterFactory().equals(FF.property(POINT_TYPE), FF.literal(POINT_TYPE_START));
286             ruleStart.setFilter(filterStart);
287 
288             Rule ruleEnd = SB.createRule(pointSymbolizerEnd);
289             Filter filterEnd = SB.getFilterFactory().equals(FF.property(POINT_TYPE), FF.literal(POINT_TYPE_END));
290             ruleEnd.setFilter(filterEnd);
291 
292             // Create the style
293             styleSurveyLinePoints = createStyle("styleSurveyLinePoints", ruleStart, ruleEnd);
294 
295             // survey Point only style
296             TextSymbolizer textPointSymbolizer = SF.createTextSymbolizer(fillBlack, new Font[]{font}, haloWhite, FF.property(LABEL), middleTopPlacement, null);
297             textPointSymbolizer.setPriority(FF.literal(2));
298             Mark markPoint = SF.createMark(FF.literal(StyleBuilder.MARK_TRIANGLE), strokeBlack, fillPoint, null, null);
299             Graphic graphicPoint = SF.createDefaultGraphic();
300             graphicPoint.graphicalSymbols().clear();
301             graphicPoint.graphicalSymbols().add(markPoint);
302             graphicPoint.setSize(FF.literal(10));
303 
304             styleSurveyPoint = createStyle("styleSurveyPoint", createPointSymbolizer("symbolizerSurveyPoint", graphicPoint), textPointSymbolizer);
305         }
306 
307         {
308             // operation Point style
309             Color color = Color.YELLOW;
310             Fill fill = SF.createFill(FF.literal(color.brighter().brighter()), FF.literal(1.0f));
311 
312             Mark mark = SF.createMark(FF.literal(StyleBuilder.MARK_SQUARE), strokeBlack, fill, null, null);
313             Graphic graphic = SF.createDefaultGraphic();
314             graphic.graphicalSymbols().clear();
315             graphic.graphicalSymbols().add(mark);
316             graphic.setSize(FF.literal(10));
317 
318             TextSymbolizer textSymbolizer = SF.createTextSymbolizer(fillBlack, new Font[]{font}, haloWhite, FF.property(LABEL), middleTopPlacement, null);
319             textSymbolizer.setPriority(FF.literal(3));
320             styleOperationPoint = createStyle("styleOperationPoint", createPointSymbolizer("symbolizerOperationPoint", graphic), textSymbolizer);
321 
322             // no operation Line style
323         }
324     }
325 
326     @Override
327     public MapContent buildNewMapContent(MapMode preferredMode) {
328 
329         MapContent mapContent = new MapContent();
330 
331         try {
332 
333             currentMapMode = Optional.ofNullable(preferredMode).orElse(configuration.getPreferredMapMode());
334             if (currentMapMode == null) {
335                 throw new DaliTechnicalException("Unable to determine map mode");
336             }
337 
338             MapConfiguration mapConfiguration = currentMapMode.getMapConfiguration();
339 
340             if (mapConfiguration.isOnline()) {
341 
342                 // if server not reachable, try another one
343                 if (!Maps.isMapServerReachable(currentMapMode)) return buildNewMapContent(getFirstOfflineMode());
344 
345                 if (mapConfiguration instanceof WMSMapConfiguration) {
346 
347                     // Call server capabilities and get layers
348                     WMSCapabilities wmsCapabilities = ((WMSMapConfiguration) mapConfiguration).getCapabilities();
349                     org.geotools.data.ows.Layer[] layers = WMSUtils.getNamedLayers(wmsCapabilities);
350 
351                     // Create layers
352                     for (MapLayerDefinition layerDefinition : currentMapMode.getMapLayerDefinitions()) {
353                         for (org.geotools.data.ows.Layer layer : layers) {
354                             if (layerDefinition.getName().equals(layer.getName())) {
355 
356                                 WMSLayer wmsLayer;
357                                 if (layerDefinition instanceof GraticuleLayerDefinition) {
358                                     wmsLayer = new GraticuleWMSLayer(((WMSMapConfiguration) mapConfiguration).getWebService(), layer);
359                                 } else {
360                                     wmsLayer = new WMSMapLayer(((WMSMapConfiguration) mapConfiguration).getWebService(), layer);
361                                 }
362                                 wmsLayer.setTitle(layer.getName());
363 
364                                 mapContent.addLayer(wmsLayer);
365                             }
366                         }
367                     }
368                     mapContent.addLayer(new ScaleLayer());
369 
370                 } else if (mapConfiguration instanceof WMTSMapConfiguration) {
371 
372                     WMTSCapabilities wmtsCapabilities = ((WMTSMapConfiguration) mapConfiguration).getCapabilities();
373                     WMTSLayer layer = wmtsCapabilities.getLayer(currentMapMode.getBaseLayerName());
374 
375                     // Restrict bbox
376                     // BoundingBoxes doit est <= à LatLonBoundingBox pour que le rendu passe en zoom out en dehors des limites
377                     CRSEnvelope limitEnvelope = new CRSEnvelope(configuration.getMapProjection().getEnvelope());
378                     layer.setBoundingBoxes(limitEnvelope);
379                     layer.setLatLonBoundingBox(limitEnvelope);
380 
381                     WMTSTileService service = new WMTSTileService(
382                             currentMapMode.getUrl(),
383                             wmtsCapabilities.getType(),
384                             layer,
385                             "",
386                             wmtsCapabilities.getMatrixSet(configuration.getMapProjection().getCode()));
387                     // Si on utilise le jeu de tuile EPSG:4326 (WGS84 standard) ça déconne grave sur certaine partie de la carte,
388                     // alors que le EPSG:3857 (WGS84 pseudo mercator) ça passe bien.
389 
390                     mapContent.addLayer(new MapTileLayer(service, currentMapMode.getBaseLayerName()));
391 
392                     // add direct graticule because wmts don't have it
393                     mapContent.addLayer(new GraticuleDirectLayer());
394                     mapContent.addLayer(new ScaleLayer());
395 
396                 } else if (mapConfiguration instanceof OSMBasedMapConfiguration) {
397 
398                     // OpenStreetMap
399                     mapContent.addLayer(new MapTileLayer(((OSMBasedMapConfiguration) mapConfiguration).getTileService(), currentMapMode.getBaseLayerName()));
400 
401                     // add direct graticule because wmts don't have it
402                     mapContent.addLayer(new GraticuleDirectLayer());
403                     mapContent.addLayer(new ScaleLayer());
404 
405                 }
406 
407             } else {
408 
409                 // Create layers
410                 for (MapLayerDefinition layerDefinition : currentMapMode.getMapLayerDefinitions()) {
411                     Layer layer = newMapLayer(layerDefinition.getName(), colorEarth.darker(), 2, colorEarth, 1f);
412                     mapContent.addLayer(layer);
413                 }
414 
415                 // Graticule and scale
416                 mapContent.addLayer(new GraticuleDirectLayer());
417                 mapContent.addLayer(new ScaleLayer());
418 
419             }
420 
421             // Apply view CRS
422             CRSAuthorityFactory factory = CRS.getAuthorityFactory(true);
423             MapProjection targetProjection = configuration.getMapProjection();
424 
425             // FIXME : for Sextant WMS, the PSEUDO MERCATOR fails to project
426 //            if (currentMapMode == MapMode.SEXTANT_WMS_MAP_MODE)
427 //                targetProjection = MapProjection.WGS84;
428 
429             targetCRS = factory.createCoordinateReferenceSystem(targetProjection.getCode());
430             mapContent.getViewport().setCoordinateReferenceSystem(targetCRS);
431 
432         } catch (IOException | ServiceException | FactoryException e) {
433             throw new DaliTechnicalException(e);
434         }
435 
436         return mapContent;
437     }
438 
439     @Override
440     public CoordinateReferenceSystem getTargetCRS() {
441         return targetCRS;
442     }
443 
444     @Override
445     public MapMode getCurrentMapMode() {
446         if (currentMapMode == null) currentMapMode = configuration.getPreferredMapMode();
447         return currentMapMode;
448     }
449 
450     @Override
451     public boolean checkOnlineReachable() {
452         return Maps.isMapServerReachable(getCurrentMapMode())
453                 || Maps.isMapServerReachable(configuration.getPreferredMapMode());
454     }
455 
456     @Override
457     public Color getBackgroundColor() {
458         return colorWater;
459     }
460 
461     private MapMode getFirstOfflineMode() {
462         return Arrays.stream(MapMode.values()).filter(mapMode -> !mapMode.isOnline()).findFirst().orElse(null);
463     }
464 
465     private Layer newMapLayer(String shapeName, Color outlineColor, int outlineWidth, Color fillColor, float fillOpacity) throws IOException {
466 
467         FileDataStore store = FileDataStoreFinder.getDataStore(getShapeURL(shapeName));
468         SimpleFeatureSource source = store.getFeatureSource();
469 
470         Stroke stroke = SF.createStroke(FF.literal(outlineColor), FF.literal(outlineWidth));
471         Fill fill = Fill.NULL;
472         if (fillColor != null) {
473             fill = SF.createFill(FF.literal(fillColor), FF.literal(fillOpacity));
474         }
475 
476         Style style = createStyle("styleMapPolygon", createPolygonSymbolizer("symbolizerMapPolygon", stroke, fill));
477         return new MapFeatureLayer(source, style, shapeName);
478 
479     }
480 
481     private URL getShapeURL(String shapeName) {
482 
483         Assert.notNull(shapeName);
484         if (!shapeName.endsWith(SHAPE_FILE_EXTENSION)) shapeName += SHAPE_FILE_EXTENSION;
485 
486         // Find resource
487         URL shapeUrl = SurveysMapUIHandler.class.getResource(String.format("/%s/%s", SHAPE_RESOURCE_DIRECTORY, shapeName));
488         if (shapeUrl == null) {
489             throw new DaliTechnicalException(shapeName + " not found");
490         }
491         return shapeUrl;
492     }
493 
494     /**
495      * Build the data layer collection from a survey
496      *
497      * @param dataObject the data object (the survey)
498      * @return the collection of data layer
499      */
500     @Override
501     public DataLayerCollection buildDataLayerCollection(Object dataObject) {
502 
503         Assert.notNull(dataObject);
504         Assert.isInstanceOf(SurveyDTO.class, dataObject);
505 
506         SurveyDTO survey = (SurveyDTO) dataObject;
507 
508         SurveyLayerCollection dataLayerCollection = new SurveyLayerCollection();
509 
510         // Location layer
511         LocationDTO location = survey.getLocation();
512         if (Geometries.isValid(location.getCoordinate())) {
513 
514             Geometry geometry = Geometries.getGeometry(location.getCoordinate().getWkt()); // Mantis #40640 : use initial WKT geometry
515             SimpleFeatureBuilder builder = new SimpleFeatureBuilder(locationFeatureType);
516             builder.add(geometry);
517             builder.add(location.getName());
518             SimpleFeature feature = builder.buildFeature(location.getId().toString());
519             DefaultFeatureCollection collection = new DefaultFeatureCollection(INTERNAL, locationFeatureType);
520             collection.add(feature);
521             SimpleFeatureSource source = new CollectionFeatureSource(collection);
522 
523             int dimension = geometry.getDimension();
524             Style style = dimension == 2 ? styleLocationPolygon : dimension == 1 ? styleLocationLine : styleLocationPoint;
525 
526             dataLayerCollection.setLocationLayer(
527                     new DataFeatureLayer(dataLayerCollection, location.getId(), source, style, LOCATION_LAYER_NAME_PREFIX + location.getId() + LAYER_NAME_SUFFIX));
528         }
529 
530         // survey layer
531         if (Geometries.isValid(survey.getCoordinate())) {
532 
533             DataFeatureLayer layerLine = null;
534             DataFeatureLayer layerPoint;
535 
536             if (Geometries.isPoint(survey.getCoordinate())) {
537                 // Only a point
538                 Point pointStart = Geometries.createPoint(survey.getCoordinate().getMinLongitude(), survey.getCoordinate().getMinLatitude());
539                 DefaultFeatureCollection collection = new DefaultFeatureCollection(INTERNAL, surveyFeatureType);
540                 SimpleFeatureBuilder builder = new SimpleFeatureBuilder(surveyFeatureType);
541                 builder.add(pointStart);
542                 builder.add(getSurveyGeometryLabel(survey));
543                 builder.add(POINT_TYPE_START);
544                 collection.add(builder.buildFeature(POINT_TYPE_START + "_" + survey.getId().toString()));
545                 SimpleFeatureSource source = new CollectionFeatureSource(collection);
546                 layerPoint = new DataFeatureLayer(dataLayerCollection, survey.getId(), source, styleSurveyPoint, SURVEY_LAYER_NAME_PREFIX + survey.getId().toString() + POINT_LAYER_NAME_SUFFIX);
547 
548             } else {
549                 {
550                     // survey line
551                     Geometry geometry = Geometries.getGeometry(survey.getCoordinate());
552                     DefaultFeatureCollection collection = new DefaultFeatureCollection(INTERNAL, surveyFeatureType);
553                     SimpleFeatureBuilder builder = new SimpleFeatureBuilder(surveyFeatureType);
554                     builder.add(geometry);
555                     builder.add(getSurveyGeometryLabel(survey));
556                     collection.add(builder.buildFeature(survey.getId().toString()));
557                     SimpleFeatureSource source = new CollectionFeatureSource(collection);
558                     layerLine = new DataFeatureLayer(dataLayerCollection, survey.getId(), source, styleSurveyLine, SURVEY_LAYER_NAME_PREFIX + survey.getId().toString() + LINE_LAYER_NAME_SUFFIX);
559                 }
560                 {
561                     // survey points
562                     Point pointStart = Geometries.createPoint(survey.getCoordinate().getMinLongitude(), survey.getCoordinate().getMinLatitude());
563                     Point pointEnd = Geometries.createPoint(survey.getCoordinate().getMaxLongitude(), survey.getCoordinate().getMaxLatitude());
564                     DefaultFeatureCollection collection = new DefaultFeatureCollection(INTERNAL, surveyFeatureType);
565                     SimpleFeatureBuilder builder = new SimpleFeatureBuilder(surveyFeatureType);
566                     builder.add(pointStart);
567                     builder.add(null);
568                     builder.add(POINT_TYPE_START);
569                     collection.add(builder.buildFeature(POINT_TYPE_START + "_" + survey.getId().toString()));
570                     builder = new SimpleFeatureBuilder(surveyFeatureType);
571                     builder.add(pointEnd);
572                     builder.add(null);
573                     builder.add(POINT_TYPE_END);
574                     collection.add(builder.buildFeature(POINT_TYPE_END + "_" + survey.getId().toString()));
575                     SimpleFeatureSource source = new CollectionFeatureSource(collection);
576                     layerPoint = new DataFeatureLayer(dataLayerCollection, survey.getId(), source, styleSurveyLinePoints, SURVEY_LAYER_NAME_PREFIX + survey.getId().toString() + POINT_LAYER_NAME_SUFFIX);
577                 }
578             }
579 
580             dataLayerCollection.setSurveyLayer(layerLine, layerPoint);
581 
582         }
583 
584         // operations layer
585         if (!survey.isSamplingOperationsEmpty()) {
586             for (SamplingOperationDTO operation : survey.getSamplingOperations()) {
587 
588                 if (Geometries.isValid(operation.getCoordinate())) {
589 
590                     Point point = Geometries.createPoint(operation.getCoordinate().getMinLongitude(), operation.getCoordinate().getMinLatitude());
591                     DefaultFeatureCollection collection = new DefaultFeatureCollection(INTERNAL, operationFeatureType);
592                     SimpleFeatureBuilder builder = new SimpleFeatureBuilder(operationFeatureType);
593                     builder.add(point);
594                     builder.add(getOperationGeometryLabel(operation));
595                     collection.add(builder.buildFeature(operation.getId().toString()));
596                     SimpleFeatureSource source = new CollectionFeatureSource(collection);
597 
598                     dataLayerCollection.addOperationLayer(
599                             null,
600                             new DataFeatureLayer(dataLayerCollection, operation.getId(), source, styleOperationPoint, OPERATION_LAYER_NAME_PREFIX + operation.getId().toString() + POINT_LAYER_NAME_SUFFIX));
601                 }
602             }
603         }
604 
605         return dataLayerCollection;
606     }
607 
608     private String getSurveyGeometryLabel(SurveyDTO survey) {
609         return survey.getName() + " - " + Dates.formatDate(survey.getDate(), configuration.getDateFormat());
610     }
611 
612     private String getOperationGeometryLabel(SamplingOperationDTO operation) {
613         return operation.getName();
614     }
615 
616     private Symbolizer createPolygonSymbolizer(String name, Stroke stroke, Fill fill) {
617         return createNamedSymbolizer(name, SF.createPolygonSymbolizer(stroke, fill, null));
618     }
619 
620     private Symbolizer createLineSymbolizer(String name, Stroke stroke) {
621         return createNamedSymbolizer(name, SF.createLineSymbolizer(stroke, null));
622     }
623 
624     private Symbolizer createPointSymbolizer(String name, Graphic graphic) {
625         return createNamedSymbolizer(name, SF.createPointSymbolizer(graphic, null));
626     }
627 
628     private Symbolizer createNamedSymbolizer(String name, Symbolizer symbolizer) {
629         symbolizer.setName(name);
630         String title = "dali.home.map.legend." + name;
631         if (symbolizer.getDescription() == null) symbolizer.setDescription(new DescriptionImpl());
632         symbolizer.getDescription().setTitle(t(title));
633         return symbolizer;
634     }
635 
636     private Style createStyle(String name, Symbolizer... symbolizers) {
637 
638         Rule rule = SF.createRule();
639 
640         if (symbolizers != null) {
641             for (Symbolizer sym : symbolizers) {
642                 rule.symbolizers().add(sym);
643             }
644         }
645 
646         return createStyle(name, rule);
647     }
648 
649     private Style createStyle(String name, Rule... rules) {
650         Style style = SF.createStyle();
651         style.featureTypeStyles().add(SF.createFeatureTypeStyle(rules));
652         style.setName(name);
653         return style;
654     }
655 
656     /**
657      * Get the name of displayable layers.
658      * Use the reverse order of layer name to bring the finest map first.
659      * The visibility is defined by the view envelope from the MapLayerDefinition
660      *
661      * @param displayArea the display area
662      * @return list of displayable layers
663      */
664     @Override
665     public List<String> getDisplayableLayerNames(ReferencedEnvelope displayArea) {
666 
667         List<String> layerNames = new ArrayList<>();
668         String defaultLayerName = null;
669         boolean addDefaultLayer = true;
670         boolean allowAddLayer = true;
671         // Get layer definitions depending on map type
672         List<MapLayerDefinition> layers = new ArrayList<>(currentMapMode.getMapLayerDefinitions());
673 
674         displayArea = Maps.transformReferencedEnvelope(displayArea, DefaultGeographicCRS.WGS84);
675 
676         // Reverse the list to begin with the finest layers
677         Collections.reverse(layers);
678         for (MapLayerDefinition layerDefinition : layers) {
679 
680             // Detect the default layer
681             if (layerDefinition.isDefaultLayer()) {
682                 defaultLayerName = layerDefinition.getName();
683             } else {
684 
685                 // Add all global layer
686                 if (layerDefinition.isGlobalLayer()) {
687                     layerNames.add(layerDefinition.getName());
688                 }
689 
690                 // Or check if the display area is entirely inside the view envelope
691                 else if (allowAddLayer && layerDefinition.getViewEnvelope().contains((BoundingBox) displayArea)) {
692 
693                     // Add the layer to display
694                     layerNames.add(layerDefinition.getName());
695 
696                     // If the layer don't allow overlap, Stop adding layer
697                     allowAddLayer = layerDefinition.isAllowOverlap();
698 
699                     // The default is not attempt to be display now
700                     addDefaultLayer = false;
701                 }
702             }
703         }
704         // add default layer if no other layer displayable
705         if (addDefaultLayer && defaultLayerName != null) {
706             layerNames.add(defaultLayerName);
707         }
708         return layerNames;
709     }
710 
711     public static boolean isLocationLayer(DataFeatureLayer layer) {
712         return layer != null && layer.getTitle() != null && layer.getTitle().startsWith(LOCATION_LAYER_NAME_PREFIX);
713     }
714 
715     public static boolean isSurveyLayer(DataFeatureLayer layer) {
716         return layer != null && layer.getTitle() != null && layer.getTitle().startsWith(SURVEY_LAYER_NAME_PREFIX);
717     }
718 
719     public static boolean isOperationLayer(DataFeatureLayer layer) {
720         return layer != null && layer.getTitle() != null && layer.getTitle().startsWith(OPERATION_LAYER_NAME_PREFIX);
721     }
722 }