Увеличение, перемещение, поворот или наклон карты в приложении «Карты» на Mac


В приложении «Карты» на Mac выполните любое из указанных действий.
- Увеличение и уменьшение масштаба. Нажимайте кнопки увеличения и уменьшения (
и
) в нижнем правом углу карты. Чтобы видеть значение масштаба карты во время увеличения или уменьшения изображения, выберите меню «Вид» > «Показать масштаб карты». Масштаб отображается в левом верхнем углу. - Перемещение вверх, вниз, влево или вправо. Удерживая нажатой кнопку мыши или трекпада, перетяните карту. На трекпаде можно также перетягивать карту двумя пальцами.
- Поворот карты. Удерживая нажатым указатель на компасе, перетягивайте его влево, вправо, вверх или вниз. После поворота карты можно вернуться к ориентации на север, нажав на компас или нажав сочетание клавиш Shift-Command-стрелка вверх.
- Наклон карты. Нажмите кнопку «3D» в панели инструментов, затем перетяните бегунок в правом нижнем углу.
- Просмотр в виде интерактивного трехмерного глобуса. Если у Вас Mac с чипом Apple, уменьшайте масштаб, пока карта не сменится глобусом. Чтобы повернуть глобус, потяните за него, или меняйте масштаб, чтобы изучать горные хребты, пустыни, океаны и другие объекты.
См. такжеНастройка оформления карты в приложении «Карты» на Mac
«Яндекс. Карты» перешли на вектор

Сегодня вышли новые версии мобильных приложений «Яндекс.Карты» для iOS и Android.
Самое главное нововведение, что карты всех регионов заметно уменьшились в размере. Например, офлайн-карта Москвы теперь занимает всего 144 МБ вместо 1,9 ГБ, офлайн-карта Санкт-Петербурга и области — 231 МБ вместо 3,2 ГБ.
Уменьшение размера достигнуто за счёт того, что «Яндекс» наконец-то перешёл с растровых карт на векторные, так что теперь не нужно сохранять несколько вариантов одной и той же карты в разных масштабах.
Кроме того, в новой версии карт реализован офлайновый поиск — то, о чём давно просили пользователи. На картах без подключения к интернету можно искать адреса и организации: кафе, магазины, банкоматы, аптеки, АЗС и проч. Карты покажут расписание транспорта, адрес организации и сообщат подробности: время работы и телефон. Для гостиниц указана примерная стоимость номеров, для ресторанов — средний чек.

Разработчики пишут, что добавили ещё несколько функций: во-первых, изменение угла наклона карты (нужно провести двумя пальцами по экрану), во-вторых, постоянное отображение на видном месте названий улиц, даже если включен режим пробок или проложен маршрут.
- Яндекс.Карты
- векторная графика
- средний чек
Yandex MapKit для новичков: разрабатываем карты в Android-приложении

Yandex MapKit — это кроссплатформенная библиотека, позволяющая использовать картографические данные и технологии Яндекса в мобильных приложениях. Список доступных возможностей действительно впечатляет, но разработчику, впервые столкнувшемуся с необходимостью работать с Яндекс-картами, многое может показаться непонятным и неочевидным в использовании.
Поэтому, чтобы научиться применять полезные особенности MapKit’a, мы с вами напишем небольшое приложение, в которое внедрим и настроим данную библиотеку: откроем определённую область на карте; выставим метку в нужном месте; установим на неё желаемые растровые и векторные изображения; поиграемся с зумом; обработаем нажатие на пин; а также будем при клике визуально выделять объекты на карте и получать от них интересующую нас информацию.
1) Введение: внедрение и настройка Yandex MapKit в проекте
Чтобы создать и запустить приложение с Яндекс-картами, первым делом необходимо:
- Получить ключ;
- Установить библиотеку MapKit;
- Настроить библиотеку;
- Собрать и запустить приложение.
Эти пункты весьма понятно и доступно описаны в документации. Дабы не копировать информацию, читателям предлагается самим ознакомиться, повторить данные шаги и затем запустить приложение с Яндекс-картой. Отметим, что в нашем приложении мы будем использовать полную full-версию библиотеки (на момент написания статьи — версия 4.3.1-full), а писать код на языке Kotlin, используя View Binding.
Отметим несколько важных моментов:
- Какими знаниями вы должны обладать: Kotlin базовый уровень; умение собрать проект, запустить приложение на эмуляторе или телефоне, загрузить необходимые библиотеки; View Binding.
- Ознакомьтесь с условиями использования MapKit. Так, например, нельзя скрывать логотип Яндекса на карте за другими объектами. Также, в вашем приложении в разделе «о программе» должна быть ссылка на условия использования Яндекс-карт.
- API-ключ должен быть задан единожды перед инициализацией MapKitFactory. Хорошим тоном будет задать ключ при запуске приложения в методе Application.onCreate() , а инициализировать уже в других необходимых активити и фрагментах. Если же при каких-то условиях будет повторно вызван MapKitFactory.setApiKey(«Ваш API-ключ»), вы получите краш приложения и ошибку в логах: «java.lang.AssertionError: You need to set the API key before using MapKit!». Примером появления подобной ошибки может быть следующий сценарий: переход с одного экрана во фрагмент с Яндекс-картами, где мы задаём API-ключ (карта при этом откроется и будет адекватно работать) -> возвращаемся на предыдущий экран -> вновь открываем фрагмент с картами -> происходит краш приложения.
- Допущения в данном проекте: в случае, если по каким-то причинам (как, например, в нашем приложении), логика работы карт и API-ключ находятся в одном активити/фрагменте, раздувать макет необходимо только после того, как установлен API-ключ. Иначе, проявится ошибка, указанная в предыдущем пункте. Также необходимо учесть момент пересоздания активити/фрагмента, например, для случая изменения ориентации экрана, вследствие чего вновь будет вызван метод MapKitFactory.setApiKey(«Ваш API-ключ»). Воспользуемся проверкой: установили ли мы ранее API-ключ для Яндекс-карт. Для этого сохраним данную информацию:
override fun onSaveInstanceState(outState: Bundle)
А при создании активности, в методе onCreate будем проверять, был ли уже установлен API-ключ ранее при помощи функции setApiKey:
private fun setApiKey(savedInstanceState: Bundle?) < val haveApiKey = savedInstanceState?.getBoolean("haveApiKey") ?: false // При первом запуске приложения всегда false if (!haveApiKey) < MapKitFactory.setApiKey(MAPKIT_API_KEY)>> // API-ключ должен быть задан единожды перед инициализацией MapKitFactory
Что же, надеюсь, у вас получилось собрать проект со своей первой Яндекс-картой и перед вами на экране отобразились материки, моря да океаны:

А код выглядит примерно следующим образом:
class MainActivity : AppCompatActivity() < private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) < super.onCreate(savedInstanceState) setApiKey(savedInstanceState) // Проверяем: был ли уже ранее установлен API-ключ в приложении. Если нет - устанавливаем его. MapKitFactory.initialize(this) // Инициализация библиотеки для загрузки необходимых нативных библиотек. binding = ActivityMainBinding.inflate(layoutInflater) // Раздуваем макет только после того, как установили API-ключ setContentView(binding.root) // Размещаем пользовательский интерфейс в экране активности >private fun setApiKey(savedInstanceState: Bundle?) < val haveApiKey = savedInstanceState?.getBoolean("haveApiKey") ?: false // При первом запуске приложения всегда false if (!haveApiKey) < MapKitFactory.setApiKey(MAPKIT_API_KEY) // API-ключ должен быть задан единожды перед инициализацией MapKitFactory >> // Если Activity уничтожается (например, при нехватке памяти или при повороте экрана) - сохраняем информацию, что API-ключ уже был получен ранее override fun onSaveInstanceState(outState: Bundle) < super.onSaveInstanceState(outState) outState.putBoolean("haveApiKey", true) >// Отображаем карты перед моментом, когда активити с картой станет видимой пользователю: override fun onStart() < super.onStart() MapKitFactory.getInstance().onStart() binding.mapview.onStart() >// Останавливаем обработку карты, когда активити с картой становится невидимым для пользователя: override fun onStop() < binding.mapview.onStop() MapKitFactory.getInstance().onStop() super.onStop() >companion object < const val MAPKIT_API_KEY = "Ваш API-ключ" >>
Соответствующая разметка .xml:
Теперь, когда все готово, перейдём к ещё более интересным и полезным моментам – интерактивам с картой. Пример полного кода будет приведён в конце статьи, а также его можно найти в GitHub.
2) Открываем определённую область на карте
Первым делом сделаем так, чтобы при открытии карты нам сразу показывалась определённая область. Здесь и далее, при необходимости импорта, выбираем библиотеку Яндекса. Добавим новые переменные: startLocation – точку, содержащую координаты (широту и долготу), в которую должна переместиться камера и zoomValue – величину приближения к данной точке:
private val startLocation = Point(59.9402, 30.315) private var zoomValue: Float = 16.5f
Создадим функцию moveToStartLocation() , в которой у нашей mapview для карты вызовем метод, перемещающий камеру к необходимой позиции:
private fun moveToStartLocation()
Конструктор класса CameraPosition принимает, соответственно: точку с координатами, величину необходимого приближения, азимут и наклон (наклон камеры в градусах, добавляет визуально ощущение 3d). Максимальное возможное значение зума – 21.0f, минимальное – 0.0f. Также можем вызвать перегруженный метод move, чтобы перемещение в нужную область происходило красиво со стартовой анимацией:
binding.mapview.map.move( CameraPosition(startLocation, zoomValue, 0.0f, 0.0f), Animation(SMOOTH, 5f), null)
Здесь, помимо CameraPosition, дополнительно необходимо указать параметры анимации (которая зависит от типа animationType и длительности duration) и функцию CameraCallback , которая принимает логический аргумент, обозначающий завершение действия камеры. Если движение камеры по каким-то причинам прерывается, то в качестве аргумента передаётся «false»; если движение камеры завершилось успешно – «true». CameraCallback имеет необязательный тип, т.е. может быть не инициализирован – укажем здесь null.
Не забываем вызвать moveToStartLocation() в методе onCreate – и можем проверять работу перемещения камеры на стартовую локацию:

3) Устанавливаем метку на карте
Отметим нашу стартовую точку пином. Для начала загрузим в проект иконку с расширением png – скачайте файл ic_pin_png и перетащите его в папку drawable проекта.
Заранее создадим две переменные, которые проинициализируем и о которых более подробно расскажем далее:
private lateinit var mapObjectCollection: MapObjectCollection private lateinit var placemarkMapObject: PlacemarkMapObject
Реализуем функцию setMarkerInStartLocation() , отвечающую за установку метки на карте. В ней первым делом создаём ссылку на нашу картинку — marker. Затем инициализируем коллекцию различных возможных объектов на карте mapObjectCollection, которая может содержать любой набор элементов MapObject. MapObject – это пользовательский объект, отображаемый на карте, например, метка с иконкой или геометрическая фигура. Создадим такой геопозиционированный объект PlacemarkMapObject , являющийся наследником MapObject – метку с иконкой — который будет располагаться по координате, используемой ранее.
Для этого к mapObjectCollection применим метод addPlacemark , в котором укажем необходимую точку с координатами и, с помощью класса ImageProvider , ссылку на иконку. Таким образом мы создадим новую метку с нашим изображением и добавим её в текущую коллекцию. Если указать только точку с координатами, тогда получится пин со значком и стилем по умолчанию (будет выглядеть, как обычная точка на карте).
Дополнительно, к placemarkMapObject мы можем применить различные свойства, например, установить прозрачность метки и текст сверху в придачу при помощи команд setOpacity и setText соответственно. В итоге, произведя все вышеперечисленные действия, мы имеем следующую функцию:
private fun setMarkerInStartLocation() < val marker = R.drawable.ic_pin_black_png // Добавляем ссылку на картинку mapObjectCollection = binding.mapview.map.mapObjects // Инициализируем коллекцию различных объектов на карте placemarkMapObject = mapObjectCollection.addPlacemark(startLocation, ImageProvider.fromResource(this, marker)) // Добавляем метку со значком placemarkMapObject.opacity = 0.5f // Устанавливаем прозрачность метке placemarkMapObject.setText("Обязательно к посещению!") // Устанавливаем текст сверху метки >
Вызываем setMarkerFirstOpen() в onCreate, запускаем приложение и наблюдаем метку над Эрмитажем.

4) Использование векторных изображений
Обычно мы используем не png файлы, а векторные изображения. Тут имеется одна неприятная особенность: векторные изображения в качестве маркеров в MapKit не поддерживаются – они просто не будут отображаться. Но данный момент можно обойти. ImageProvider умеет работать с bitmap. Таким образом, нам стоит просто перевести векторное изображение в bitmap и использовать по назначению. Для этого создадим соответствующую функцию:
private fun createBitmapFromVector(art: Int): Bitmap?
Используем её: загрузите приложенные векторные изображения ic_pin_black_svg, ic_pin_blue_svg и ic_pin_red_svg. Затем добавьте их в свой проект: правой кнопкой по папке drawable -> Vector Asset -> Local file -> установите размер 64dp X 64dp -> Finish.
После этого в функции setMarkerInStartLocation() исправим marker и placemarkMapObject на:
val marker = createBitmapFromVector(R.drawable.ic_pin_black_svg) placemarkMapObject = mapObjectCollection.addPlacemark(startLocation, ImageProvider.fromBitmap(marker))
Таким образом мы можем использовать в качестве иконок для меток и векторные и растровые изображения.
5) Работа с зумом: меняем иконку маркера при отдалении и приближении камеры
Теперь обработаем моменты увеличения и уменьшения масштаба карты пользователем: пусть при пересечении некоторой границы величины зума маркер становится красным при отдалении камеры и синим при приближении. Данную величину границы зума сразу вынесем в константу в companion object:
const val ZOOM_BOUNDARY = 16.4f
Наследуемся от интерфейса CameraListener , который позволяет следить за обновлениями положения камеры:
class MainActivity : AppCompatActivity(), CameraListener
и переопределяем метод onCameraPositionChanged , который срабатывает при изменении положения камеры:
override fun onCameraPositionChanged( map: Map, cameraPosition: CameraPosition, cameraUpdateReason: CameraUpdateReason, finished: Boolean ) <>
Здесь параметрами являются:
- map - новая область карты (будьте внимательны при импорте – это не коллекция Map);
- cameraPosition - текущее положение камеры;
- cameraUpdateReason - причина обновления камеры. Это enum-класс, включающий в себя две константы: APPLICATION – т.е. причиной обновления камеры является вызов приложением метода move; GESTURES – причиной являются действия пользователя, такие как масштабирование, поворот и прочее;
- finished – завершилось ли движение камеры окончательно. Будет «true», если камера закончила движение, «false» - в противном случае.
Обсудим логику работы изменения иконки метки при регулировании масштаба отображения карты. После того как пользователь успешно приблизил или отдалил карту, т.е. finished==true, проверим величину нового зума. Если величина зума cameraPosition.zoom будет меньше фиксированного значения ZOOM_BOUNDARY - мы поменяем изображение метки на красную иконку. Иначе, будет установлен пин синего цвета. Так же, чтобы каждый раз не производить излишнюю замену иконки при изменении масштаба пользователем, когда порог ZOOM_BOUNDARY не был преодолён, добавим дополнительную проверку: стала ли новая величина зума больше или меньше фиксированного значения? Для этого будем перезаписывать значение zoomValue после каждого изменения масштаба карты пользователем. В итоге имеем:
override fun onCameraPositionChanged( map: Map, cameraPosition: CameraPosition, cameraUpdateReason: CameraUpdateReason, finished: Boolean ) < if (finished) < // Если камера закончила движение when < cameraPosition.zoom >= ZOOM_BOUNDARY && zoomValue < placemarkMapObject.setIcon(ImageProvider.fromBitmap(createBitmapFromVector(R.drawable.ic_pin_blue_svg))) >cameraPosition.zoom = ZOOM_BOUNDARY -> < placemarkMapObject.setIcon(ImageProvider.fromBitmap(createBitmapFromVector(R.drawable.ic_pin_red_svg))) >> zoomValue = cameraPosition.zoom // После изменения позиции камеры сохраняем величину зума > >
И последний штрих: необходимо инициализировать метод слушателя камеры, чтобы логика, прописанная в onCameraPositionChanged , срабатывала. В onCreate добавляем следующую команду и после проверяем в приложении изменение иконки метки при регулировании масштаба:
binding.mapview.map.addCameraListener(this)

6) Обработка события нажатия на метку
Перейдем к обработке события нажатия на пин – выведем всплывающее окно Toast с надписью.
Для этого нам понадобится интерфейс MapObjectTapListener, как раз отвечающий за тапы по различным объектам на карте. Создадим экземпляр класса MapObjectTapListener и переопределим onMapObjectTap, который возвращает «true», если событие было обработано, иначе – «false». При этом, мы можем использовать информацию о mapObject’е и точке с координатами, по которой произошёл тап. Мы же просто воспользуемся Toast’ом с фиксированным текстом:
private val mapObjectTapListener = object : MapObjectTapListener < override fun onMapObjectTap(mapObject: MapObject, point: Point): Boolean< Toast.makeText(applicationContext, "Эрмитаж — музей изобразительных искусств", Toast.LENGTH_SHORT).show() return true >>
Интерфейс MapObjectTapListener может быть присоединён к любому MapObject’у. Подключим этот слушатель в функции setMarkerFirstOpen() к нашей метке placemarkMapObject :
placemarkMapObject.addTapListener(mapObjectTapListener)
Готово! Теперь, при тапе на метку, будет появляться всплывающее окно с необходимой информацией.

Отдельно отметим, почему мы создаем отдельную переменную mapObjectTapListener , а не сразу пишем в функции setMarkerFirstOpen подобным образом:
placemarkMapObject.addTapListener(object : MapObjectTapListener < override fun onMapObjectTap(mapObject: MapObject, point: Point): Boolean< Toast.makeText(applicationContext, "Эрмитаж — музей изобразительных искусств", Toast.LENGTH_SHORT).show() return true>>)
Дело в том, что MapKit хранит слабые ссылки на передаваемые ему Listener-объекты, поэтому их необходимо сохранять на стороне приложения. Иначе, первое время клики будут работать адекватно, а затем перестанут реагировать на тапы. Это связано с тем, что сборщик мусора, который не учитывает связь ссылки и объекта в куче при выявлении объектов, подлежащих удалению, в какой-то момент удалит наш слушатель. После этого при тапе на пин в логах возникнет неприятное сообщение: «yandex.maps.runtime: Java object is already finalized. Nothing to do» и клики перестанут обрабатываться должным образом. Поэтому, необходимо использовать строгую ссылку на объект MapObjectTapListener, что мы и делаем выше.
Бонусные разделы
Весьма полезной и интересной темой в Mapkit является получения информации об объекте, используя поиск и метаданные. Поскольку наш туториал рассчитан на новичков, сейчас мы не будем подробно разбираться в том, как именно устроен поиск и как работать с метаданными в mapkit. Но, в качестве дополнительной полезной информации, без особой конкретики и не раскрывая темы в полном объёме, приведём примеры кода с использованием этих моментов в пунктах 7*) и 8*). Более подробно прочитать о данных возможностях Mapkit можно в статье «Поиск в MapKit: Tips & Tricks».
7) Выделение объекта на карте
Продолжим знакомство со слушателями объектов на карте. Теперь будем работать с GeoObject – объектом в слоях карты, примером которого может выступать здание или памятник. Слушателем будет выступать экземпляр класса GeoObjectTapListener, в котором требуется переопределить onObjectTap. Метод onObjectTap позволяет извлечь краткую информацию затронутого геообъекта и возвращает булевское значение. Так, если мы вернём «false» – тогда событие клика распространится на карту, и его сможет перехватить другой слушатель. Если «true» – «дальше» событие никуда не пойдёт. Пример: если пользователь клацает на объект, а в проекте имеется дополнительный слушатель, обрабатывающий действия по всей карте (как, например, в пункте 8), то при значении «true» для onObjectTap данное событие более перехвачено не будет.
Давайте подсветим какое-либо здание при тапе на него, иначе говоря, отобразим для пользователя «выделение» объекта. Для этого нам понадобятся идентификаторы объекта и слоя. Получим геообъект из geoObjectTapEvent методом getGeoObject. Информация про геообъект хранится в метаданных. Метаданные бывают разных видов, более подробно можно ознакомиться в «Поиск в MapKit: Tips & Tricks». Доступ к информации можно получить с помощью метода getMetadataContainer(), для которого с помощью метода getItem указываем ключ для этого контейнера - тип необходимых метаданных. У нас данный ключ – GeoObjectSelectionMetadata.
После того как мы получили метаданные, для карты mapview используем метод selectGeoObject, в который передаем необходимые идентификаторы объекта и слоя:
private val tapListener = object : GeoObjectTapListener < override fun onObjectTap(geoObjectTapEvent: GeoObjectTapEvent): Boolean < val selectionMetadata: GeoObjectSelectionMetadata = geoObjectTapEvent .geoObject .metadataContainer .getItem(GeoObjectSelectionMetadata::class.java) binding.mapview.map.selectGeoObject(selectionMetadata.id, selectionMetadata.layerId) return false >>
Подключаем данный слушатель:
binding.mapview.map.addTapListener(tapListener) // Добавляем карте слушатель тапов по объектам
Теперь, при клике на здание, оно выделится следующим образом:

8) Получаем информацию об объекте при тапе на него
Осуществим следующую идею: при тапе в любую область карты будет появляться всплывающее окно с информацией об улице в данном месте. Если по каким-то причинам улицы нет, например, если тапнули по реке или не пришел ответ от сервера – будем выводить соответствующее сообщение.
В переопределённой функции onSearchResponse, последовательно используя различные вызовы, мы сможем докопаться до интересующей нас сущности – будь то улица, дом, маршрут, страна и прочее. А данную информацию можем использовать уже как угодно, например, поместить в Toast:
private val searchListener = object : Session.SearchListener < override fun onSearchResponse(response: Response) < val street = response.collection.children.firstOrNull()?.obj ?.metadataContainer ?.getItem(ToponymObjectMetadata::class.java) ?.address ?.components ?.firstOrNull < it.kinds.contains(Address.Component.Kind.STREET)>?.name ?: "Информация об улице не найдена" Toast.makeText(applicationContext, street, Toast.LENGTH_SHORT).show() > override fun onSearchError(p0: Error) < >>
Предварительно создадим две новые переменные: поисковую сессию searchSession и интерфейс для начала поиска searchManager, которые проинициализируем далее:
lateinit var searchManager: SearchManager lateinit var searchSession: Session
Теперь searchListener надо поместить в другой слушатель – inputListener, который обрабатывает тапы по всей карте. В нем переопределяются два метода – короткое и длинное нажатие. onMapTap - вызывается при быстром касании, если оно не было обработано геообъектами или объектами карты. Как уже говорилось ранее в пункте 7: если касание было обработано onObjectTap, то onMapTap может не сработать. Чтобы метод адекватно обработал тап – в onObjectTap необходимо вернуть «false». В самом методе onMapTap инициализируем поисковую сессию searchSession:
private val inputListener = object : InputListener < override fun onMapTap(map: Map, point: Point) < searchSession = searchManager.submit(point, 20, SearchOptions(), searchListener) >override fun onMapLongTap(map: Map, point: Point) <> >
Метод обратного поиска submit требует на вход точку с координатами, величину зума (в окрестности которой будет происходить поиск), настройки поиска и слушатель.
После, в методе onCreate инициализируем searchManager и добавляем к нашей карте слушатель тапов по карте с извлечением информации:
searchManager = SearchFactory.getInstance().createSearchManager(SearchManagerType.ONLINE) binding.mapview.map.addInputListener(inputListener) // Добавляем слушатель тапов по карте с извлечением информации
В итоге, после проведенных манипуляций, при клике на какое-либо место в городе на экране будет появляться название соответствующей улицы:

Заключение
Yandex MapKit предоставляет гигантское количество возможностей, с помощью которых можно реализовать самые различные интересные и полезные идеи. Сегодня мы познакомились с основными моментами использования Яндекс-карт: были показаны часто используемые методы, даны различные рекомендации и хитрости для проблемных мест, ответы на вероятно возникающие вопросы при первом знакомстве с данной библиотекой.
Данная статья не является исчерпывающей, наоборот - с помощью Yandex MapKit’а можно решить множество разнообразных задач: построить маршрут для пешей прогулки иль поездки на общественном транспорте; отобразить на карте ближайший к пользователю банкомат; найти нужную организацию; узнать о пробках на дорогах в реальном времени; предоставить местоположение пользователя; использовать панорамы; получить какую-либо информацию об объекте на карте и многое, многое другое.
Дополнительно ознакомиться с примерами реализации части функционала можно здесь.
Искренне надеюсь, что полученные сегодня знания ускорили ваш порог вхождения в Яндекс MapKit, дали понимание, каким образом можно взаимодействовать и работать с Яндекс-картами в плане разработки.
Дерзайте! И помните: «Дорогу осилит идущий».
Список используемой литературы:
- Руководство MapKit;
- Условия использования MapKit;
- Статья «Поиск в MapKit: Tips & Tricks»;
- Примеры некоторых возможностей Mapkit;
- Проект на Github.
Код программы
Код программы
class MainActivity : AppCompatActivity(), CameraListener < private lateinit var binding: ActivityMainBinding private lateinit var mapObjectCollection: MapObjectCollection // Коллекция различных объектов на карте private lateinit var placemarkMapObject: PlacemarkMapObject // Геопозиционированный объект (метка со значком) на карте private val startLocation = Point(59.9402, 30.315) // Координаты Эрмитажа private var zoomValue: Float = 16.5f // Величина зума lateinit var searchManager: SearchManager lateinit var searchSession: Session private val mapObjectTapListener = object : MapObjectTapListener < override fun onMapObjectTap(mapObject: MapObject, point: Point): Boolean < Toast.makeText(applicationContext, "Эрмитаж — музей изобразительных искусств", Toast.LENGTH_SHORT).show() return true >> private val geoObjectTapListener = object : GeoObjectTapListener < override fun onObjectTap(geoObjectTapEvent: GeoObjectTapEvent): Boolean < val selectionMetadata: GeoObjectSelectionMetadata = geoObjectTapEvent .geoObject .metadataContainer .getItem(GeoObjectSelectionMetadata::class.java) binding.mapview.map.selectGeoObject(selectionMetadata.id, selectionMetadata.layerId) return false >> private val searchListener = object : Session.SearchListener < override fun onSearchResponse(response: Response) < val street = response.collection.children.firstOrNull()?.obj ?.metadataContainer ?.getItem(ToponymObjectMetadata::class.java) ?.address ?.components ?.firstOrNull < it.kinds.contains(Address.Component.Kind.STREET) >?.name ?: "Информация об улице не найдена" Toast.makeText(applicationContext, street, Toast.LENGTH_SHORT).show() > override fun onSearchError(p0: Error) < >> private val inputListener = object : InputListener < override fun onMapTap(map: Map, point: Point) < searchSession = searchManager.submit(point, 20, SearchOptions(), searchListener) >override fun onMapLongTap(map: Map, point: Point) <> > override fun onCreate(savedInstanceState: Bundle?) < super.onCreate(savedInstanceState) setApiKey(savedInstanceState) // Проверяем: был ли уже ранее установлен API-ключ в приложении. Если нет - устанавливаем его. MapKitFactory.initialize(this) // Инициализация библиотеки для загрузки необходимых нативных библиотек. binding = ActivityMainBinding.inflate(layoutInflater) // Раздуваем макет только после того, как установили API-ключ setContentView(binding.root) // Размещаем пользовательский интерфейс в экране активности moveToStartLocation() // Перемещаем камеру в определенную область на карте setMarkerInStartLocation() // Устанавливаем маркер на карте binding.mapview.map.addCameraListener(this) // Добавляем карте слушатель камеры для слежки за изменением величины зума binding.mapview.map.addTapListener(geoObjectTapListener) // Добавляем слушатель тапов по объектам searchManager = SearchFactory.getInstance().createSearchManager(SearchManagerType.ONLINE) binding.mapview.map.addInputListener(inputListener) // Добавляем слушатель тапов по карте с извлечением информации >override fun onCameraPositionChanged( map: Map, cameraPosition: CameraPosition, cameraUpdateReason: CameraUpdateReason, finished: Boolean ) < if (finished) < // Если камера закончила движение when < cameraPosition.zoom >= ZOOM_BOUNDARY && zoomValue < placemarkMapObject.setIcon(ImageProvider.fromBitmap(createBitmapFromVector(R.drawable.ic_pin_blue_svg))) >cameraPosition.zoom = ZOOM_BOUNDARY -> < placemarkMapObject.setIcon(ImageProvider.fromBitmap(createBitmapFromVector(R.drawable.ic_pin_red_svg))) >> zoomValue = cameraPosition.zoom // После изменения позиции камеры сохраняем величину зума > > private fun setMarkerInStartLocation() < val marker = createBitmapFromVector(R.drawable.ic_pin_black_svg) mapObjectCollection = binding.mapview.map.mapObjects // Инициализируем коллекцию различных объектов на карте placemarkMapObject = mapObjectCollection.addPlacemark(startLocation, ImageProvider.fromBitmap(marker)) // Добавляем метку со значком placemarkMapObject.opacity = 0.5f // Устанавливаем прозрачность метке placemarkMapObject.setText("Обязательно к посещению!") // Устанавливаем текст сверху метки placemarkMapObject.addTapListener(mapObjectTapListener) >private fun createBitmapFromVector(art: Int): Bitmap? < val drawable = ContextCompat.getDrawable(this, art) ?: return null val bitmap = Bitmap.createBitmap( drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888 ) ?: return null val canvas = Canvas(bitmap) drawable.setBounds(0, 0, canvas.width, canvas.height) drawable.draw(canvas) return bitmap >private fun moveToStartLocation() < binding.mapview.map.move( CameraPosition(startLocation, zoomValue, 0.0f, 0.0f), // Позиция камеры Animation(Type.SMOOTH, 2f), // Красивая анимация при переходе на стартовую точку null ) >private fun setApiKey(savedInstanceState: Bundle?) < val haveApiKey = savedInstanceState?.getBoolean("haveApiKey") ?: false // При первом запуске приложения всегда false if (!haveApiKey) < MapKitFactory.setApiKey(MAPKIT_API_KEY) // API-ключ должен быть задан единожды перед инициализацией MapKitFactory >> // Если Activity уничтожается (например, при нехватке памяти или при повороте экрана) - сохраняем информацию, что API-ключ уже был получен ранее override fun onSaveInstanceState(outState: Bundle) < super.onSaveInstanceState(outState) outState.putBoolean("haveApiKey", true) >// Отображаем карты перед тем моментом, когда активити с картой станет видимой пользователю: override fun onStart() < super.onStart() MapKitFactory.getInstance().onStart() binding.mapview.onStart() >// Останавливаем обработку карты, когда активити с картой становится невидимым для пользователя: override fun onStop() < binding.mapview.onStop() MapKitFactory.getInstance().onStop() super.onStop() >companion object < const val MAPKIT_API_KEY = "Ваш API-ключ" const val ZOOM_BOUNDARY = 16.4f >>
activity_main.xml
Также код можно посмотреть в GitHub.
«Яндекс. Карты» теперь весят в 10 раз меньше

В закладки

Владельцы 16-гигабайтных iPhone будут довольны.
Картографическое мобильное приложение «Яндекс.Карты» получило важное обновление.
Карты всех регионов заметно уменьшились в размере за счет перехода с растровых изображений на векторные, соответственно, больше нет надобности в нескольких вариантах одной и той же карты в разных масштабах.
Согласно официальному пресс-релизу, офлайн-карта Москвы теперь занимает всего 144 МБ вместо 1,9 ГБ, офлайн-карта Санкт-Петербурга и области — 231 МБ вместо 3,2 ГБ. В реальности программа предлагала обновить карту Санкт-Петербурга, объемом 616 Мб. Лишь после полной переустановки приложения, был предложен уменьшенный вариант.




- Кроме того, без подключения к Интернету можно искать кафе, рестораны, магазины, банкоматы, аптеки, АЗС и другие важные объекты на карте. Достаточно лишь заранее сохранить на устройство карту нужного города или региона.
- В управлении появилась возможность менять угол наклона карты, что поможет сориентироваться в незнакомом месте (достаточно провести двумя пальцами по экрану).
Скачать новые Яндекс.Карты, получившие обновленный дизайн, можно бесплатно в App Store.
Важно! При обновлении приложения карты, сохранённые в память ранее, будут удалены, потребуется их повторное скачивание.
(Проголосуйте первым за статью!)
Хочешь ещё? Читай больше в Telegram
Ищешь ответ на вопрос? Приходи на Форум
Владельцы 16-гигабайтных iPhone будут довольны. Картографическое мобильное приложение «Яндекс.Карты» получило важное обновление. Карты всех регионов заметно уменьшились в размере за счет перехода с растровых изображений на векторные, соответственно, больше нет надобности в нескольких вариантах одной и той же карты в разных масштабах. Согласно официальному пресс-релизу, офлайн-карта Москвы теперь занимает всего 144 МБ вместо 1,9 ГБ, офлайн-карта Санкт-Петербурга.