1 package fr.ifremer.dali.ui.swing.content.home.map;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 import com.google.common.collect.Lists;
27 import com.google.common.collect.Sets;
28 import com.vividsolutions.jts.geom.Envelope;
29 import fr.ifremer.dali.decorator.DecoratorService;
30 import fr.ifremer.dali.dto.DaliBeans;
31 import fr.ifremer.dali.dto.data.sampling.SamplingOperationDTO;
32 import fr.ifremer.dali.dto.data.survey.SurveyDTO;
33 import fr.ifremer.dali.map.MapMode;
34 import fr.ifremer.dali.map.Maps;
35 import fr.ifremer.dali.service.DaliTechnicalException;
36 import fr.ifremer.dali.ui.swing.util.AbstractDaliUIHandler;
37 import fr.ifremer.dali.ui.swing.util.map.DataMapPane;
38 import fr.ifremer.dali.ui.swing.util.map.DataSelectionAdapter;
39 import fr.ifremer.dali.ui.swing.util.map.DataSelectionEvent;
40 import fr.ifremer.dali.ui.swing.util.map.layer.DataFeatureLayer;
41 import fr.ifremer.dali.ui.swing.util.map.layer.DataLayerCollection;
42 import fr.ifremer.dali.ui.swing.util.map.layer.InfoPanelLayer;
43 import fr.ifremer.dali.ui.swing.util.map.layer.LegendLayer;
44 import fr.ifremer.dali.ui.swing.util.table.AbstractDaliTableUIModel;
45 import fr.ifremer.quadrige3.core.dao.technical.Dates;
46 import fr.ifremer.quadrige3.ui.swing.action.ActionFactory;
47 import jaxx.runtime.SwingUtil;
48 import org.apache.commons.collections4.CollectionUtils;
49 import org.apache.commons.logging.Log;
50 import org.apache.commons.logging.LogFactory;
51 import org.geotools.geometry.DirectPosition2D;
52 import org.geotools.geometry.jts.ReferencedEnvelope;
53 import org.geotools.map.Layer;
54 import org.geotools.map.MapContent;
55 import org.geotools.referencing.crs.DefaultGeographicCRS;
56 import org.geotools.referencing.operation.projection.ProjectionException;
57 import org.geotools.renderer.RenderListener;
58 import org.geotools.swing.event.MapMouseAdapter;
59 import org.geotools.swing.event.MapMouseEvent;
60 import org.geotools.swing.event.MapPaneAdapter;
61 import org.geotools.swing.event.MapPaneEvent;
62 import org.nuiton.jaxx.application.swing.util.Cancelable;
63 import org.opengis.feature.simple.SimpleFeature;
64
65 import javax.swing.SwingWorker;
66 import java.beans.PropertyChangeListener;
67 import java.util.Collection;
68 import java.util.List;
69 import java.util.Optional;
70 import java.util.Set;
71 import java.util.concurrent.ExecutionException;
72 import java.util.concurrent.ThreadPoolExecutor;
73
74 import static org.nuiton.i18n.I18n.t;
75
76
77
78
79 public class SurveysMapUIHandler extends AbstractDaliUIHandler<SurveysMapUIModel, SurveysMapUI> implements Cancelable {
80
81
82
83
84 private static final Log LOG = LogFactory.getLog(SurveysMapUIHandler.class);
85 private ThreadPoolExecutor executor;
86 private PropertyChangeListener mapListener;
87
88
89
90
91 @Override
92 public void beforeInit(final SurveysMapUI ui) {
93 super.beforeInit(ui);
94
95
96 final SurveysMapUIModel model = new SurveysMapUIModel();
97 ui.setContextValue(model);
98 }
99
100
101
102
103 @Override
104 public void afterInit(SurveysMapUI ui) {
105
106 initUI(ui);
107
108
109
110 SurveysMapBuilder mapBuilder = new SurveysMapBuilder(getConfig());
111 ui.getMapPane().setMapBuilder(mapBuilder);
112 boolean checkOnline = mapBuilder.checkOnlineReachable();
113 ui.getOfflineButton().setEnabled(checkOnline);
114 ui.getOfflineButton().setSelected(!checkOnline);
115
116 SwingUtil.setLayerUI(ui.getMapPane(), ui.getMapBlockLayer());
117
118 initListeners();
119 }
120
121
122 private ThreadPoolExecutor getExecutor() {
123 if (executor == null || executor.isShutdown())
124 executor = ActionFactory.createSingleThreadExecutor(ActionFactory.ExecutionMode.LATEST);
125 return executor;
126 }
127
128 private void shutdownExecutor() {
129 if (executor != null)
130 executor.shutdown();
131 }
132
133 @Override
134 public void onCloseUI() {
135
136
137 shutdownExecutor();
138 getUI().getMapPane().clearMapContent();
139 super.onCloseUI();
140 }
141
142
143
144
145 private void initListeners() {
146
147 getUI().getMapPane().addMapPaneListener(new MapPaneAdapter() {
148
149 @Override
150 public void onRenderingStarted(MapPaneEvent ev) {
151 getUI().getLoadingIndicator().setVisible(true);
152 }
153
154 @Override
155 public void onRenderingStopped(MapPaneEvent ev) {
156 getModel().setLoading(false);
157 getUI().getLoadingIndicator().setVisible(false);
158 }
159
160 @Override
161 public void onDisplayAreaChanged(MapPaneEvent ev) {
162 ReferencedEnvelope envelope = (ReferencedEnvelope) ev.getData();
163 checkVisibleEnvelope(Maps.transformReferencedEnvelope(envelope, DefaultGeographicCRS.WGS84));
164
165
166
167
168
169 }
170 });
171
172 getUI().getMapPane().addMouseListener(new MapMouseAdapter() {
173 @Override
174 public void onMouseEntered(MapMouseEvent ev) {
175 displayCoordinates(ev.getWorldPos());
176 }
177
178 @Override
179 public void onMouseExited(MapMouseEvent ev) {
180 displayCoordinates(null);
181 }
182
183 @Override
184 public void onMouseMoved(MapMouseEvent ev) {
185 displayCoordinates(ev.getWorldPos());
186 }
187 });
188
189 getUI().getMapPane().addDataSelectionListener(new DataSelectionAdapter() {
190
191 boolean previouslySelected = false;
192
193 @Override
194 public void onEmptySelection(DataSelectionEvent event) {
195 if (previouslySelected) {
196
197 InfoPanelLayer infoPanelLayer = event.getSource().getInfoPanelLayer();
198 if (infoPanelLayer != null) {
199 infoPanelLayer.clear();
200 event.getSource().redrawLayers();
201 }
202 previouslySelected = false;
203 }
204 }
205
206 @Override
207 public void onDataLayerSelected(DataSelectionEvent event) {
208
209 InfoPanelLayer infoPanelLayer = event.getSource().getInfoPanelLayer();
210 if (infoPanelLayer == null) {
211
212 return;
213 }
214
215
216 Optional<DataFeatureLayer> layerOptional = event.getSelectedDataLayers().stream().filter(
217 layer -> SurveysMapBuilder.isSurveyLayer(layer) || SurveysMapBuilder.isOperationLayer(layer)).findFirst();
218 if (!layerOptional.isPresent()) {
219 infoPanelLayer.clear();
220 event.getSource().redrawLayers();
221 return;
222 }
223 DataFeatureLayer selectedLayer = layerOptional.get();
224
225 List<String> texts = Lists.newArrayList();
226
227 boolean isSurvey = SurveysMapBuilder.isSurveyLayer(selectedLayer);
228 boolean isOperation = SurveysMapBuilder.isOperationLayer(selectedLayer);
229
230 if (isSurvey || isOperation) {
231
232 Integer surveyId = isSurvey ? selectedLayer.getId() : ((SurveyLayerCollection) selectedLayer.getDataLayerCollection()).getSurveyId();
233 if (surveyId != null) {
234 SurveyDTO survey = DaliBeans.findById(getModel().getSelectedSurveys(), surveyId);
235 if (survey != null) {
236
237
238 texts.add(String.format("%s: %s", t("dali.property.program"), decorate(survey.getProgram())));
239 texts.add(String.format("%s: %s", t("dali.property.location"), decorate(survey.getLocation())));
240 texts.add(String.format("%s: %s", t("dali.property.date"), Dates.formatDate(survey.getDate(), getConfig().getDateFormat())));
241 texts.add(String.format("%s: %s", t("dali.property.mnemonic"), survey.getName()));
242
243 if (isOperation) {
244 Integer operationId = selectedLayer.getId();
245 if (operationId != null) {
246 SamplingOperationDTO operation = DaliBeans.findById(survey.getSamplingOperations(), operationId);
247 if (operation != null) {
248
249
250 texts.add(String.format("%s: %s", t("dali.property.samplingOperation"), decorate(operation, DecoratorService.CONCAT)));
251
252 } else {
253 if (LOG.isWarnEnabled()) {
254 LOG.warn(String.format("The operation with id=%s should be found in survey's operations list", operationId));
255 return;
256 }
257 }
258
259 } else {
260
261 }
262 }
263
264 } else {
265 if (LOG.isWarnEnabled()) {
266 LOG.warn(String.format("The survey with id=%s should be found in table", surveyId));
267 return;
268 }
269 }
270
271 } else {
272
273 }
274
275 } else {
276
277
278 }
279
280
281
282
283
284
285 if (CollectionUtils.isNotEmpty(texts)) {
286
287 infoPanelLayer.setInfo(event.getScreenPoint(), texts);
288 event.getSource().redrawLayers();
289 previouslySelected = true;
290 }
291 }
292 });
293
294 getUI().getMapPane().getRenderer().addRenderListener(new RenderListener() {
295 @Override
296 public void featureRenderer(SimpleFeature feature) {
297
298 }
299
300 @Override
301 public void errorOccurred(Exception e) {
302 if (e.getCause() instanceof ProjectionException)
303 getUI().getMapPane().fixFullExtend();
304 }
305 });
306 }
307
308 private void displayCoordinates(DirectPosition2D p) {
309 DirectPosition2D position = Maps.transformDirectPosition(p, DefaultGeographicCRS.WGS84);
310 getUI().getMapPositionLabel().setText(position != null ? String.format("[ %.4f, %.4f ]", position.getX(), position.getY()) : "[ ]");
311 }
312
313 private void checkVisibleEnvelope(ReferencedEnvelope visibleEnvelope) {
314
315
316 ReferencedEnvelope overallEnvelope = getUI().getMapPane().getOverallEnvelope();
317 if (!overallEnvelope.isEmpty() && !visibleEnvelope.contains((Envelope) overallEnvelope)) {
318 getUI().getInfoLabel().setText(t("dali.home.map.warning.dataOutsideEnvelope"));
319 } else {
320 getUI().getInfoLabel().setText(null);
321 }
322 }
323
324 public void openMap() {
325
326 if (getModel().getSelectionModel() != null) {
327 getModel().getSelectionModel().addPropertyChangeListener(AbstractDaliTableUIModel.PROPERTY_SELECTED_ROWS, getMapListener());
328 }
329 buildMapPositions();
330 displayCoordinates(null);
331 }
332
333 public void closeMap() {
334
335 getUI().getMapPane().clearMapContent();
336
337 if (getModel().getSelectionModel() != null) {
338 getModel().getSelectionModel().removePropertyChangeListener(AbstractDaliTableUIModel.PROPERTY_SELECTED_ROWS, getMapListener());
339 }
340 }
341
342 private PropertyChangeListener getMapListener() {
343 if (mapListener == null) {
344 mapListener = evt -> SurveysMapUIHandler.this.buildMapPositions();
345 }
346 return mapListener;
347 }
348
349 public void buildMapPositions() {
350
351
352
353
354
355 if (!getModel().isBuildMapOnSelectionChanged()) return;
356
357 Collection<? extends SurveyDTO> selectedSurveys = getModel().getSelectedSurveys();
358
359 if (selectedSurveys.size() > getConfig().getMaxSurveyInMap()) {
360 getContext().getDialogHelper().showWarningDialog(t("dali.home.map.warning.tooManySurveys", getConfig().getMaxSurveyInMap()));
361 return;
362 }
363
364 getModel().setLoading(true);
365 getExecutor().execute(new MapLoader(selectedSurveys));
366 LOG.debug("MapLoader ask execute");
367 }
368
369 public void zoomFit() {
370
371
372 ReferencedEnvelope displayEnvelope = getUI().getMapPane().getOverallDisplayEnvelope();
373 if (!displayEnvelope.isEmpty()) {
374 getUI().getMapPane().setDisplayArea(displayEnvelope);
375 }
376 }
377
378 public void toggleOffline() {
379
380 boolean selected = getUI().getOfflineButton().isSelected();
381 getModel().setMapMode(selected ? MapMode.EMBEDDED_SHAPE_MAP_MODE : null);
382 buildMapPositions();
383
384 }
385
386 public void toggleGraticule() {
387
388 getUI().getMapPane().toggleGraticuleVisibility();
389
390 }
391
392 public void toggleLegend() {
393
394 getUI().getMapPane().toggleLegendVisibility();
395
396 }
397
398 public void fullScreen() {
399
400 getUI().getFullScreenButton().setVisible(false);
401
402 getModel().getParentUIModel().fireOpenFullScreenEvent();
403
404 }
405
406 @Override
407 public void cancel() {
408
409 closeMap();
410
411 getUI().getFullScreenButton().setVisible(true);
412
413 getModel().getParentUIModel().fireCloseFullScreenEvent();
414
415 }
416
417 class MapLoader extends SwingWorker<MapContent, Void> {
418
419 private final Collection<? extends SurveyDTO> selectedSurveys;
420
421 MapLoader(Collection<? extends SurveyDTO> selectedSurveys) {
422 this.selectedSurveys = selectedSurveys;
423 }
424
425 @Override
426 protected MapContent doInBackground() {
427
428
429
430 if (LOG.isDebugEnabled()) LOG.debug("MapLoader BEGIN");
431
432 DataMapPane mapPane = getUI().getMapPane();
433
434
435 mapPane.clearMapContent();
436
437
438 MapContent mapContent = mapPane.getMapBuilder().buildNewMapContent(getModel().getMapMode());
439
440 mapPane.clearDataLayerCollections();
441
442 boolean noData = false;
443
444 if (CollectionUtils.isNotEmpty(selectedSurveys)) {
445
446 Set<Layer> allLayers = Sets.newLinkedHashSet();
447
448
449 for (SurveyDTO survey : selectedSurveys) {
450
451
452 if (selectedSurveys.size() == 1 && survey.getLocation() == null) {
453 noData = true;
454 break;
455 }
456
457
458
459 if (survey.getId() == null) {
460 continue;
461 }
462
463
464 SurveyDTO clonedSurvey = getContext().getObservationService().duplicateSurvey(survey, false, true);
465 clonedSurvey.setId(survey.getId());
466
467 getContext().getObservationService().loadSamplingOperationsFromSurvey(clonedSurvey, false);
468
469
470 DataLayerCollection dataLayerCollection;
471 try {
472 dataLayerCollection = mapPane.newDataLayerCollection(clonedSurvey);
473 } catch (Exception e) {
474 LOG.error(String.format("Error when create data layer collection for survey '%s'", decorate(clonedSurvey)), e);
475 noData = true;
476 break;
477 }
478
479 List<Layer> dataLayers = null;
480
481 if (dataLayerCollection != null) {
482
483 dataLayers = dataLayerCollection.getLayers();
484 }
485
486 if (CollectionUtils.isNotEmpty(dataLayers)) {
487
488 allLayers.addAll(dataLayers);
489 } else {
490
491 if (LOG.isWarnEnabled()) {
492 LOG.warn(String.format("No data layers built for survey '%s'", decorate(clonedSurvey)));
493 }
494 }
495 }
496
497 if (CollectionUtils.isNotEmpty(allLayers)) {
498
499 mapContent.addLayers(allLayers);
500
501
502 mapContent.addLayer(new InfoPanelLayer());
503
504
505 mapContent.addLayer(new LegendLayer());
506
507
508 ReferencedEnvelope displayEnvelope = mapPane.getOverallDisplayEnvelope();
509 if (!displayEnvelope.isEmpty()) {
510
511
512 mapPane.setLayersVisible(mapContent.layers(), displayEnvelope);
513
514
515 mapPane.setDisplayArea(displayEnvelope);
516 }
517 } else {
518 noData = true;
519 }
520
521 } else {
522 noData = true;
523 }
524
525 if (noData) {
526 mapPane.setLayersVisible(mapContent.layers(), mapPane.getDisplayArea());
527 }
528
529 if (LOG.isDebugEnabled()) LOG.debug("MapLoader END");
530
531 return mapContent;
532 }
533
534 @Override
535 protected void done() {
536 try {
537 if (isCancelled())
538 return;
539
540 try {
541 MapContent mapContent = get();
542
543 DataMapPane mapPane = getUI().getMapPane();
544
545
546 getUI().getOfflineButton().setEnabled(mapPane.getMapBuilder().checkOnlineReachable());
547 getModel().setMapMode(mapPane.getMapBuilder().getCurrentMapMode());
548
549 mapPane.setMapContent(mapContent);
550
551 if (LOG.isDebugEnabled()) LOG.debug("MapLoader done OK");
552
553 } catch (InterruptedException | ExecutionException e) {
554 throw new DaliTechnicalException(e);
555 }
556
557 } finally {
558 getModel().setLoading(false);
559 }
560 }
561 }
562
563 }