package com.grigowashere.aismap.maps; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.view.View; import com.grigowashere.aismap.models.Vessel; import com.grigowashere.aismap.models.AISVessel; import com.grigowashere.aismap.view.CursorOverlay; import com.grigowashere.aismap.R; import com.yandex.mapkit.Animation; import android.view.ViewGroup; import com.yandex.mapkit.geometry.Point; import com.yandex.mapkit.map.CameraPosition; import com.yandex.mapkit.map.MapObjectCollection; import com.yandex.mapkit.mapview.MapView; import com.yandex.runtime.image.ImageProvider; import java.util.HashMap; import java.util.Map; /** * Реализация карты для Яндекс.Карт * Использует новый менеджер маркеров для предотвращения финализации объектов */ public class YandexMapImpl implements MapInterface { private Context context; private MapView mapView; private MapObjectCollection mapObjects; private MarkerClickListener markerClickListener; // Новый менеджер маркеров private YandexMarkerManager markerManager; // Слушатель поворота карты private com.yandex.mapkit.map.InputListener inputListener; private float lastMapAzimuth = 0.0f; // Курсор overlay private CursorOverlay cursorOverlay; private Vessel ownVessel; public YandexMapImpl(Context context, MapView mapView) { this.context = context; this.mapView = mapView; this.cursorOverlay = new CursorOverlay(context); // Добавляем overlay курсора в MapView if (mapView instanceof ViewGroup) { ViewGroup parent = (ViewGroup) mapView; // Проверяем, не добавлен ли уже курсор if (parent.findViewById(R.id.cursor_cross) == null) { parent.addView(cursorOverlay.getView()); } } // Получение коллекции объектов карты try { this.mapObjects = mapView.getMap().getMapObjects().addCollection(); // Инициализируем менеджер маркеров com.grigowashere.aismap.utils.SettingsManager settingsManager = new com.grigowashere.aismap.utils.SettingsManager(context); this.markerManager = new YandexMarkerManager(context, mapObjects, mapView, settingsManager); } catch (Exception e) { // Ошибка создания коллекции объектов карты } } @Override public void initialize() { // Инициализируем слушатель поворота карты setupCameraListener(); // Инициализируем слушатель движения карты setupMapMovementListener(); // Инициализируем менеджер маркеров if (markerManager != null) { markerManager.initialize(); } } @Override public void cleanup() { // Очищаем менеджер маркеров if (markerManager != null) { markerManager.cleanup(); } // Удаляем слушатель ввода if (inputListener != null && mapView != null) { mapView.getMap().removeInputListener(inputListener); } if (mapObjects != null) { mapView.getMap().getMapObjects().remove(mapObjects); } if (mapView != null) { mapView.onStop(); } } @Override public void addOwnVesselMarker(Vessel vessel) { this.ownVessel = vessel; if (cursorOverlay != null) { cursorOverlay.setOwnVessel(vessel); } if (markerManager != null) { markerManager.updateOwnVesselMarker(vessel); } } @Override public void updateOwnVesselPosition(Vessel vessel) { this.ownVessel = vessel; if (cursorOverlay != null) { cursorOverlay.setOwnVessel(vessel); } if (markerManager != null) { markerManager.updateOwnVesselMarker(vessel); } } @Override public void addAISVesselMarker(AISVessel vessel) { if (markerManager != null) { markerManager.updateAISVesselMarker(vessel); } } @Override public void updateAISVesselPosition(AISVessel vessel) { if (markerManager != null) { markerManager.updateAISVesselMarker(vessel); } } @Override public void removeAISVesselMarker(String mmsi) { if (markerManager != null) { markerManager.removeAISVesselMarker(mmsi); } } @Override public void clearAISVesselMarkers() { if (markerManager != null) { markerManager.clearAISVesselMarkers(); } } @Override public void centerOnPosition(double latitude, double longitude) { Point point = new Point(latitude, longitude); CameraPosition cameraPosition = new CameraPosition(point, 13.0f, 0.0f, 0.0f); mapView.getMap().move(cameraPosition, new Animation(Animation.Type.SMOOTH, 1.0f), null); } @Override public void setZoom(float zoom) { CameraPosition currentPosition = mapView.getMap().getCameraPosition(); Point target = currentPosition.getTarget(); CameraPosition newPosition = new CameraPosition(target, zoom, currentPosition.getAzimuth(), currentPosition.getTilt()); mapView.getMap().move(newPosition, new Animation(Animation.Type.SMOOTH, 0.5f), null); } @Override public float getZoom() { return mapView.getMap().getCameraPosition().getZoom(); } @Override public void addLayer(String layerId, Object layerData) { // Реализация добавления дополнительных слоев // Зависит от конкретных требований } @Override public void removeLayer(String layerId) { // Реализация удаления слоев } @Override public void setMarkerClickListener(MarkerClickListener listener) { this.markerClickListener = listener; // Устанавливаем обработчик в менеджере маркеров if (markerManager != null) { markerManager.setMarkerClickListener(listener); } } /** * Обновляет обработчики кликов для всех существующих маркеров * Этот метод переустанавливает обработчики для всех маркеров */ public void refreshMarkerClickListeners() { if (markerManager != null) { markerManager.checkAndRestoreMarkers(); } } /** * Перерисовывает все маркеры с учетом текущего азимута карты * Вызывается при повороте карты */ public void refreshAllMarkers() { if (markerManager != null) { markerManager.refreshAllMarkers(); } } /** * Обновляет все маркеры при повороте карты * Вызывается из слушателя поворота карты */ public void onMapRotationChanged() { if (markerManager != null) { markerManager.refreshAllMarkers(); } } /** * Принудительно обновляет все маркеры * Можно вызывать извне для обновления маркеров */ public void forceRefreshMarkers() { if (markerManager != null) { markerManager.refreshAllMarkers(); } } /** * Принудительно обновляет все маркеры при изменении зума */ public void forceRefreshMarkersOnZoomChange() { if (markerManager != null) { markerManager.forceRefreshAllMarkers(); } } /** * Проверяет и восстанавливает финализированные маркеры */ public void checkAndRestoreMarkers() { if (markerManager != null) { markerManager.checkAndRestoreMarkers(); } } /** * Получает количество активных маркеров */ public int getActiveMarkerCount() { if (markerManager != null) { return markerManager.getActiveMarkerCount(); } return 0; } /** * Включает/выключает отображение путей движения */ public void setPathTrackingEnabled(boolean enabled) { if (markerManager != null) { markerManager.setPathTrackingEnabled(enabled); } } /** * Очищает путь конкретного судна */ public void clearVesselPath(String mmsi) { if (markerManager != null) { markerManager.clearVesselPath(mmsi); } } /** * Очищает трекер пути собственного судна */ @Override public void clearVesselPath() { if (markerManager != null) { markerManager.clearVesselPath("own_vessel"); } // Также очищаем VesselPathController если он используется // (для MapLibre это делается в MapLibreMapImpl, для Yandex - здесь) // В YandexMapImpl VesselPathController не используется напрямую, // но если в будущем будет использоваться, нужно добавить очистку } /** * Очищает все пути движения */ public void clearAllPaths() { if (markerManager != null) { markerManager.clearAllPaths(); } } /** * Обновляет настройки отображения путей */ public void updatePathSettings(int pathColor, int predictionColor, float pathWidth, float predictionWidth) { if (markerManager != null) { markerManager.updatePathSettings(pathColor, predictionColor, pathWidth, predictionWidth); } } /** * Получение MapView для использования в layout */ public MapView getMapView() { return mapView; } /** * Настройка слушателя поворота карты */ private void setupCameraListener() { try { inputListener = new com.yandex.mapkit.map.InputListener() { @Override public void onMapTap(com.yandex.mapkit.map.Map map, com.yandex.mapkit.geometry.Point point) { // Не обрабатываем клики по карте } @Override public void onMapLongTap(com.yandex.mapkit.map.Map map, com.yandex.mapkit.geometry.Point point) { // Не обрабатываем долгие клики по карте } }; // Добавляем слушатель к карте mapView.getMap().addInputListener(inputListener); // Включаем жесты поворота карты mapView.getMap().setRotateGesturesEnabled(true); // Добавляем слушатель изменений камеры для обновления маркеров при повороте и зуме mapView.getMap().addCameraListener(new com.yandex.mapkit.map.CameraListener() { private long lastUpdateTime = 0; private static final long UPDATE_THROTTLE = 200; // 200мс между обновлениями (увеличено для снижения нагрузки) private float lastZoom = -1; @Override public void onCameraPositionChanged(com.yandex.mapkit.map.Map map, com.yandex.mapkit.map.CameraPosition cameraPosition, com.yandex.mapkit.map.CameraUpdateReason reason, boolean finished) { // Обновляем маркеры в реальном времени с throttling long currentTime = System.currentTimeMillis(); float currentZoom = cameraPosition.getZoom(); // Проверяем, изменился ли зум значительно (больше чем на 1.0) boolean zoomChanged = Math.abs(currentZoom - lastZoom) > 1.0f; if (currentTime - lastUpdateTime >= UPDATE_THROTTLE || zoomChanged) { //onMapRotationChanged(); // Обновляем маркеры только при значительных изменениях if (zoomChanged) { // При изменении зума принудительно обновляем все маркеры forceRefreshMarkersOnZoomChange(); } else { // При повороте только проверяем валидность маркеров checkAndRestoreMarkers(); } lastUpdateTime = currentTime; lastZoom = currentZoom; } } }); // Добавляем дополнительный слушатель для жестов поворота mapView.getMap().addInputListener(new com.yandex.mapkit.map.InputListener() { private long lastGestureTime = 0; private static final long GESTURE_THROTTLE = 100; // 100мс между обновлениями @Override public void onMapTap(com.yandex.mapkit.map.Map map, com.yandex.mapkit.geometry.Point point) { // Не обрабатываем клики по карте } @Override public void onMapLongTap(com.yandex.mapkit.map.Map map, com.yandex.mapkit.geometry.Point point) { // Не обрабатываем долгие клики по карте } }); } catch (Exception e) { // Ошибка установки слушателя } } @Override public void showCursor() { if (cursorOverlay != null) { cursorOverlay.showCursor(); } } @Override public void hideCursor() { if (cursorOverlay != null) { cursorOverlay.hideCursor(); } } @Override public void updateCursorCoordinates(double latitude, double longitude) { if (cursorOverlay != null) { cursorOverlay.updateCursorCoordinates(latitude, longitude); } } @Override public void updateCursorFromMapCenter() { if (cursorOverlay != null && mapView != null) { // Получаем координаты центра карты com.yandex.mapkit.geometry.Point center = mapView.getMap().getCameraPosition().getTarget(); cursorOverlay.updateCursorCoordinates(center.getLatitude(), center.getLongitude()); } } @Override public void setAisVesselInfo(com.grigowashere.aismap.models.AISVessel vessel) { if (cursorOverlay != null) { cursorOverlay.setAisVesselInfo(vessel); } } @Override public void clearAisVesselInfo() { if (cursorOverlay != null) { cursorOverlay.clearAisVesselInfo(); } } /** * Настраивает слушатель движения карты для обновления курсора */ private void setupMapMovementListener() { if (mapView != null) { mapView.getMap().addCameraListener(new com.yandex.mapkit.map.CameraListener() { @Override public void onCameraPositionChanged(com.yandex.mapkit.map.Map map, com.yandex.mapkit.map.CameraPosition cameraPosition, com.yandex.mapkit.map.CameraUpdateReason cameraUpdateReason, boolean finished) { // Обновляем координаты курсора при движении карты updateCursorFromMapCenter(); } }); } } }