如何防止 Mapnik 在我基于 OSMdroid 的应用程序中混合图块源
How can I prevent Mapnik from mixing tile sources in my OSMdroid based app
我制作了一个供个人使用的地图应用程序,我可以在其中选择三个地图来源之一:OpenTopo、Mapnik 或 HikeBike。它主要用于离线使用,为此我下载了一套完整的 OpenTopo 位图图块 (.png),涵盖了我最感兴趣的局部区域的 11 到 15 缩放级别。而Mapnik和HikeBike的地图模式,我只打算上线时使用,主要用于旅游规划等
OpenTopo 瓦片存档以.gemf 文件的形式直接存储在/storage/extSdCard/osmdroid/(这是查询Configuration.getInstance().getOsmdroidBasePath().getAbsolutePath();
返回的路径)。
我现在遇到的问题是两个 "auxiliary" 地图模式(Mapnik 和 HikeBike)没有完全 "respect" 为它们设置的图块源,甚至如果连接到互联网。相反,每当 mapview 所需的位置和缩放级别被本地可用的 OpenTopo 瓦片覆盖时,Mapnik/HikeBike 更喜欢显示这些瓦片。
我已将 map.setUseDataConnection(true)
放入 onCreate()
中,我希望这将使在线获取(丢失的)图块成为默认设置,并授予网络连接。
对于 (1) 如何使基于矢量的地图模式按预期区分它们自己的正确图块和 OpenTopo 静态位图,我将不胜感激。以及 (2) 如果实际在线,如何吸引他们动态下载任何丢失的图块。
以下是我的 MapsActivity.java 文件中 1100 多行代码中的一些可能相关的片段:
// ===========================================================
// (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 目录结构的屏幕截图:
storage extSdCard.png
我通过首先将缓存的图块从“/storage/extSdCard/osmodroid/tiles”移动到新目录“/storage/extSdCard/osmXtra/tiles”解决了这个问题,然后基本上剥离了 onCreate()
的内容并将其放入它在一个单独的函数 onCreateCommonCode(String tileSourceName_X)
中,该函数将 TileSource 名称作为参数。
此函数现在在 onCreate()
中启动时运行一次,使用默认的 OpenTopoMaps 作为输入,而且(使用适当的 TileSource 参数)每当用户命令切换地图类型时也运行一次。一个简单的“if TileSourceThis then TilePathThat”测试选择正确的目录来搜索存档文件或切片缓存。
也许不是最优雅的解决方案,但除了一些小问题,比如 mLocationOverlay.enableFollowLocation()
在地图切换后的表现,我对它现在的工作方式很满意。
我制作了一个供个人使用的地图应用程序,我可以在其中选择三个地图来源之一:OpenTopo、Mapnik 或 HikeBike。它主要用于离线使用,为此我下载了一套完整的 OpenTopo 位图图块 (.png),涵盖了我最感兴趣的局部区域的 11 到 15 缩放级别。而Mapnik和HikeBike的地图模式,我只打算上线时使用,主要用于旅游规划等
OpenTopo 瓦片存档以.gemf 文件的形式直接存储在/storage/extSdCard/osmdroid/(这是查询Configuration.getInstance().getOsmdroidBasePath().getAbsolutePath();
返回的路径)。
我现在遇到的问题是两个 "auxiliary" 地图模式(Mapnik 和 HikeBike)没有完全 "respect" 为它们设置的图块源,甚至如果连接到互联网。相反,每当 mapview 所需的位置和缩放级别被本地可用的 OpenTopo 瓦片覆盖时,Mapnik/HikeBike 更喜欢显示这些瓦片。
我已将 map.setUseDataConnection(true)
放入 onCreate()
中,我希望这将使在线获取(丢失的)图块成为默认设置,并授予网络连接。
对于 (1) 如何使基于矢量的地图模式按预期区分它们自己的正确图块和 OpenTopo 静态位图,我将不胜感激。以及 (2) 如果实际在线,如何吸引他们动态下载任何丢失的图块。
以下是我的 MapsActivity.java 文件中 1100 多行代码中的一些可能相关的片段:
// ===========================================================
// (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 目录结构的屏幕截图:
storage extSdCard.png
我通过首先将缓存的图块从“/storage/extSdCard/osmodroid/tiles”移动到新目录“/storage/extSdCard/osmXtra/tiles”解决了这个问题,然后基本上剥离了 onCreate()
的内容并将其放入它在一个单独的函数 onCreateCommonCode(String tileSourceName_X)
中,该函数将 TileSource 名称作为参数。
此函数现在在 onCreate()
中启动时运行一次,使用默认的 OpenTopoMaps 作为输入,而且(使用适当的 TileSource 参数)每当用户命令切换地图类型时也运行一次。一个简单的“if TileSourceThis then TilePathThat”测试选择正确的目录来搜索存档文件或切片缓存。
也许不是最优雅的解决方案,但除了一些小问题,比如 mLocationOverlay.enableFollowLocation()
在地图切换后的表现,我对它现在的工作方式很满意。