четверг, 1 декабря 2011 г.

Карты в приложении для платформы Android

Здравствуйте!

Существует много способов отображения различной информации на карте в приложении для платформы Android, например, Google Maps или Яндекс Карты, но, пожалуй, самым быстрым и  гибким способом является использование библиотеки osmdroid, т.к. для ее использования не потребуется получения API-ключа, исходный код библиотеки открыт, есть возможность отображать карты из различных источников (например, OpenStreetMap и др.), добавлять новые без существенной доработки исходного кода библиотеки или вовсе без доработки. Загруженные карты сохраняются на карте памяти и доступны без подключения к сети Internet.

В этой заметке, рассмотрим:
  1. Добавление библиотеки в проект (Eclipse, Windows)
  2. Размещение карты в разметке
  3. Добавление нового источника карт
  4. Переключение между поставщиками карт, изменение масштаба, центра карты
  5. Вывод информации на карту
1. Добавление библиотеки в проект (Eclipse, Windows)
Скачаем основную библиотеку и вспомогательную, которая требуется для работы основной и добавим их в наш проект.

В манифест добавим разрешения для нашего приложения
"android.permission.INTERNET" 
"android.permission.WRITE_EXTERNAL_STORAGE" "android.permission.ACCESS_COARSE_LOCATION" "android.permission.ACCESS_FINE_LOCATION" "android.permission.ACCESS_NETWORK_STATE"
"android.permission.ACCESS_WIFI_STATE" 

2. Размещение карты в разметке
Разметка нашей Activity с картой будет динамической, т.е. создадим ее в процессе выполнения приложения в методе onCreate
private MapView mMap;
@Override public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  final RelativeLayout rl = new RelativeLayout(this); // разметка  mMap = new MapView(this, 256); // наша карта  //разрешаем встроенные кнопки изменения масштаба  mMap.setBuiltInZoomControls(true);  // добавим карту в разметку  rl.addView( mMap, new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT,    LayoutParams.FILL_PARENT));  setContentView(rl); }



3. Добавление нового источника карт
Чтобы добавить новый источник создадим новый класс наследник XYTileSource и добавим в наш пакет.

public class myNoYandexTileSource extends XYTileSource{

 public myNoYandexTileSource(String aName, string aResourceId,
   int aZoomMinLevel, int aZoomMaxLevel, int aTileSizePixels,
   String aImageFilenameEnding, String... aBaseUrl) {
  super(aName, aResourceId, aZoomMinLevel, aZoomMaxLevel, aTileSizePixels,
    aImageFilenameEnding, aBaseUrl);
  // TODO Auto-generated constructor stub
 }

 @Override
 public String getTileURLString(MapTile aTile) {
  // TODO Auto-generated method stub
  return String.format(getBaseUrl(), aTile.getX(), aTile.getY(), aTile.getZoomLevel());
 }

}
И создадим экземпляры этого класса

 //Google Maps

final ITileSource tileSourceGoogleMap=new myNoYandexTileSource("Google-Map", null, 0, 23, 256, ".png",

  "http://mt0.google.com/vt/lyrs=m&hl=ru&x=%s&y=%s&z=%s&s=Galileo",

  "http://mt1.google.com/vt/lyrs=m&hl=ru&x=%s&y=%s&z=%s&s=Galileo",

  "http://mt2.google.com/vt/lyrs=m&hl=ru&x=%s&y=%s&z=%s&s=Galileo",

  "http://mt3.google.com/vt/lyrs=m&hl=ru&x=%s&y=%s&z=%s&s=Galileo");   

mTileSources.add(tileSourceGoogleMap);

/Google Maps Sat

final ITileSource tileSourceGoogleSat=new myNoYandexTileSource("Google-Sat", null, 0, 23, 256, ".png",

  "http://khm0.google.ru/kh/v=95&x=%s&y=%s&z=%s&s=Galileo",

  "http://khm1.google.ru/kh/v=95&x=%s&y=%s&z=%s&s=Galileo",

   "http://khm2.google.ru/kh/v=95&x=%s&y=%s&z=%s&s=Galileo",

   "http://khm3.google.ru/kh/v=95&x=%s&y=%s&z=%s&s=Galileo");
4. Переключение между поставщиками карт, изменение масштаба, центра карты
Отображать Google Maps Карты
mMap.setTileSource(tileSourceGoogleMap);
Отображать Google Maps Спутник
mMap.setTileSource(tileSourceGoogleSat);
Отображать OpenStreetMap
mMap.setTileSource(TileSourceFactory.MAPNIK);
Изменение масштаба
mMap.getController().setZoom(10);
Перемещение центра карты
GeoPoint p = new GeoPoint(55.786438, 49.122458);
mMap.getController().animateTo(p);

5. Вывод информации на карту


final ArrayList items = new ArrayList(); 
items.add(new OverlayItem("Казань", "Татарстан", new GeoPoint(55.786438, 49.122458)));
ItemizedIconOverlay MyLocationOverlay = new ItemizedIconOverlay(this, items, null);          
mMap.getOverlays().add(MyLocationOverlay);





Замечу, что Яндекс Карты добавленные описанным способом будут отображаться неверно из-за того, что библиотека не поддерживает нужной проекции. В следующих заметках постараюсь описать, как это исправить без изменения кода библиотеки и доработав код.

14 комментариев:

  1. Пытаюсь делать всё по инструкции и сталкиваюсь с такой ошибкой: java.lang.NoClassDefFoundError: org.osmdroid.views.MapView
    Я понимаю, что не получается найти класс, но библиотеки я добавил, их видно...

    ОтветитьУдалить
  2. если библиотеки добавлены, попробуй сделать Android Tools -> Fix Project Properties

    ОтветитьУдалить
  3. ну так что там с яндекс картами то?

    ОтветитьУдалить
    Ответы
    1. все отлично, выводятся

      Удалить
    2. я про пересчет проекции:
      "Замечу, что Яндекс Карты добавленные описанным способом будут отображаться неверно из-за того, что библиотека не поддерживает нужной проекции. В следующих заметках постараюсь описать, как это исправить без изменения кода библиотеки и доработав код."

      Удалить
    3. схема такая
      1.Расширяешь класс XYTileSource, он будет генерить ссылку на тайл c сервера яндекс
      2.Расширяешь класс TilesOverlay, он будет отрисовывать тайлы (пересчет координат в тайлы и обратно для яндекса легко найдешь в сети), пример такого класса есть в исходниках osmdroid(osmdroid-third-party)
      3. выводишь все на карту
      tileProviderYandex = new MapTileProviderBasic(getApplicationContext());
      tileProviderYandex .setTileSource(экземпляр класса созданного на первом шаге);
      tilesOverlayYandex = new КлассСозданныйНаВторомШаге(tileProviderYandex, this.getBaseContext());
      mMap.getOverlays().add(tilesOverlayYandex);

      Удалить
    4. не могли бы Вы привести код данных классов

      Удалить
    5. вот ссылка http://www.geofaq.ru/forum/index.php?action=vthread&forum=2&topic=7&page=5#msg1152

      Удалить
    6. Здравствуйте!
      При таком способе возникают проблемы при смене зума. Видны тайлы в неправильной проекции, до того как загрузятся нужные в overlay. Если подсунуть фейковые, пустые тайлы - все равно при зуме проблемы:
      1)решетка, вместо масштабируемого изображения предыдущего zoomlevel'a
      2)на части экрана снова куски карты, хоть и из нужной области экрана, но явно с неправильным масштабом и позицией.
      Не могли бы вы подсказать, есть какой-то альтернативный способ корректировки проекции, чтобы она применялась как tileSource, а не overlay. Или это у Вас все зумится корректно?

      Удалить
    7. наверное, ты где-то ошибся, у меня все работает нормально

      Удалить
  4. именно с этим расчетом я и экспериментировал до вопросов Вам, подозреваю что Вы меня попросите показать код, встречный вопрос - Вам какой из ДЕСЯТКОВ вариантов?

    ОтветитьУдалить
    Ответы
    1. не угадал ), не попрошу, у меня есть рабочий код )
      а у тебя все есть, чтобы вывести тайл яндекса, в чем проблема?

      Удалить
    2. проблема в том что тайлы получаю, и вроде как нужные, а показать их в правильном месте не выходит

      Удалить
    3. пиши на почтовый адрес, который укащан в профиле

      Удалить