diff --git a/.gitignore b/.gitignore index 889f3aa..db042e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.iml .gradle +.idea /local.properties /.idea/caches /.idea/libraries @@ -14,3 +15,43 @@ .externalNativeBuild .cxx local.properties +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Log/OS Files +*.log + +# Android Studio generated files and folders +captures/ +.externalNativeBuild/ +.cxx/ +*.aab +*.apk +output-metadata.json + +# IntelliJ +*.iml +.idea/ +misc.xml +deploymentTargetDropDown.xml +render.experimental.xml + +# Keystore files +*.jks +*.keystore + +# Google Services (e.g. APIs or Firebase) +google-services.json + +# Android Profiling +*.hprof + +# Android Studio / IntelliJ IDEA +*.iws +.idea/libraries +.idea/tasks.xml +.idea/vcs.xml +.idea/workspace.xml \ No newline at end of file diff --git a/app/src/main/java/com/grigowashere/aismap/maps/YandexMapImpl.java b/app/src/main/java/com/grigowashere/aismap/maps/YandexMapImpl.java index fa01bfa..95e2581 100644 --- a/app/src/main/java/com/grigowashere/aismap/maps/YandexMapImpl.java +++ b/app/src/main/java/com/grigowashere/aismap/maps/YandexMapImpl.java @@ -39,40 +39,44 @@ public class YandexMapImpl implements MapInterface { private boolean ownVesselClickListenerSet = false; private Map aisVesselClickListenersSet = new HashMap<>(); + // Флаги для предотвращения повторного добавления обработчиков + private Map aisVesselClickListenersAdded = new HashMap<>(); + + // Слушатель поворота карты + private com.yandex.mapkit.map.InputListener inputListener; + private float lastMapAzimuth = 0.0f; + + // Отслеживание двойного клика для AIS судов + private Map lastClickTime = new HashMap<>(); + private static final long DOUBLE_CLICK_DELAY = 300; // 300мс для двойного клика + public YandexMapImpl(Context context, MapView mapView) { this.context = context; this.mapView = mapView; this.aisMarkers = new HashMap<>(); this.aisVessels = new HashMap<>(); - android.util.Log.d("YandexMapImpl", "Конструктор YandexMapImpl вызван"); - android.util.Log.d("YandexMapImpl", "Context: " + (context != null ? "установлен" : "null")); - android.util.Log.d("YandexMapImpl", "MapView: " + (mapView != null ? "установлен" : "null")); - // Получение коллекции объектов карты try { this.mapObjects = mapView.getMap().getMapObjects().addCollection(); - android.util.Log.d("YandexMapImpl", "Коллекция объектов карты создана: " + (mapObjects != null ? "успешно" : "null")); } catch (Exception e) { - android.util.Log.e("YandexMapImpl", "Ошибка создания коллекции объектов карты: " + e.getMessage(), e); + // Ошибка создания коллекции объектов карты } } @Override public void initialize() { - android.util.Log.d("YandexMapImpl", "initialize() вызван"); - android.util.Log.d("YandexMapImpl", "mapObjects: " + (mapObjects != null ? "установлен" : "null")); - android.util.Log.d("YandexMapImpl", "mapView: " + (mapView != null ? "установлен" : "null")); - android.util.Log.d("YandexMapImpl", "context: " + (context != null ? "установлен" : "null")); - - // Карта уже инициализирована в конструкторе - if (mapObjects != null) { - android.util.Log.d("YandexMapImpl", "Коллекция объектов карты готова к использованию"); - } + // Инициализируем слушатель поворота карты + setupCameraListener(); } @Override public void cleanup() { + // Удаляем слушатель ввода + if (inputListener != null && mapView != null) { + mapView.getMap().removeInputListener(inputListener); + } + if (mapObjects != null) { mapView.getMap().getMapObjects().remove(mapObjects); } @@ -83,151 +87,123 @@ public class YandexMapImpl implements MapInterface { @Override public void addOwnVesselMarker(Vessel vessel) { - android.util.Log.d("YandexMapImpl", "addOwnVesselMarker вызван: lat=" + vessel.getLatitude() + ", lon=" + vessel.getLongitude() + ", course=" + vessel.getCourse() + "°"); - // Сохраняем ссылку на судно this.ownVessel = vessel; - // Проверяем координаты - if (vessel.getLatitude() == 0.0 && vessel.getLongitude() == 0.0) { - android.util.Log.w("YandexMapImpl", "Координаты равны 0,0 - маркер не будет создан"); + // Проверяем валидность координат (исключаем только невалидные значения) + if (Double.isNaN(vessel.getLatitude()) || Double.isNaN(vessel.getLongitude()) || + Double.isInfinite(vessel.getLatitude()) || Double.isInfinite(vessel.getLongitude())) { return; } if (ownVesselMarker != null) { - android.util.Log.d("YandexMapImpl", "Удаляем существующий маркер"); mapObjects.remove(ownVesselMarker); } Point point = new Point(vessel.getLatitude(), vessel.getLongitude()); - android.util.Log.d("YandexMapImpl", "Создаем Point: " + point); - ownVesselMarker = mapObjects.addPlacemark(point); - android.util.Log.d("YandexMapImpl", "Placemark создан: " + (ownVesselMarker != null ? "успешно" : "null")); if (ownVesselMarker == null) { - android.util.Log.e("YandexMapImpl", "Не удалось создать Placemark!"); return; } - // Используем готовую иконку стрелки с учетом курса - android.util.Log.d("YandexMapImpl", "Устанавливаем иконку стрелки с курсом: " + vessel.getCourse() + "°"); - setMarkerIcon(ownVesselMarker, "arrowship", vessel.getCourse()); + // Используем готовую иконку стрелки с учетом курса (синий цвет для нашего судна) + setMarkerIcon(ownVesselMarker, "target", vessel.getCourse(), android.graphics.Color.BLUE); // Устанавливаем размер иконки - android.util.Log.d("YandexMapImpl", "Устанавливаем IconStyle..."); com.yandex.mapkit.map.IconStyle iconStyle = new com.yandex.mapkit.map.IconStyle(); - iconStyle.setScale(1.5f); // Увеличиваем размер иконки + iconStyle.setScale(1.0f); // Уменьшаем масштаб иконки ownVesselMarker.setIconStyle(iconStyle); // Устанавливаем обработчик кликов только если он еще не установлен if (!ownVesselClickListenerSet) { - android.util.Log.d("YandexMapImpl", "Устанавливаем обработчик клика для маркера..."); ownVesselMarker.addTapListener((mapObject, point1) -> { - android.util.Log.d("YandexMapImpl", "Клик по маркеру нашего судна!"); if (markerClickListener != null && ownVessel != null) { - android.util.Log.d("YandexMapImpl", "Вызываем callback onOwnVesselClick"); markerClickListener.onOwnVesselClick(ownVessel); - } else { - android.util.Log.e("YandexMapImpl", "markerClickListener == null или ownVessel == null!"); - android.util.Log.d("YandexMapImpl", "markerClickListener = " + (markerClickListener != null ? "установлен" : "null")); - android.util.Log.d("YandexMapImpl", "ownVessel = " + (ownVessel != null ? "установлен" : "null")); } return true; }); ownVesselClickListenerSet = true; } - - android.util.Log.d("YandexMapImpl", "Маркер нашего судна создан и настроен, markerClickListener = " + (markerClickListener != null ? "установлен" : "null")); - - // Проверяем, что маркер действительно добавлен в коллекцию - android.util.Log.d("YandexMapImpl", "Маркер добавлен в коллекцию объектов карты"); } @Override public void updateOwnVesselPosition(Vessel vessel) { - android.util.Log.d("YandexMapImpl", "updateOwnVesselPosition вызван: lat=" + vessel.getLatitude() + ", lon=" + vessel.getLongitude() + ", course=" + vessel.getCourse() + "°"); - // Обновляем ссылку на судно this.ownVessel = vessel; - // Проверяем координаты - if (vessel.getLatitude() == 0.0 && vessel.getLongitude() == 0.0) { - android.util.Log.w("YandexMapImpl", "Координаты равны 0,0 - обновление пропущено"); + // Проверяем валидность координат (исключаем только невалидные значения) + if (Double.isNaN(vessel.getLatitude()) || Double.isNaN(vessel.getLongitude()) || + Double.isInfinite(vessel.getLatitude()) || Double.isInfinite(vessel.getLongitude())) { return; } if (ownVesselMarker == null) { // Создаем маркер нашего судна, если его еще нет - android.util.Log.d("YandexMapImpl", "Создаем новый маркер нашего судна"); addOwnVesselMarker(vessel); } else { - // Проверяем, нужно ли обновить курс - boolean needCourseUpdate = Math.abs(vessel.getCourse()) > 0.1; // Если курс больше 0.1 градуса - - if (needCourseUpdate) { - android.util.Log.d("YandexMapImpl", "Обновляем курс маркера на " + vessel.getCourse() + "°"); - // Обновляем только иконку с новым курсом - setMarkerIcon(ownVesselMarker, "arrowship", vessel.getCourse()); - } + // Всегда обновляем иконку с текущим курсом + // Обновляем иконку с новым курсом (синий цвет для нашего судна) + setMarkerIcon(ownVesselMarker, "target", vessel.getCourse(), android.graphics.Color.BLUE); // Обновляем позицию маркера Point newPoint = new Point(vessel.getLatitude(), vessel.getLongitude()); ownVesselMarker.setGeometry(newPoint); - android.util.Log.d("YandexMapImpl", "Позиция маркера обновлена на: " + newPoint); - // Переустанавливаем обработчик клика после обновления маркера - if (markerClickListener != null) { - android.util.Log.d("YandexMapImpl", "Переустанавливаем обработчик клика после обновления маркера"); - // В Яндекс.Картах нет метода setTapListener(null), поэтому просто добавляем новый обработчик - ownVesselMarker.addTapListener((mapObject, point1) -> { - android.util.Log.d("YandexMapImpl", "Клик по маркеру нашего судна!"); - if (markerClickListener != null && ownVessel != null) { - android.util.Log.d("YandexMapImpl", "Вызываем callback onOwnVesselClick"); - markerClickListener.onOwnVesselClick(ownVessel); - } else { - android.util.Log.e("YandexMapImpl", "markerClickListener == null или ownVessel == null!"); - } - return true; - }); + // Обработчик клика уже установлен при создании маркера, не добавляем повторно } - } - - android.util.Log.d("YandexMapImpl", "Маркер нашего судна обновлен, ownVesselMarker = " + (ownVesselMarker != null ? "создан" : "null") + ", markerClickListener = " + (markerClickListener != null ? "установлен" : "null")); } @Override public void addAISVesselMarker(AISVessel vessel) { - android.util.Log.d("YandexMapImpl", "addAISVesselMarker вызван: lat=" + vessel.getLatitude() + ", lon=" + vessel.getLongitude() + ", course=" + vessel.getCourse() + "°"); + // Проверяем валидность координат (исключаем только невалидные значения) + if (Double.isNaN(vessel.getLatitude()) || Double.isNaN(vessel.getLongitude()) || + Double.isInfinite(vessel.getLatitude()) || Double.isInfinite(vessel.getLongitude())) { + return; + } + Point point = new Point(vessel.getLatitude(), vessel.getLongitude()); com.yandex.mapkit.map.PlacemarkMapObject marker = mapObjects.addPlacemark(point); + if (marker == null) { + return; + } + // Сохраняем ссылку на судно aisVessels.put(vessel.getMmsi(), vessel); - // Используем готовую иконку стрелки для AIS судов с учетом курса - setMarkerIcon(marker, "arrowship", vessel.getCourse()); + // Используем готовую иконку стрелки для AIS судов с учетом курса и цвета + int vesselColor = getVesselColor(vessel); + setMarkerIcon(marker, "target", vessel.getCourse(), vesselColor, vessel.isSelected()); // Устанавливаем размер иконки com.yandex.mapkit.map.IconStyle iconStyle = new com.yandex.mapkit.map.IconStyle(); - iconStyle.setScale(1.5f); // Увеличиваем размер иконки + iconStyle.setScale(1.0f); // Уменьшаем масштаб иконки marker.setIconStyle(iconStyle); // Установка обработчика кликов только если он еще не установлен String mmsi = vessel.getMmsi(); - if (!aisVesselClickListenersSet.containsKey(mmsi) || !aisVesselClickListenersSet.get(mmsi)) { + if (!aisVesselClickListenersAdded.containsKey(mmsi) || !aisVesselClickListenersAdded.get(mmsi)) { marker.addTapListener((mapObject, point1) -> { - android.util.Log.d("YandexMapImpl", "Клик по AIS маркеру: " + mmsi); - if (markerClickListener != null) { - android.util.Log.d("YandexMapImpl", "Вызываем callback onAISVesselClick"); - markerClickListener.onAISVesselClick(vessel); + // Проверяем двойной клик + long currentTime = System.currentTimeMillis(); + Long lastTime = lastClickTime.get(mmsi); + + if (lastTime != null && (currentTime - lastTime) < DOUBLE_CLICK_DELAY) { + // Двойной клик - вызываем BottomSheet + if (markerClickListener != null) { + markerClickListener.onAISVesselClick(vessel); + } + lastClickTime.remove(mmsi); // Сбрасываем для следующего двойного клика } else { - android.util.Log.e("YandexMapImpl", "markerClickListener == null!"); - android.util.Log.d("YandexMapImpl", "markerClickListener = " + (markerClickListener != null ? "установлен" : "null")); + // Одиночный клик - выделяем/снимаем выделение + toggleVesselSelection(vessel); + lastClickTime.put(mmsi, currentTime); } + return true; }); - aisVesselClickListenersSet.put(mmsi, true); + aisVesselClickListenersAdded.put(mmsi, true); } aisMarkers.put(vessel.getMmsi(), marker); @@ -243,27 +219,11 @@ public class YandexMapImpl implements MapInterface { Point newPoint = new Point(vessel.getLatitude(), vessel.getLongitude()); marker.setGeometry(newPoint); - // Обновляем курс маркера, если он изменился - if (Math.abs(vessel.getCourse()) > 0.1) { - android.util.Log.d("YandexMapImpl", "Обновляем курс AIS маркера " + vessel.getMmsi() + " на " + vessel.getCourse() + "°"); - setMarkerIcon(marker, "arrowship", vessel.getCourse()); - } + // Всегда обновляем курс маркера + int vesselColor = getVesselColor(vessel); + setMarkerIcon(marker, "target", vessel.getCourse(), vesselColor, vessel.isSelected()); - // Переустанавливаем обработчик клика после обновления маркера - if (markerClickListener != null) { - android.util.Log.d("YandexMapImpl", "Переустанавливаем обработчик клика для AIS маркера: " + vessel.getMmsi()); - // В Яндекс.Картах нет метода setTapListener(null), поэтому просто добавляем новый обработчик - marker.addTapListener((mapObject, point1) -> { - android.util.Log.d("YandexMapImpl", "Клик по AIS маркеру: " + vessel.getMmsi()); - if (markerClickListener != null) { - android.util.Log.d("YandexMapImpl", "Вызываем callback onAISVesselClick"); - markerClickListener.onAISVesselClick(vessel); - } else { - android.util.Log.e("YandexMapImpl", "markerClickListener == null!"); - } - return true; - }); - } + // Обработчик клика уже установлен при создании маркера, не добавляем повторно } } @@ -275,8 +235,9 @@ public class YandexMapImpl implements MapInterface { } // Удаляем ссылку на судно aisVessels.remove(mmsi); - // Удаляем флаг обработчика кликов + // Удаляем флаги обработчиков кликов aisVesselClickListenersSet.remove(mmsi); + aisVesselClickListenersAdded.remove(mmsi); } @Override @@ -287,6 +248,7 @@ public class YandexMapImpl implements MapInterface { aisMarkers.clear(); aisVessels.clear(); aisVesselClickListenersSet.clear(); + aisVesselClickListenersAdded.clear(); } @Override @@ -322,7 +284,6 @@ public class YandexMapImpl implements MapInterface { @Override public void setMarkerClickListener(MarkerClickListener listener) { - android.util.Log.d("YandexMapImpl", "setMarkerClickListener вызван: " + (listener != null ? "listener установлен" : "listener == null")); this.markerClickListener = listener; // Переустанавливаем обработчики кликов для всех существующих маркеров @@ -334,46 +295,108 @@ public class YandexMapImpl implements MapInterface { * Этот метод переустанавливает обработчики для всех маркеров */ private void updateAllMarkerClickListeners() { - android.util.Log.d("YandexMapImpl", "updateAllMarkerClickListeners вызван - переустанавливаем обработчики"); + // Обработчик для маркера нашего судна уже установлен при создании, не добавляем повторно - // Переустанавливаем обработчик для маркера нашего судна - if (ownVesselMarker != null) { - android.util.Log.d("YandexMapImpl", "Переустанавливаем обработчик для маркера нашего судна"); - // В Яндекс.Картах нет метода setTapListener(null), поэтому просто добавляем новый обработчик - ownVesselMarker.addTapListener((mapObject, point1) -> { - android.util.Log.d("YandexMapImpl", "Клик по маркеру нашего судна!"); - if (markerClickListener != null && ownVessel != null) { - android.util.Log.d("YandexMapImpl", "Вызываем callback onOwnVesselClick"); - markerClickListener.onOwnVesselClick(ownVessel); - } else { - android.util.Log.e("YandexMapImpl", "markerClickListener == null или ownVessel == null!"); - } - return true; - }); - ownVesselClickListenerSet = true; - } - - // Переустанавливаем обработчики для AIS маркеров - for (Map.Entry entry : aisMarkers.entrySet()) { - String mmsi = entry.getKey(); - com.yandex.mapkit.map.PlacemarkMapObject marker = entry.getValue(); - AISVessel vessel = aisVessels.get(mmsi); - - if (marker != null && vessel != null) { - android.util.Log.d("YandexMapImpl", "Переустанавливаем обработчик для AIS маркера: " + mmsi); - // В Яндекс.Картах нет метода setTapListener(null), поэтому просто добавляем новый обработчик - marker.addTapListener((mapObject, point1) -> { - android.util.Log.d("YandexMapImpl", "Клик по AIS маркеру: " + mmsi); - if (markerClickListener != null) { - android.util.Log.d("YandexMapImpl", "Вызываем callback onAISVesselClick"); - markerClickListener.onAISVesselClick(vessel); - } else { - android.util.Log.e("YandexMapImpl", "markerClickListener == null!"); - } - return true; - }); - aisVesselClickListenersSet.put(mmsi, true); + // Обработчики для AIS маркеров уже установлены при создании, не добавляем повторно + } + + /** + * Создание повернутой цветной иконки из ресурса + */ + private Bitmap createRotatedIconFromResource(int resourceId, double course, int color) { + return createRotatedIconFromResource(resourceId, course, color, false); + } + + /** + * Создание повернутой цветной иконки из ресурса с поддержкой выделения + */ + private Bitmap createRotatedIconFromResource(int resourceId, double course, int color, boolean isSelected) { + try { + // Получаем drawable из ресурса + Drawable drawable = context.getResources().getDrawable(resourceId, null); + if (drawable == null) { + return null; } + + // Применяем цвет к drawable + if (color != 0) { + drawable.setColorFilter(color, android.graphics.PorterDuff.Mode.SRC_IN); + } + + // Получаем оригинальные размеры drawable + int originalWidth = drawable.getIntrinsicWidth(); + int originalHeight = drawable.getIntrinsicHeight(); + + // Если размеры не определены, используем стандартные + if (originalWidth <= 0) originalWidth = 32; + if (originalHeight <= 0) originalHeight = 48; + + // Масштабируем размеры (уменьшаем еще больше) + float scale = 0.3f; // Уменьшаем в 3.3 раза + int width = (int) (originalWidth * scale); + int height = (int) (originalHeight * scale); + + // Получаем азимут карты (поворот карты) + float mapAzimuth = 0.0f; + try { + CameraPosition cameraPosition = mapView.getMap().getCameraPosition(); + mapAzimuth = cameraPosition.getAzimuth(); + } catch (Exception e) { + // Не удалось получить азимут карты + } + + // Создаем bitmap минимального размера для уменьшения хитбокса + int bitmapSize = Math.max(width, height) + 8; // Добавляем только небольшой отступ + Bitmap bitmap = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + + // Поворачиваем маркер на курс судна с учетом поворота карты + // Курс судна - это направление относительно севера + // Азимут карты - это поворот карты относительно севера + // Итоговый поворот = курс судна - азимут карты (чтобы маркер оставался относительно севера) + float rotationAngle = (float) (course - mapAzimuth); + + // Центрируем drawable в bitmap + int centerX = bitmapSize / 2; + int centerY = bitmapSize / 2; + int left = centerX - width / 2; + int top = centerY - height / 2; + + // Устанавливаем границы для drawable + drawable.setBounds(left, top, left + width, top + height); + + // Поворачиваем canvas на курс + canvas.save(); + canvas.rotate(rotationAngle, centerX, centerY); + + // Рисуем drawable + drawable.draw(canvas); + + canvas.restore(); + + // Если судно выделено, добавляем рамку выделения + if (isSelected) { + // Получаем drawable для рамки выделения + Drawable selectionDrawable = context.getResources().getDrawable( + context.getResources().getIdentifier("chosentarget", "drawable", context.getPackageName()), null); + + if (selectionDrawable != null) { + // Масштабируем рамку выделения + int selectionSize = Math.max(width, height) + 16; // Рамка немного больше + int selectionLeft = centerX - selectionSize / 2; + int selectionTop = centerY - selectionSize / 2; + + selectionDrawable.setBounds(selectionLeft, selectionTop, + selectionLeft + selectionSize, selectionTop + selectionSize); + + // Рисуем рамку выделения + selectionDrawable.draw(canvas); + } + } + + return bitmap; + } catch (Exception e) { + return null; } } @@ -410,10 +433,8 @@ public class YandexMapImpl implements MapInterface { canvas.drawPath(path, paint); canvas.restore(); - android.util.Log.d("YandexMapImpl", "Программная иконка с курсом " + course + "° создана успешно, размер: " + size + "x" + size); return bitmap; } catch (Exception e) { - android.util.Log.e("YandexMapImpl", "Ошибка создания программной иконки: " + e.getMessage(), e); return null; } } @@ -429,7 +450,6 @@ public class YandexMapImpl implements MapInterface { * Принудительно пересоздает маркер нашего судна с иконкой */ public void recreateOwnVesselMarker(Vessel vessel) { - android.util.Log.d("YandexMapImpl", "Принудительно пересоздаем маркер нашего судна"); if (ownVesselMarker != null) { mapObjects.remove(ownVesselMarker); ownVesselMarker = null; @@ -442,7 +462,6 @@ public class YandexMapImpl implements MapInterface { * Вызывается после закрытия BottomSheet для восстановления функциональности */ public void refreshMarkerClickListeners() { - android.util.Log.d("YandexMapImpl", "refreshMarkerClickListeners вызван - переустанавливаем все обработчики"); updateAllMarkerClickListeners(); } @@ -450,84 +469,143 @@ public class YandexMapImpl implements MapInterface { * Устанавливает иконку для маркера с fallback */ private void setMarkerIcon(com.yandex.mapkit.map.PlacemarkMapObject marker, String iconName, double course) { + setMarkerIcon(marker, iconName, course, 0); // По умолчанию без цвета + } + + /** + * Устанавливает цветную иконку для маркера с fallback + */ + private void setMarkerIcon(com.yandex.mapkit.map.PlacemarkMapObject marker, String iconName, double course, int color) { + setMarkerIcon(marker, iconName, course, color, false); + } + + /** + * Устанавливает цветную иконку для маркера с поддержкой выделения + */ + private void setMarkerIcon(com.yandex.mapkit.map.PlacemarkMapObject marker, String iconName, double course, int color, boolean isSelected) { try { - android.util.Log.d("YandexMapImpl", "Пытаемся установить иконку: " + iconName + " с курсом: " + course + "°"); - android.util.Log.d("YandexMapImpl", "Package name: " + context.getPackageName()); - - // Сначала пробуем создать программную иконку с учетом курса - android.util.Log.d("YandexMapImpl", "Создаем программную иконку стрелки с курсом " + course + "°..."); - Bitmap iconBitmap = createVesselIcon(android.graphics.Color.BLUE, course); - if (iconBitmap != null) { - android.util.Log.d("YandexMapImpl", "Программная иконка с курсом " + course + "° создана, устанавливаем..."); - marker.setIcon(ImageProvider.fromBitmap(iconBitmap)); - android.util.Log.d("YandexMapImpl", "Программная иконка с курсом " + course + "° установлена успешно"); - return; - } - - // Если программная иконка не создалась, пробуем ресурс + // Сначала пробуем использовать ресурс с поворотом int iconResId = context.getResources().getIdentifier(iconName, "drawable", context.getPackageName()); - android.util.Log.d("YandexMapImpl", "ID ресурса " + iconName + ": " + iconResId); if (iconResId != 0) { - android.util.Log.d("YandexMapImpl", "Устанавливаем иконку из ресурса..."); - marker.setIcon(ImageProvider.fromResource(context, iconResId)); - android.util.Log.d("YandexMapImpl", "Иконка " + iconName + " установлена успешно"); + Bitmap rotatedBitmap = createRotatedIconFromResource(iconResId, course, color, isSelected); + if (rotatedBitmap != null) { + marker.setIcon(ImageProvider.fromBitmap(rotatedBitmap)); + return; + } else { + marker.setIcon(ImageProvider.fromResource(context, iconResId)); + return; + } + } + + // Если ресурс не найден, создаем программную иконку с учетом курса + Bitmap iconBitmap = createVesselIcon(android.graphics.Color.BLUE, course); + if (iconBitmap != null) { + marker.setIcon(ImageProvider.fromBitmap(iconBitmap)); } else { - android.util.Log.e("YandexMapImpl", "Не удалось найти ресурс " + iconName); - android.util.Log.d("YandexMapImpl", "Используем fallback иконку..."); // Создаем простую иконку как fallback marker.setIcon(ImageProvider.fromResource(context, android.R.drawable.ic_menu_compass)); - android.util.Log.d("YandexMapImpl", "Fallback иконка установлена"); } } catch (Exception e) { - android.util.Log.e("YandexMapImpl", "Ошибка установки иконки " + iconName + ": " + e.getMessage(), e); - android.util.Log.d("YandexMapImpl", "Используем fallback иконку после ошибки..."); // Создаем простую иконку как fallback marker.setIcon(ImageProvider.fromResource(context, android.R.drawable.ic_menu_compass)); - android.util.Log.d("YandexMapImpl", "Fallback иконка установлена после ошибки"); } // После установки иконки проверяем, что обработчик клика все еще работает // Это может помочь с проблемами, когда установка иконки нарушает обработчики - android.util.Log.d("YandexMapImpl", "Иконка установлена, проверяем обработчик клика..."); - // Дополнительная проверка: если это маркер нашего судна, переустанавливаем обработчик клика - if (marker == ownVesselMarker && markerClickListener != null) { - android.util.Log.d("YandexMapImpl", "Переустанавливаем обработчик клика для маркера нашего судна после установки иконки"); - // В Яндекс.Картах нет метода setTapListener(null), поэтому просто добавляем новый обработчик - marker.addTapListener((mapObject, point1) -> { - android.util.Log.d("YandexMapImpl", "Клик по маркеру нашего судна!"); - if (markerClickListener != null && ownVessel != null) { - android.util.Log.d("YandexMapImpl", "Вызываем callback onOwnVesselClick"); - markerClickListener.onOwnVesselClick(ownVessel); - } else { - android.util.Log.e("YandexMapImpl", "markerClickListener == null или ownVessel == null!"); + // Обработчик клика для нашего судна уже установлен при создании маркера, не добавляем повторно + + // Обработчики кликов уже установлены при создании маркеров, не добавляем повторно + } + + /** + * Настройка слушателя поворота карты + */ + 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) { + // Не обрабатываем клики по карте } - return true; - }); + + @Override + public void onMapLongTap(com.yandex.mapkit.map.Map map, com.yandex.mapkit.geometry.Point point) { + // Не обрабатываем долгие клики по карте + } + }; + + // Добавляем слушатель к карте + mapView.getMap().addInputListener(inputListener); + + // Включаем жесты поворота карты + mapView.getMap().setRotateGesturesEnabled(true); + } catch (Exception e) { + // Ошибка установки слушателя + } + } + + /** + * Перерисовывает все маркеры с учетом текущего азимута карты + * Вызывается при повороте карты + */ + public void refreshAllMarkers() { + // Перерисовываем маркер нашего судна (синий цвет) + if (ownVesselMarker != null && ownVessel != null) { + setMarkerIcon(ownVesselMarker, "target", ownVessel.getCourse(), android.graphics.Color.BLUE); } - // Дополнительная проверка: если это AIS маркер, переустанавливаем обработчик клика + // Перерисовываем все AIS маркеры с их цветами for (Map.Entry entry : aisMarkers.entrySet()) { - if (entry.getValue() == marker && markerClickListener != null) { - String mmsi = entry.getKey(); - AISVessel vessel = aisVessels.get(mmsi); - if (vessel != null) { - android.util.Log.d("YandexMapImpl", "Переустанавливаем обработчик клика для AIS маркера " + mmsi + " после установки иконки"); - // В Яндекс.Картах нет метода setTapListener(null), поэтому просто добавляем новый обработчик - marker.addTapListener((mapObject, point1) -> { - android.util.Log.d("YandexMapImpl", "Клик по AIS маркеру: " + mmsi); - if (markerClickListener != null) { - android.util.Log.d("YandexMapImpl", "Вызываем callback onAISVesselClick"); - markerClickListener.onAISVesselClick(vessel); - } else { - android.util.Log.e("YandexMapImpl", "markerClickListener == null!"); - } - return true; - }); - } - break; + String mmsi = entry.getKey(); + com.yandex.mapkit.map.PlacemarkMapObject marker = entry.getValue(); + AISVessel vessel = aisVessels.get(mmsi); + + if (marker != null && vessel != null) { + int vesselColor = getVesselColor(vessel); + setMarkerIcon(marker, "target", vessel.getCourse(), vesselColor, vessel.isSelected()); } } } + + /** + * Переключает выделение AIS судна + */ + private void toggleVesselSelection(AISVessel vessel) { + vessel.setSelected(!vessel.isSelected()); + + // Обновляем иконку маркера с учетом состояния выделения + com.yandex.mapkit.map.PlacemarkMapObject marker = aisMarkers.get(vessel.getMmsi()); + if (marker != null) { + int vesselColor = getVesselColor(vessel); + setMarkerIcon(marker, "target", vessel.getCourse(), vesselColor, vessel.isSelected()); + } + } + + /** + * Получает цвет для AIS судна в зависимости от его статуса + */ + private int getVesselColor(AISVessel vessel) { + // Можно настроить цвета в зависимости от параметров судна + // Используем navigation status из AIS данных + String navStatus = vessel.getNavigationalStatus(); + if (navStatus != null) { + switch (navStatus.toLowerCase()) { + case "under way using engine": + case "under way": + return android.graphics.Color.GREEN; + case "at anchor": + return android.graphics.Color.YELLOW; + case "moored": + return android.graphics.Color.BLUE; + case "not under command": + case "restricted manoeuvrability": + return android.graphics.Color.RED; + default: + return android.graphics.Color.WHITE; + } + } + return android.graphics.Color.WHITE; // По умолчанию белый + } } diff --git a/app/src/main/java/com/grigowashere/aismap/models/AISVessel.java b/app/src/main/java/com/grigowashere/aismap/models/AISVessel.java index 0fcd8f7..9c00826 100644 --- a/app/src/main/java/com/grigowashere/aismap/models/AISVessel.java +++ b/app/src/main/java/com/grigowashere/aismap/models/AISVessel.java @@ -29,6 +29,7 @@ public class AISVessel { private boolean positionAccuracy; // точность позиции private String vesselClass; // класс судна (Class A, Class B, Extended Class B) private String vendorId; // идентификатор производителя оборудования + private boolean selected; // выделено ли судно на карте public AISVessel() { this.lastUpdate = LocalDateTime.now(); @@ -110,6 +111,9 @@ public class AISVessel { public String getVendorId() { return vendorId; } public void setVendorId(String vendorId) { this.vendorId = vendorId; } + public boolean isSelected() { return selected; } + public void setSelected(boolean selected) { this.selected = selected; } + /** * Обновляет позицию и курс судна */ diff --git a/app/src/main/res/drawable/chosentarget.xml b/app/src/main/res/drawable/chosentarget.xml new file mode 100644 index 0000000..3aeaaab --- /dev/null +++ b/app/src/main/res/drawable/chosentarget.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/losingtarget.xml b/app/src/main/res/drawable/losingtarget.xml new file mode 100644 index 0000000..8a426c7 --- /dev/null +++ b/app/src/main/res/drawable/losingtarget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/main/res/drawable/scaletarget.xml b/app/src/main/res/drawable/scaletarget.xml new file mode 100644 index 0000000..913d715 --- /dev/null +++ b/app/src/main/res/drawable/scaletarget.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/target.xml b/app/src/main/res/drawable/target.xml new file mode 100644 index 0000000..7da4108 --- /dev/null +++ b/app/src/main/res/drawable/target.xml @@ -0,0 +1,11 @@ + + +