Как я могу запретить Mapnik смешивать источники тайлов в моем приложении на основе OSMdroid

Я сделал картографическое приложение для личного пользования, в котором я могу выбрать один из трех источников карт: OpenTopo, Mapnik или HikeBike. Он в первую очередь предназначен для автономного использования, и с этой целью я загрузил полный набор растровых фрагментов OpenTopo (.png), охватывающий уровни масштабирования от 11 до 15 для локальных областей, которые меня больше всего интересуют. С другой стороны, режимы карты Mapnik и HikeBike я собираюсь использовать только в режиме онлайн, в основном для планирования туров и т. д.

Архивы тайлов OpenTopo хранятся в виде файлов .gemf непосредственно в /storage/extSdCard/osmdroid/ (это путь, возвращаемый запросом Configuration.getInstance().getOsmdroidBasePath().getAbsolutePath();).

Проблема, с которой я столкнулся сейчас, заключается в том, что два «вспомогательных» режима карты (Mapnik и HikeBike) не полностью «уважают» источники тайлов, которые были установлены для их использования, даже если они подключены к Интернету. Вместо этого всякий раз, когда локальность и уровни масштабирования, необходимые для просмотра карты, покрываются локально доступными тайлами OpenTopo, Mapnik/HikeBike раздражающе предпочитают отображать эти тайлы.

Я поместил map.setUseDataConnection(true) в onCreate(), что, как я надеялся, сделает извлечение (отсутствующих) плиток в режиме онлайн по умолчанию при наличии сетевого подключения.

Я был бы признателен за совет (1) о том, как заставить режимы векторной карты различать свои собственные правильные плитки и статические растровые изображения OpenTopo, как и ожидалось. И (2) как побудить их динамически загружать любые отсутствующие плитки, если они действительно находятся в сети.

Вот некоторые потенциально важные фрагменты из более чем 1100 строк кода в моем файле MapsActivity.java:

// ===========================================================
//                      (Some) Constants
// ===========================================================

private static final String tileSourceName_MPNK = "Mapnik";
private static final String tileSourceName_HKBK = "HikeBikeMap";
private static final String tileSourceName_OPTP = "OpenTopoMap";

// Pre-select the default (initial) tile source here
public static String activeTileSourceName = tileSourceName_OPTP;


// ===========================================================
//                      (Some) Fields
// ===========================================================

private SharedPreferences mPrefs;
MapView map = null;     
SqliteArchiveTileWriter tileWriter = null;

// Some state booleans
boolean registeredNetworkReceiver = false;
boolean wiffiUp = false;


// ===========================================================
//                    (parts of) onCreate
// ===========================================================

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mContext = this;

    // Load/initialize the osmdroid configuration
    final Context ctx = getApplicationContext();
    Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(ctx));
    final DisplayMetrics dm = ctx.getResources().getDisplayMetrics();

    mPrefs = ctx.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);

    // Inflate and create the map
    setContentView(R.layout.activity_maps);
    map = findViewById(R.id.map);

    // Set initial map type (tile source)
    map.setTileSource(TileSourceFactory.getTileSource(activeTileSourceName));

    ...

    // Use custom [+][-] buttons
    map.getZoomController().setVisibility(
            CustomZoomButtonsController.Visibility.SHOW_AND_FADEOUT);

    // Scale tiles relative to the current screen's DPI. Should help with readability in "the field"
    map.setTilesScaleFactor(1.2f);          // Compromise scale, better than dpi

    // Store the startup zoom level we just set, so it survives first pass through onResume()
    final SharedPreferences.Editor edit = mPrefs.edit();
    edit.putFloat(PREFS_ZOOM_LEVEL_DOUBLE, (float) map.getZoomLevelDouble());
    edit.apply();

    ....

    // If/when we have a network connection: default to using (missing?) on-line tiles.
    map.setUseDataConnection(true);

    // Register a wiffi networkReceiver
    registerReceiver();
}


// ===========================================================
//                    (parts of) onPause
// ===========================================================

@Override
public void onPause() {

    // Save the current location
    final SharedPreferences.Editor edit = mPrefs.edit();
    edit.putString(PREFS_TILE_SOURCE, map.getTileProvider().getTileSource().name());

}

// ===========================================================
//                    (parts of) onResume
// ===========================================================

@Override
protected void onResume() {
    super.onResume();

    // Set zoom to default level if first pass, else to the latest level stored onPause().
    final float zoomLevel = mPrefs.getFloat(PREFS_ZOOM_LEVEL_DOUBLE, 1);
    map.getController().setZoom(zoomLevel);

}


// ===========================================================
//                    (parts of) onDestroy
// ===========================================================

@Override
protected void onDestroy() {
    unregisterReceiver(networkReceiver);
    ....

    super.onDestroy();
}


// ===========================================================
//            networkReceiver --> onReceive
// ===========================================================

/**
The idea behind the following function is to force a map refresh when switching from offline to online. If the display is not refreshed there may be no attempt to get better tiles */

private final BroadcastReceiver networkReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                NetworkInfo networkInfo = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
                if (networkInfo != null && networkInfo.getDetailedState() == NetworkInfo.DetailedState.CONNECTED) {
                    Log.d("Network", "Internet YAY !!!!!!!!!!!!!!!!!!");
                    toaster("Internet YAY !!!!!!!!!!!!!!!!!!");
                    wiffiUp = true;
                } else if (networkInfo != null && networkInfo.getDetailedState() == NetworkInfo.DetailedState.DISCONNECTED) {
                    Log.d("Network", "No internet :(");
                    toaster("No internet :(");
                    wiffiUp = false;
                }
                map.invalidate();    // i.e. refresh screen
            }
        }
        catch(NullPointerException e) {
            e.printStackTrace();
        }
    }
};


// ===========================================================
//                      registerReceiver
// ===========================================================

private void registerReceiver() {
    if (registeredNetworkReceiver) {
        return;
    }
    registeredNetworkReceiver = true;
    IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
    MapsActivity.this.registerReceiver(networkReceiver, filter);
}


// ===========================================================
//                      onOptionsItemSelected
// ===========================================================

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id. map1_menu:
            map.setTileSource(TileSourceFactory.getTileSource(tileSourceName_OPTP));
            toaster(" OpenTopo map ");
            return true;

        case R.id. map2_menu:
            map.setTileSource(TileSourceFactory.getTileSource(tileSourceName_MPNK));
            map.invalidate();
            toaster(" Mapnik map ");
            return true;

        case R.id. map3_menu:
            map.setTileSource(TileSourceFactory.getTileSource(tileSourceName_HKBK));
            map.invalidate();
            toaster(" BikeHike map ");
            return true;

        default:
            return super.onOptionsItemSelected(item);
    }
}

Редактировать 2: Вот снимок экрана, показывающий структуру каталога /storage/extSdCard/osmdroid:

хранилище extSdCard.png


person moof-moof    schedule 07.11.2019    source источник
comment
Может быть, поделитесь кодом, показывающим, как вы настраиваете источники плиток в своем приложении (и общую конфигурацию) и как вы переключаете источники плиток во время выполнения, чтобы мы могли проверить, не видим ли мы там каких-либо проблем. Я предполагаю, что у вас либо несколько источников тайлов на карте одновременно, либо могут возникнуть проблемы с настройкой кэширования (github.com/osmdroid/osmdroid/wiki/Tile-Caching). Но нельзя сказать только по описанию.   -  person Josef Adamcik    schedule 07.11.2019
comment
Конечно. Я добавил примеры кода.   -  person moof-moof    schedule 07.11.2019
comment
Помню, видел такие штуки несколько лет назад. По сути, базовый код для рендеринга на основе файлов в вашем случае. Материал .gemf имеет приоритет и не имеет реального представления о том, что такое ваш источник плитки и какой источник плитки он содержит, поэтому плитки всегда используются. Лучше всего, вероятно, попытаться удалить поставщиков файловых плиток, когда вы находитесь в онлайн-режиме. Вам придется копаться в источнике, чтобы попытаться понять, как удалить их или создать что-то без них.   -  person Ifor    schedule 10.11.2019
comment
@ifor - Спасибо за подсказки. Недавно я понял, что то, что вы описываете, вероятно, происходит, и поэтому попробовал стратегию активного сброса путей провайдера тайлов на лету. Я думаю, что теперь у меня все (в основном) работает, и я опубликую свое решение через некоторое время.   -  person moof-moof    schedule 10.11.2019


Ответы (1)


Я решил эту проблему, сначала переместив только кешированные плитки из «/storage/extSdCard/osmodroid/tiles» в новый каталог «/storage/extSdCard/osmXtra/tiles», а затем, в основном, вычистив кишки onCreate() и поместив их в отдельная функция onCreateCommonCode(String tileSourceName_X), которая принимает имя TileSource в качестве параметра.

Эта функция теперь запускается один раз при запуске в onCreate() с OpenTopoMaps по умолчанию в качестве входных данных, а также (с соответствующим параметром TileSource) всякий раз, когда пользователь командует переключением типа карты. Простой тест "if TileSourceThis то TilePathTht" выбирает правильный каталог для поиска либо архивных файлов, либо тайловый кеш.

Возможно, не самое элегантное решение, но, если не считать мелких странностей вроде поведения mLocationOverlay.enableFollowLocation() после смены карты, меня устраивает, как она теперь работает.

person moof-moof    schedule 10.11.2019