diff --git a/src/main/java/org/traccar/web/client/Application.java b/src/main/java/org/traccar/web/client/Application.java index a00c2a43..d9af353c 100644 --- a/src/main/java/org/traccar/web/client/Application.java +++ b/src/main/java/org/traccar/web/client/Application.java @@ -57,15 +57,17 @@ public static DataServiceAsync getDataService() { private final ReportsController reportsController; private final LogController logController; private final GroupsController groupsController; + private final VisibilityController visibilityController; private ApplicationView view; public Application() { DeviceProperties deviceProperties = GWT.create(DeviceProperties.class); - ListStore deviceStore = new ListStore(deviceProperties.id()); + ListStore deviceStore = new ListStore<>(deviceProperties.id()); settingsController = new SettingsController(userSettingsHandler); - mapController = new MapController(mapHandler, deviceStore); + visibilityController = new VisibilityController(); + mapController = new MapController(mapHandler, deviceStore, visibilityController); geoFenceController = new GeoFenceController(deviceStore, mapController); geoFenceController.getGeoFenceStore().addStoreHandlers(geoFenceStoreHandler); commandController = new CommandController(); @@ -73,6 +75,7 @@ public Application() { deviceController = new DeviceController(mapController, geoFenceController, commandController, + visibilityController, deviceStore, deviceStoreHandler, geoFenceController.getGeoFenceStore(), @@ -99,6 +102,7 @@ public void run() { geoFenceController.run(); commandController.run(); groupsController.run(); + visibilityController.run(); setupTimeZone(); } @@ -242,7 +246,6 @@ public void onTakeCurrentMapState(ComboBox mapType, overlays.deselectAll(); for (UserSettings.OverlayType overlayType : UserSettings.OverlayType.values()) { Layer[] mapLayer = mapController.getMap().getLayersByName(i18n.overlayType(overlayType)); - GWT.log(i18n.overlayType(overlayType) + ":: " + mapLayer.length); if (mapLayer != null && mapLayer.length == 1 && mapLayer[0].isVisible()) { overlays.select(overlayType, true); } diff --git a/src/main/java/org/traccar/web/client/controller/DeviceController.java b/src/main/java/org/traccar/web/client/controller/DeviceController.java index 4351c88f..7db423d4 100644 --- a/src/main/java/org/traccar/web/client/controller/DeviceController.java +++ b/src/main/java/org/traccar/web/client/controller/DeviceController.java @@ -67,6 +67,7 @@ public class DeviceController implements ContentController, DeviceView.DeviceHan public DeviceController(MapController mapController, DeviceView.GeoFenceHandler geoFenceHandler, DeviceView.CommandHandler commandHandler, + DeviceView.DeviceVisibilityHandler deviceVisibilityHandler, final ListStore deviceStore, StoreHandlers deviceStoreHandler, ListStore geoFenceStore, @@ -81,7 +82,7 @@ public DeviceController(MapController mapController, this.deviceGeoFences = deviceGeoFences; this.groupStore = groupStore; - deviceView = new DeviceView(this, geoFenceHandler, commandHandler, deviceStore, geoFenceStore, groupStore); + deviceView = new DeviceView(this, geoFenceHandler, commandHandler, deviceVisibilityHandler, deviceStore, geoFenceStore, groupStore); } public ListStore getDeviceStore() { @@ -246,7 +247,10 @@ public void onSuccess(Device result) { @Override public void onMouseOver(int mouseX, int mouseY, Device device) { - positionInfo.show(mouseX, mouseY, mapController.getLatestPosition(device)); + Position latestPosition = mapController.getLatestPosition(device); + if (latestPosition != null) { + positionInfo.show(mouseX, mouseY, latestPosition); + } } @Override diff --git a/src/main/java/org/traccar/web/client/controller/MapController.java b/src/main/java/org/traccar/web/client/controller/MapController.java index 0f80ee9f..0b28c97e 100644 --- a/src/main/java/org/traccar/web/client/controller/MapController.java +++ b/src/main/java/org/traccar/web/client/controller/MapController.java @@ -32,13 +32,15 @@ import org.traccar.web.client.GeoFenceDrawing; import org.traccar.web.client.Track; import org.traccar.web.client.i18n.Messages; +import org.traccar.web.client.state.DeviceVisibilityChangeHandler; +import org.traccar.web.client.state.DeviceVisibilityProvider; import org.traccar.web.client.view.MapView; import org.traccar.web.client.view.MarkerIcon; import org.traccar.web.shared.model.*; import java.util.*; -public class MapController implements ContentController, MapView.MapHandler { +public class MapController implements ContentController, MapView.MapHandler, DeviceVisibilityChangeHandler { public interface MapHandler { void onDeviceSelected(Device device); void onArchivePositionSelected(Position position); @@ -50,10 +52,13 @@ public interface MapHandler { private final ListStore deviceStore; - public MapController(MapHandler mapHandler, ListStore deviceStore) { + public MapController(MapHandler mapHandler, + ListStore deviceStore, + DeviceVisibilityProvider deviceVisibilityProvider) { this.mapHandler = mapHandler; this.deviceStore = deviceStore; - mapView = new MapView(this, deviceStore); + mapView = new MapView(this, deviceStore, deviceVisibilityProvider); + deviceVisibilityProvider.addVisibilityChangeHandler(this); loadMapSettings(); } @@ -103,6 +108,8 @@ public void onSuccess(List positions) { private Map latestPositionMap = new HashMap<>(); + private Map alertsMap = new HashMap<>(); + private Map latestNonIdlePositionMap = new HashMap<>(); private Map timestampMap = new HashMap<>(); @@ -122,7 +129,7 @@ public void onSuccess(List result) { /** * Set up icon, 'idle since' and calculate alerts */ - List alerts = null; + alertsMap.clear(); long currentTime = System.currentTimeMillis(); int selectedIndex = -1; for (int i = 0; i < result.size(); i++) { @@ -155,8 +162,7 @@ public void onSuccess(List result) { // check maintenances for (Maintenance maintenance : device.getMaintenances()) { if (device.getOdometer() >= maintenance.getLastService() + maintenance.getServiceInterval()) { - if (alerts == null) alerts = new LinkedList<>(); - alerts.add(position); + alertsMap.put(device.getId(), position); break; } } @@ -171,9 +177,7 @@ public void onSuccess(List result) { * Draw positions */ mapView.clearLatestPositions(); - mapView.showLatestPositions(result); - mapView.showAlerts(alerts); - mapView.showDeviceName(result); + mapView.showLatestPositions(result, alertsMap.values()); /** * Follow positions and draw track if necessary */ @@ -312,4 +316,17 @@ public void clearArchive(Device device) { public void updateAlert(Device device, boolean show) { mapView.updateAlert(device, show); } + + @Override + public void visibilityChanged(Long deviceId, boolean visible) { + if (visible) { + Position latestPosition = latestPositionMap.get(deviceId); + if (latestPosition != null) { + Position alert = alertsMap.get(deviceId); + mapView.showLatestPositions(Collections.singletonList(latestPosition), alert == null ? null : Collections.singleton(alert)); + } + } else { + mapView.clearLatestPosition(deviceId); + } + } } diff --git a/src/main/java/org/traccar/web/client/controller/VisibilityController.java b/src/main/java/org/traccar/web/client/controller/VisibilityController.java new file mode 100644 index 00000000..2b63a32a --- /dev/null +++ b/src/main/java/org/traccar/web/client/controller/VisibilityController.java @@ -0,0 +1,123 @@ +/* + * Copyright 2015 Vitaly Litvak (vitavaque@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.web.client.controller; + +import com.google.gwt.core.client.GWT; +import com.google.web.bindery.autobean.shared.AutoBean; +import com.google.web.bindery.autobean.shared.AutoBeanCodex; +import com.google.web.bindery.autobean.shared.AutoBeanUtils; +import com.sencha.gxt.widget.core.client.ContentPanel; +import org.traccar.web.client.i18n.Messages; +import org.traccar.web.client.model.BaseAsyncCallback; +import org.traccar.web.client.model.UIStateService; +import org.traccar.web.client.model.UIStateServiceAsync; +import org.traccar.web.client.state.DeviceVisibilityChangeHandler; +import org.traccar.web.client.state.DeviceVisibilityState; +import org.traccar.web.client.state.StateAutoBeanFactory; +import org.traccar.web.client.view.DeviceView; +import org.traccar.web.shared.model.Device; + +import java.util.*; + +public class VisibilityController implements ContentController, DeviceView.DeviceVisibilityHandler { + public static final String STATE_KEY_DEVICE_VISIBILITY = "deviceVisibility"; + + private final Messages i18n = GWT.create(Messages.class); + private final UIStateServiceAsync service = GWT.create(UIStateService.class); + private final StateAutoBeanFactory factory = GWT.create(StateAutoBeanFactory.class); + private final LinkedList visibilityChangeHandlers = new LinkedList<>(); + private DeviceVisibilityState deviceVisibility; + private Map cachedVisibility = new HashMap<>(); + + @Override + public ContentPanel getView() { + return null; + } + + @Override + public void run() { + service.getValue(STATE_KEY_DEVICE_VISIBILITY, new BaseAsyncCallback(i18n) { + @Override + public void onSuccess(String result) { + AutoBean deviceVisibility = + result == null + ? factory.deviceVisibility() + : AutoBeanCodex.decode(factory, DeviceVisibilityState.class, result); + loadedDeviceVisibility(deviceVisibility.as()); + } + }); + } + + private void loadedDeviceVisibility(DeviceVisibilityState deviceVisibility) { + this.deviceVisibility = deviceVisibility; + if (deviceVisibility.getHiddenForced() == null) { + deviceVisibility.setHiddenForced(new HashSet()); + } + if (deviceVisibility.getVisibleForced() == null) { + deviceVisibility.setVisibleForced(new HashSet()); + } + for (Map.Entry entry : cachedVisibility.entrySet()) { + Long deviceId = entry.getKey(); + boolean calculated = calculateVisibility(deviceId); + if (entry.getValue() != calculated) { + entry.setValue(calculated); + fireChange(deviceId, calculated); + } + } + } + + @Override + public boolean isVisible(Device device) { + Long deviceId = device.getId(); + Boolean visibility = cachedVisibility.get(deviceId); + if (visibility == null) { + visibility = calculateVisibility(deviceId); + cachedVisibility.put(deviceId, visibility); + } + return visibility; + } + + private boolean calculateVisibility(Long deviceId) { + return deviceVisibility != null + && (deviceVisibility.getVisibleForced().contains(deviceId) + || !deviceVisibility.getHiddenForced().contains(deviceId)); + } + + @Override + public void setVisible(Device device, boolean b) { + (b ? deviceVisibility.getHiddenForced() : deviceVisibility.getVisibleForced()).remove(device.getId()); + (b ? deviceVisibility.getVisibleForced() : deviceVisibility.getHiddenForced()).add(device.getId()); + service.setValue(STATE_KEY_DEVICE_VISIBILITY, + AutoBeanCodex.encode(AutoBeanUtils.getAutoBean(deviceVisibility)).getPayload(), + new BaseAsyncCallback(i18n)); + Boolean previous = cachedVisibility.get(device.getId()); + if (previous == null || previous != b) { + cachedVisibility.put(device.getId(), b); + fireChange(device.getId(), b); + } + } + + @Override + public void addVisibilityChangeHandler(DeviceVisibilityChangeHandler visibilityChangeHandler) { + visibilityChangeHandlers.add(visibilityChangeHandler); + } + + private void fireChange(Long deviceId, boolean visibility) { + for (Iterator it = visibilityChangeHandlers.descendingIterator(); it.hasNext(); ) { + it.next().visibilityChanged(deviceId, visibility); + } + } +} diff --git a/src/main/java/org/traccar/web/client/state/DeviceVisibilityChangeHandler.java b/src/main/java/org/traccar/web/client/state/DeviceVisibilityChangeHandler.java new file mode 100644 index 00000000..195e5fa5 --- /dev/null +++ b/src/main/java/org/traccar/web/client/state/DeviceVisibilityChangeHandler.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Vitaly Litvak (vitavaque@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.web.client.state; + +public interface DeviceVisibilityChangeHandler { + void visibilityChanged(Long deviceId, boolean visible); +} diff --git a/src/main/java/org/traccar/web/client/state/DeviceVisibilityProvider.java b/src/main/java/org/traccar/web/client/state/DeviceVisibilityProvider.java new file mode 100644 index 00000000..55b82f64 --- /dev/null +++ b/src/main/java/org/traccar/web/client/state/DeviceVisibilityProvider.java @@ -0,0 +1,23 @@ +/* + * Copyright 2015 Vitaly Litvak (vitavaque@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.web.client.state; + +import org.traccar.web.shared.model.Device; + +public interface DeviceVisibilityProvider { + boolean isVisible(Device device); + void addVisibilityChangeHandler(DeviceVisibilityChangeHandler visibilityChangeHandler); +} diff --git a/src/main/java/org/traccar/web/client/state/DeviceVisibilityState.java b/src/main/java/org/traccar/web/client/state/DeviceVisibilityState.java new file mode 100644 index 00000000..769eda5b --- /dev/null +++ b/src/main/java/org/traccar/web/client/state/DeviceVisibilityState.java @@ -0,0 +1,38 @@ +/* + * Copyright 2015 Vitaly Litvak (vitavaque@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.web.client.state; + +import java.util.Set; + +public interface DeviceVisibilityState { + boolean getHideOnline(); + void setHideOnline(boolean hideOnline); + + boolean getHideOffline(); + void setHideOffline(boolean hideOffline); + + boolean getHideIdle(); + void setHideIdle(boolean hideIdle); + + boolean getHideMoving(); + void setHideMoving(boolean hideMoving); + + Set getHiddenForced(); + void setHiddenForced(Set hiddenForced); + + Set getVisibleForced(); + void setVisibleForced(Set visibleForced); +} diff --git a/src/main/java/org/traccar/web/client/state/StateAutoBeanFactory.java b/src/main/java/org/traccar/web/client/state/StateAutoBeanFactory.java index 80b447be..22aedff2 100644 --- a/src/main/java/org/traccar/web/client/state/StateAutoBeanFactory.java +++ b/src/main/java/org/traccar/web/client/state/StateAutoBeanFactory.java @@ -20,4 +20,5 @@ public interface StateAutoBeanFactory extends DefaultStateAutoBeanFactory { AutoBean checkBox(); + AutoBean deviceVisibility(); } diff --git a/src/main/java/org/traccar/web/client/view/ApplicationView.java b/src/main/java/org/traccar/web/client/view/ApplicationView.java index 882463a1..f00bb9c2 100644 --- a/src/main/java/org/traccar/web/client/view/ApplicationView.java +++ b/src/main/java/org/traccar/web/client/view/ApplicationView.java @@ -124,8 +124,8 @@ public ApplicationView(ContentPanel navView, archivePanel.removeFromParent(); } - if (devicePanel.getElement().getSize().getWidth() < 260) { - westData.setSize(260); + if (devicePanel.getElement().getSize().getWidth() < 310) { + westData.setSize(310); } new ExpandCollapseHandler(southData, Style.LayoutRegion.SOUTH, archivePanel, 84); diff --git a/src/main/java/org/traccar/web/client/view/DeviceView.java b/src/main/java/org/traccar/web/client/view/DeviceView.java index a3d20a64..903b3378 100644 --- a/src/main/java/org/traccar/web/client/view/DeviceView.java +++ b/src/main/java/org/traccar/web/client/view/DeviceView.java @@ -35,7 +35,6 @@ import com.sencha.gxt.core.client.ToStringValueProvider; import com.sencha.gxt.core.client.ValueProvider; import com.sencha.gxt.core.client.XTemplates; -import com.sencha.gxt.data.shared.SortInfo; import com.sencha.gxt.data.shared.Store; import com.sencha.gxt.data.shared.event.StoreAddEvent; import com.sencha.gxt.data.shared.event.StoreRemoveEvent; @@ -59,6 +58,7 @@ import org.traccar.web.client.model.BaseStoreHandlers; import org.traccar.web.client.model.DeviceProperties; import org.traccar.web.client.model.GeoFenceProperties; +import org.traccar.web.client.state.DeviceVisibilityProvider; import org.traccar.web.client.state.GridStateHandler; import org.traccar.web.shared.model.Device; @@ -110,6 +110,10 @@ public interface CommandHandler { void onCommand(Device device); } + public interface DeviceVisibilityHandler extends DeviceVisibilityProvider { + void setVisible(Device device, boolean b); + } + private static class GroupsHandler extends BaseStoreHandlers { private final ListStore deviceStore; private final ListStore groupStore; @@ -285,6 +289,7 @@ public ContentPanel getView() { public DeviceView(final DeviceHandler deviceHandler, final GeoFenceHandler geoFenceHandler, final CommandHandler commandHandler, + final DeviceVisibilityHandler deviceVisibilityHandler, final ListStore deviceStore, final ListStore geoFenceStore, ListStore groupStore) { @@ -295,6 +300,7 @@ public DeviceView(final DeviceHandler deviceHandler, // create a new devices store so the filtering will not affect global store this.deviceStore = new ListStore<>(deviceStore.getKeyProvider()); + this.deviceStore.setAutoCommit(true); this.deviceStore.addAll(deviceStore.getAll()); // handle events from global devices store and update local one accordingly deviceStore.addStoreHandlers(new BaseStoreHandlers() { @@ -327,9 +333,35 @@ public void onSort(StoreSortEvent event) { }); DeviceProperties deviceProperties = GWT.create(DeviceProperties.class); + Resources resources = GWT.create(Resources.class); + HeaderIconTemplate headerTemplate = GWT.create(HeaderIconTemplate.class); List> columnConfigList = new LinkedList<>(); + // 'Visible' column + ColumnConfig colVisible = new ColumnConfig<>(new ValueProvider() { + @Override + public Boolean getValue(Device device) { + return deviceVisibilityHandler.isVisible(device); + } + + @Override + public void setValue(Device device, Boolean value) { + deviceVisibilityHandler.setVisible(device, value); + } + + @Override + public String getPath() { + return "visible"; + } + }, 50, ""); + colVisible.setCell(new CheckBoxCell()); + colVisible.setFixed(true); + colVisible.setResizable(false); + colVisible.setToolTip(new SafeHtmlBuilder().appendEscaped(i18n.visible()).toSafeHtml()); + columnConfigList.add(colVisible); + + // Name column ColumnConfig colName = new ColumnConfig<>(deviceProperties.name(), 0, i18n.name()); colName.setCell(new AbstractCell(BrowserEvents.MOUSEOVER, BrowserEvents.MOUSEOUT) { @Override @@ -357,9 +389,7 @@ public void onBrowserEvent(Context context, Element parent, String value, Native }); columnConfigList.add(colName); - Resources resources = GWT.create(Resources.class); - HeaderIconTemplate headerTemplate = GWT.create(HeaderIconTemplate.class); - + // 'Follow' column ColumnConfig colFollow = new ColumnConfig<>(new ValueProvider() { @Override @@ -387,6 +417,7 @@ public String getPath() { colFollow.setToolTip(new SafeHtmlBuilder().appendEscaped(i18n.follow()).toSafeHtml()); columnConfigList.add(colFollow); + // 'Record trace' column ColumnConfig colRecordTrace = new ColumnConfig<>(new ValueProvider() { @Override public Boolean getValue(Device device) { @@ -511,7 +542,10 @@ public void onSelectionChanged(SelectionChangedEvent event) { @Override public void onRowMouseDown(RowMouseDownEvent event) { - deviceHandler.onSelected(grid.getSelectionModel().getSelectedItem()); + Device device = grid.getSelectionModel().getSelectedItem(); + if (device != null) { + deviceHandler.onSelected(device); + } } @Override diff --git a/src/main/java/org/traccar/web/client/view/MapPositionRenderer.java b/src/main/java/org/traccar/web/client/view/MapPositionRenderer.java index 2b859102..73581a94 100644 --- a/src/main/java/org/traccar/web/client/view/MapPositionRenderer.java +++ b/src/main/java/org/traccar/web/client/view/MapPositionRenderer.java @@ -35,6 +35,7 @@ import org.gwtopenmaps.openlayers.client.util.JSObject; import org.traccar.web.client.Track; import org.traccar.web.client.TrackSegment; +import org.traccar.web.client.state.DeviceVisibilityProvider; import org.traccar.web.shared.model.Device; import org.traccar.web.shared.model.Position; @@ -61,11 +62,16 @@ protected Markers getMarkerLayer() { private final SelectHandler selectHandler; private final MouseHandler mouseHandler; + private final DeviceVisibilityProvider visibilityProvider; - public MapPositionRenderer(MapView mapView, SelectHandler selectHandler, MouseHandler mouseHandler) { + public MapPositionRenderer(MapView mapView, + SelectHandler selectHandler, + MouseHandler mouseHandler, + DeviceVisibilityProvider visibilityProvider) { this.mapView = mapView; this.selectHandler = selectHandler; this.mouseHandler = mouseHandler; + this.visibilityProvider = visibilityProvider; } private void addSelectEvent(Marker marker, final Position position) { @@ -271,6 +277,10 @@ public void clear(Device device) { clear(getDeviceData(device)); } + public void clear(Long deviceId) { + clear(getDeviceData(deviceId)); + } + private void clearMarkersAndTitleAndAlert(DeviceData deviceData) { for (Marker marker : deviceData.markerMap.values()) { getMarkerLayer().removeMarker(marker); @@ -335,13 +345,15 @@ public void showPositions(List positions) { DeviceData deviceData = getDeviceData(positions); deviceData.positions = positions; for (Position position : positions) { - Marker marker = new Marker( - mapView.createLonLat(position.getLongitude(), position.getLatitude()), - MarkerIconFactory.getIcon(position.getIcon(), false)); - deviceData.markerMap.put(position.getId(), marker); - addSelectEvent(marker, position); - addMouseEvent(marker, position); - getMarkerLayer().addMarker(marker); + if (visibilityProvider.isVisible(position.getDevice())) { + Marker marker = new Marker( + mapView.createLonLat(position.getLongitude(), position.getLatitude()), + MarkerIconFactory.getIcon(position.getIcon(), false)); + deviceData.markerMap.put(position.getId(), marker); + addSelectEvent(marker, position); + addMouseEvent(marker, position); + getMarkerLayer().addMarker(marker); + } } if (!selectPosition(null, selectedPosition, false)) { @@ -359,46 +371,52 @@ public void showPositions(List positions) { public void showDeviceName(List positions) { for (Position position : positions) { - DeviceData deviceData = getDeviceData(position.getDevice()); - org.gwtopenmaps.openlayers.client.Style st = new org.gwtopenmaps.openlayers.client.Style(); - st.setLabel(position.getDevice().getName()); - st.setLabelXOffset(0); - st.setLabelYOffset(-12); - st.setLabelAlign("cb"); - st.setFontColor("#0000FF"); - st.setFontSize("12"); - st.setFill(false); - st.setStroke(false); - - final VectorFeature deviceName = new VectorFeature(mapView.createPoint(position.getLongitude(), position.getLatitude()), st); - getVectorLayer().addFeature(deviceName); - deviceData.title = deviceName; + if (visibilityProvider.isVisible(position.getDevice())) { + DeviceData deviceData = getDeviceData(position.getDevice()); + org.gwtopenmaps.openlayers.client.Style st = new org.gwtopenmaps.openlayers.client.Style(); + st.setLabel(position.getDevice().getName()); + st.setLabelXOffset(0); + st.setLabelYOffset(-12); + st.setLabelAlign("cb"); + st.setFontColor("#0000FF"); + st.setFontSize("12"); + st.setFill(false); + st.setStroke(false); + + final VectorFeature deviceName = new VectorFeature(mapView.createPoint(position.getLongitude(), position.getLatitude()), st); + getVectorLayer().addFeature(deviceName); + deviceData.title = deviceName; + } } } public void showTime(List positions, boolean abovePoint) { DeviceData deviceData = getDeviceData(positions); for (Position position : positions) { - org.gwtopenmaps.openlayers.client.Style st = new org.gwtopenmaps.openlayers.client.Style(); - st.setLabel(timeFormat.format(position.getTime())); - st.setLabelXOffset(0); - st.setLabelYOffset(abovePoint ? 12 : -12); - st.setLabelAlign("cb"); - st.setFontColor("#FF4D00"); - st.setFontSize("11"); - st.setFill(false); - st.setStroke(false); - - final VectorFeature point = new VectorFeature(mapView.createPoint(position.getLongitude(), position.getLatitude()), st); - getVectorLayer().addFeature(point); - deviceData.timeLabels.put(position, point); + if (visibilityProvider.isVisible(position.getDevice())) { + org.gwtopenmaps.openlayers.client.Style st = new org.gwtopenmaps.openlayers.client.Style(); + st.setLabel(timeFormat.format(position.getTime())); + st.setLabelXOffset(0); + st.setLabelYOffset(abovePoint ? 12 : -12); + st.setLabelAlign("cb"); + st.setFontColor("#FF4D00"); + st.setFontSize("11"); + st.setFill(false); + st.setStroke(false); + + final VectorFeature point = new VectorFeature(mapView.createPoint(position.getLongitude(), position.getLatitude()), st); + getVectorLayer().addFeature(point); + deviceData.timeLabels.put(position, point); + } } } public void showTrack(Track track) { List segments = track.getSegments(); - if (!segments.isEmpty()) { + if (!segments.isEmpty() + && visibilityProvider.isVisible(segments.get(0).getPositions().get(0).getDevice())) { DeviceData deviceData = getDeviceData(segments.get(0).getPositions()); + List linePoints = new ArrayList<>(); for (TrackSegment segment : segments) { @@ -486,6 +504,9 @@ public void selectPosition(Position position, boolean center) { } public void selectDevice(Device device, boolean center) { + if (!visibilityProvider.isVisible(device)) { + return; + } DeviceData oldDeviceData = getDeviceData(selectedDeviceId); Position oldPosition = oldDeviceData == null ? null : oldDeviceData.getLatestPosition(); @@ -519,7 +540,8 @@ private boolean selectPosition(Position oldPosition, Position newPosition, boole } public void catchPosition(Position position) { - if (!mapView.getMap().getExtent().containsLonLat(mapView.createLonLat(position.getLongitude(), position.getLatitude()), true)) { + if (visibilityProvider.isVisible(position.getDevice()) + && !mapView.getMap().getExtent().containsLonLat(mapView.createLonLat(position.getLongitude(), position.getLatitude()), true)) { selectPosition(position, true); } } @@ -559,9 +581,11 @@ public void clearTrackPositions(Device device, Date before) { public void showTrackPositions(List positions) { DeviceData deviceData = getDeviceData(positions); for (Position position : positions) { - VectorFeature point = new VectorFeature(mapView.createPoint(position.getLongitude(), position.getLatitude()), getTrackPointStyle()); - getVectorLayer().addFeature(point); - deviceData.trackPoints.add(point); + if (visibilityProvider.isVisible(position.getDevice())) { + VectorFeature point = new VectorFeature(mapView.createPoint(position.getLongitude(), position.getLatitude()), getTrackPointStyle()); + getVectorLayer().addFeature(point); + deviceData.trackPoints.add(point); + } } } @@ -577,20 +601,24 @@ private org.gwtopenmaps.openlayers.client.Style getTrackPointStyle() { } public void updateIcon(Device device) { - DeviceData deviceData = getDeviceData(device); - Position position = deviceData.positions == null || deviceData.positions.size() != 1 ? null : deviceData.positions.get(0); - if (position != null) { - position.setDevice(device); - position.setIcon(MarkerIcon.create(position)); - boolean selected = selectedPosition != null && selectedPosition.getId() == position.getId(); - changeMarkerIcon(position, MarkerIconFactory.getIcon(position.getIcon(), selected)); + if (visibilityProvider.isVisible(device)) { + DeviceData deviceData = getDeviceData(device); + Position position = deviceData.positions == null || deviceData.positions.size() != 1 ? null : deviceData.positions.get(0); + if (position != null) { + position.setDevice(device); + position.setIcon(MarkerIcon.create(position)); + boolean selected = selectedPosition != null && selectedPosition.getId() == position.getId(); + changeMarkerIcon(position, MarkerIconFactory.getIcon(position.getIcon(), selected)); + } } } - public void showAlerts(List positions) { + public void showAlerts(Collection positions) { if (positions != null) { for (Position position : positions) { - drawAlert(position); + if (visibilityProvider.isVisible(position.getDevice())) { + drawAlert(position); + } } } } @@ -618,7 +646,7 @@ public void updateAlert(Device device, boolean show) { getVectorLayer().removeFeature(deviceData.alert); deviceData.alert.destroy(); } - if (show) { + if (show && visibilityProvider.isVisible(device)) { Position latestPosition = deviceData.getLatestPosition(); if (latestPosition != null) { drawAlert(latestPosition); diff --git a/src/main/java/org/traccar/web/client/view/MapView.java b/src/main/java/org/traccar/web/client/view/MapView.java index 057d9458..4ede616b 100644 --- a/src/main/java/org/traccar/web/client/view/MapView.java +++ b/src/main/java/org/traccar/web/client/view/MapView.java @@ -15,10 +15,7 @@ */ package org.traccar.web.client.view; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; +import java.util.*; import com.google.gwt.core.client.GWT; import com.sencha.gxt.data.shared.ListStore; @@ -41,11 +38,14 @@ import org.gwtopenmaps.openlayers.client.event.MapZoomListener; import org.gwtopenmaps.openlayers.client.geometry.Point; import org.gwtopenmaps.openlayers.client.layer.*; +import org.gwtopenmaps.openlayers.client.layer.Vector; import org.gwtopenmaps.openlayers.client.util.JSObject; import org.traccar.web.client.ApplicationContext; import org.traccar.web.client.GeoFenceDrawing; import org.traccar.web.client.Track; import org.traccar.web.client.i18n.Messages; +import org.traccar.web.client.state.DeviceVisibilityChangeHandler; +import org.traccar.web.client.state.DeviceVisibilityProvider; import org.traccar.web.shared.model.Device; import org.traccar.web.shared.model.GeoFence; import org.traccar.web.shared.model.Position; @@ -195,7 +195,9 @@ public static native JSObject getTileURL() /*-{ return $wnd.getTileURL; }-*/; - public MapView(MapHandler mapHandler, ListStore deviceStore) { + public MapView(MapHandler mapHandler, + ListStore deviceStore, + DeviceVisibilityProvider deviceVisibilityProvider) { this.mapHandler = mapHandler; this.popup = new PositionInfoPopup(deviceStore); contentPanel = new ContentPanel(); @@ -282,9 +284,18 @@ public void onMapZoom(MapZoomEvent eventObject) { } }); - latestPositionRenderer = new MapPositionRenderer(this, latestPositionSelectHandler, positionMouseHandler); - archivePositionRenderer = new MapPositionRenderer(this, archivePositionSelectHandler, positionMouseHandler); - latestPositionTrackRenderer = new MapPositionRenderer(this, null, null); + latestPositionRenderer = new MapPositionRenderer(this, latestPositionSelectHandler, positionMouseHandler, deviceVisibilityProvider); + archivePositionRenderer = new MapPositionRenderer(this, archivePositionSelectHandler, positionMouseHandler, new DeviceVisibilityProvider() { + @Override + public boolean isVisible(Device device) { + return true; + } + + @Override + public void addVisibilityChangeHandler(DeviceVisibilityChangeHandler visibilityChangeHandler) { + } + }); + latestPositionTrackRenderer = new MapPositionRenderer(this, null, null, deviceVisibilityProvider); geoFenceRenderer = new GeoFenceRenderer(this); } @@ -297,17 +308,23 @@ public void clearLatestPositions() { latestPositionRenderer.clearPositionsAndTitlesAndAlerts(); } - public void showLatestPositions(List positions) { + public void clearLatestPosition(Long deviceId) { + latestPositionRenderer.clear(deviceId); + } + + public void showLatestPositions(List positions, Collection alerts) { for (Position position : positions) { latestPositionRenderer.showPositions(Collections.singletonList(position)); } + showAlerts(alerts); + showDeviceName(positions); } public void showDeviceName(List positions) { latestPositionRenderer.showDeviceName(positions); } - public void showAlerts(List positions) { + public void showAlerts(Collection positions) { latestPositionRenderer.showAlerts(positions); }