Skip to content
Permalink
Browse files

feature/#1289 - online tile source usage policy enforcement, particul…

…arly for MAPNIK

New classes:
* `TileSourcePolicy`: tile source usage policy, including the max number of concurrent downloads, if it accepts a meaningless user agent, and if it accepts bulk downloads
* `TileSourcePolicyException`: `RuntimeException` dedicated to the enforcement of online tile source usage policies (`TileSourcePolicy`)

Impacted classes:
* `CacheManager`: the constructors now throw `TileSourcePolicyException` if the online tile source doesn't accept bulk downloads
* `DefaultConfigurationProvider`: created the static member `DEFAULT_USER_AGENT` in order to store the default user agent (if the actual user agent equals that default value, enforcement of some tile source policies will reject the downloads for this tile source)
* `OnlineTileSourceBase`: added a `TileSourcePolicy` member and its setter; replaced the `int pMaxConcurrent` parameter of a constructor with a broader `TileSourcePolicy` parameter
* `SampleCacheDownloaderArchive`: replaced the tile source (default:MAPNIK) with one that accepts bulk downloads (HIKEBIKEMAP); embedded each `new CacheManager` piece in a try/catch syntax because of the new thrown `TileSourcePolicyException`
* `TileDownloader`: checking the tile source usage policy regarding the value of the user agent
* `TileSourceFactory`: changed the definition of tile source MAPNIK - now we enforce a tighter policy (no meaningless user agent, no bulk downloads)
* `XYTileSource`: replaced the `int pMaxConcurrent` parameter of a constructor with a broader `TileSourcePolicy` parameter
  • Loading branch information...
monsieurtanuki committed Mar 7, 2019
1 parent f4ad989 commit 5dcf44eb46bd855a8160e5065890c6fb795d954a
@@ -6,6 +6,7 @@
import android.os.Environment;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -20,6 +21,8 @@
import org.osmdroid.samplefragments.BaseSampleFragment;
import org.osmdroid.tileprovider.cachemanager.CacheManager;
import org.osmdroid.tileprovider.modules.SqliteArchiveTileWriter;
import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
import org.osmdroid.tileprovider.tilesource.TileSourcePolicyException;
import org.osmdroid.util.BoundingBox;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapView;
@@ -59,6 +62,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
setHasOptionsMenu(false);

mMapView = new MapView(getActivity());
mMapView.setTileSource(TileSourceFactory.HIKEBIKEMAP);
((LinearLayout) root.findViewById(R.id.mapview)).addView(mMapView);
btnCache = root.findViewById(R.id.btnCache);
btnCache.setOnClickListener(this);
@@ -107,7 +111,13 @@ private void showCacheManagerDialog(){
public void onClick(DialogInterface dialog, int which) {
switch (which){
case 0:
mgr = new CacheManager(mMapView);
try {
mgr = new CacheManager(mMapView);
} catch(TileSourcePolicyException e) {
Log.e(TAG, e.getMessage());
dialog.dismiss();
return;
}
showCurrentCacheInfo();
break;
case 1:
@@ -209,10 +219,21 @@ private void updateEstimate(boolean startJob) {
if (startJob) {
String outputName = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "osmdroid" + File.separator + cache_output.getText().toString();
writer=new SqliteArchiveTileWriter(outputName);
mgr = new CacheManager(mMapView, writer);
try {
mgr = new CacheManager(mMapView, writer);
} catch (TileSourcePolicyException ex) {
Log.e(TAG, ex.getMessage());
return;
}
} else {
if (mgr==null)
mgr = new CacheManager(mMapView);
if (mgr==null) {
try {
mgr = new CacheManager(mMapView);
} catch (TileSourcePolicyException ex) {
Log.e(TAG, ex.getMessage());
return;
}
}
}
int zoommin = zoom_min.getProgress();
int zoommax = zoom_max.getProgress();
@@ -29,14 +29,15 @@
*/
public class DefaultConfigurationProvider implements IConfigurationProvider {

public static final String DEFAULT_USER_AGENT = "osmdroid";

protected long gpsWaitTime =20000;
protected boolean debugMode= false;
protected boolean debugMapView = false;
protected boolean debugTileProviders = false;
protected boolean debugMapTileDownloader=false;
protected boolean isMapViewHardwareAccelerated=true;
protected String userAgentValue="osmdroid";
protected String userAgentValue=DEFAULT_USER_AGENT;
protected String userAgentHttpHeader = "User-Agent";
private final Map<String, String> mAdditionalHttpRequestProperties = new HashMap<>();
protected short cacheMapTileCount = 9;
@@ -21,6 +21,7 @@
import org.osmdroid.tileprovider.modules.TileDownloader;
import org.osmdroid.tileprovider.tilesource.ITileSource;
import org.osmdroid.tileprovider.tilesource.OnlineTileSourceBase;
import org.osmdroid.tileprovider.tilesource.TileSourcePolicyException;
import org.osmdroid.util.BoundingBox;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.util.IterableWithSize;
@@ -71,11 +72,11 @@
protected Set<CacheManagerTask> mPendingTasks = new HashSet<>();
protected boolean verifyCancel = true;

public CacheManager(final MapView mapView) {
public CacheManager(final MapView mapView) throws TileSourcePolicyException {
this(mapView, mapView.getTileProvider().getTileWriter());
}

public CacheManager(final MapView mapView, IFilesystemCache writer) {
public CacheManager(final MapView mapView, IFilesystemCache writer) throws TileSourcePolicyException {
this(mapView.getTileProvider(), writer, (int) mapView.getMinZoomLevel(), (int) mapView.getMaxZoomLevel());
}

@@ -85,7 +86,8 @@ public CacheManager(final MapView mapView, IFilesystemCache writer) {
*/
public CacheManager(final MapTileProviderBase pTileProvider,
final IFilesystemCache pWriter,
final int pMinZoomLevel, final int pMaxZoomLevel) {
final int pMinZoomLevel, final int pMaxZoomLevel)
throws TileSourcePolicyException{
this(pTileProvider.getTileSource(), pWriter, pMinZoomLevel, pMaxZoomLevel);
}

@@ -94,11 +96,17 @@ public CacheManager(final MapTileProviderBase pTileProvider,
*/
public CacheManager(final ITileSource pTileSource,
final IFilesystemCache pWriter,
final int pMinZoomLevel, final int pMaxZoomLevel) {
final int pMinZoomLevel, final int pMaxZoomLevel)
throws TileSourcePolicyException{
mTileSource = pTileSource;
mTileWriter = pWriter;
mMinZoomLevel = pMinZoomLevel;
mMaxZoomLevel = pMaxZoomLevel;
if (pTileSource instanceof OnlineTileSourceBase) {
if (!((OnlineTileSourceBase) pTileSource).getTileSourcePolicy().acceptsBulkDownload()) {
throw new TileSourcePolicyException("This online tile source doesn't support bulk download");
}
}
}

/**
@@ -49,6 +49,11 @@ public Drawable downloadTile(final long pMapTileIndex, final int redirectCount,
return null;
}

final String userAgent = Configuration.getInstance().getUserAgentValue();
if (!pTileSource.getTileSourcePolicy().acceptsUserAgent(userAgent)) {
Log.e(IMapView.LOGTAG,"Please configure a relevant user agent; current value is: " + userAgent);
return null;
}
InputStream in = null;
OutputStream out = null;
HttpURLConnection c=null;
@@ -72,7 +77,7 @@ public Drawable downloadTile(final long pMapTileIndex, final int redirectCount,
c = (HttpURLConnection) new URL(tileURLString).openConnection();
}
c.setUseCaches(true);
c.setRequestProperty(Configuration.getInstance().getUserAgentHttpHeader(),Configuration.getInstance().getUserAgentValue());
c.setRequestProperty(Configuration.getInstance().getUserAgentHttpHeader(), userAgent);
for (final Map.Entry<String, String> entry : Configuration.getInstance().getAdditionalHttpRequestProperties().entrySet()) {
c.setRequestProperty(entry.getKey(), entry.getValue());
}
@@ -5,8 +5,17 @@
public abstract class OnlineTileSourceBase extends BitmapTileSourceBase {

private final String mBaseUrls[];

/**
* @since 6.1.0
*/
private final Semaphore mSemaphore;

/**
* @since 6.1.0
*/
private final TileSourcePolicy mTileSourcePolicy;

public OnlineTileSourceBase(final String aName,
final int aZoomMinLevel, final int aZoomMaxLevel, final int aTileSizePixels,
final String aImageFilenameEnding, final String[] aBaseUrl) {
@@ -20,7 +29,7 @@ public OnlineTileSourceBase(final String aName,
final int aZoomMinLevel, final int aZoomMaxLevel, final int aTileSizePixels,
final String aImageFilenameEnding, final String[] aBaseUrl, String copyyright) {
this(aName, aZoomMinLevel, aZoomMaxLevel, aTileSizePixels,
aImageFilenameEnding, aBaseUrl, copyyright, 0);
aImageFilenameEnding, aBaseUrl, copyyright, new TileSourcePolicy());
}

/**
@@ -32,17 +41,18 @@ public OnlineTileSourceBase(final String aName,
* @param pImageFilenameEnding the file name extension used when constructing the filename
* @param pBaseUrl the base url(s) of the tile server used when constructing the url to download the tiles
* @param pCopyright the source copyright
* @param pMaxConcurrent the maximum number of concurrent downloads
* @param pTileSourcePolicy tile source policy
*/
public OnlineTileSourceBase(final String pName,
final int pZoomMinLevel, final int pZoomMaxLevel, final int pTileSizePixels,
final String pImageFilenameEnding, final String[] pBaseUrl, final String pCopyright,
final int pMaxConcurrent) {
final TileSourcePolicy pTileSourcePolicy) {
super(pName, pZoomMinLevel, pZoomMaxLevel, pTileSizePixels,
pImageFilenameEnding, pCopyright);
mBaseUrls = pBaseUrl;
if (pMaxConcurrent > 0) {
mSemaphore = new Semaphore(pMaxConcurrent, true);
mTileSourcePolicy = pTileSourcePolicy;
if (mTileSourcePolicy.getMaxConcurrent() > 0) {
mSemaphore = new Semaphore(mTileSourcePolicy.getMaxConcurrent(), true);
} else {
mSemaphore = null;
}
@@ -76,4 +86,11 @@ public void release() {
}
mSemaphore.release();
}

/**
* @since 6.1.0
*/
public TileSourcePolicy getTileSourcePolicy() {
return mTileSourcePolicy;
}
}
@@ -100,7 +100,8 @@ public static int removeTileSources(final String aRegex) {
0, 19, 256, ".png", new String[] {
"https://a.tile.openstreetmap.org/",
"https://b.tile.openstreetmap.org/",
"https://c.tile.openstreetmap.org/" },"© OpenStreetMap contributors", 2);
"https://c.tile.openstreetmap.org/" },"© OpenStreetMap contributors",
new TileSourcePolicy(2, false, false));
// max concurrent thread number is 2 (cf. https://operations.osmfoundation.org/policies/tiles/)

public static final OnlineTileSourceBase PUBLIC_TRANSPORT = new XYTileSource(
@@ -0,0 +1,59 @@
package org.osmdroid.tileprovider.tilesource;

import org.osmdroid.config.DefaultConfigurationProvider;

/**
* Online Tile Source Usage Policy, including<ul>
* <li>the max number of concurrent downloads</li>
* <li>if it accepts a meaningless user agent</li>
* <li>if it accepts bulk downloads</li>
* </ul>
* @since 6.1.0
* @author Fabrice Fontaine
*/
public class TileSourcePolicy {

/**
* maximum number of concurrent downloads
*/
private final int mMaxConcurrent;

/**
* accepts bulk download
*/
private final boolean mAcceptsBulkDownload;

/**
* accepts meaningless default user agent
*/
private final boolean mAcceptsMeaninglessUserAgent;

public TileSourcePolicy() {
this(0, true, true);
}

public TileSourcePolicy(final int pMaxConcurrent,
final boolean pAcceptsBulkDownload,
final boolean pAcceptsMeaninglessUserAgent) {
mMaxConcurrent = pMaxConcurrent;
mAcceptsBulkDownload = pAcceptsBulkDownload;
mAcceptsMeaninglessUserAgent = pAcceptsMeaninglessUserAgent;
}

public int getMaxConcurrent() {
return mMaxConcurrent;
}

public boolean acceptsBulkDownload() {
return mAcceptsBulkDownload;
}

public boolean acceptsUserAgent(final String pUserAgent) {
if (mAcceptsMeaninglessUserAgent) {
return true;
}
return pUserAgent != null
&& pUserAgent.trim().length() > 0
&& (!pUserAgent.equals(DefaultConfigurationProvider.DEFAULT_USER_AGENT));
}
}
@@ -0,0 +1,13 @@
package org.osmdroid.tileprovider.tilesource;

/**
* Exception dedicated to the enforcement of online tile source usage policies
* @since 6.1.0
* @author Fabrice Fontaine
*/
public class TileSourcePolicyException extends RuntimeException{

public TileSourcePolicyException(final String pMessage) {
super(pMessage);
}
}
@@ -10,15 +10,15 @@
public XYTileSource(final String aName, final int aZoomMinLevel,
final int aZoomMaxLevel, final int aTileSizePixels, final String aImageFilenameEnding,
final String[] aBaseUrl) {
this(aName, aZoomMinLevel, aZoomMaxLevel, aTileSizePixels,
aImageFilenameEnding, aBaseUrl,null);
super(aName, aZoomMinLevel, aZoomMaxLevel, aTileSizePixels,
aImageFilenameEnding, aBaseUrl);
}

public XYTileSource(final String aName, final int aZoomMinLevel,
final int aZoomMaxLevel, final int aTileSizePixels, final String aImageFilenameEnding,
final String[] aBaseUrl, final String copyright) {
this(aName, aZoomMinLevel, aZoomMaxLevel, aTileSizePixels,
aImageFilenameEnding, aBaseUrl, copyright, 0);
super(aName, aZoomMinLevel, aZoomMaxLevel, aTileSizePixels,
aImageFilenameEnding, aBaseUrl, copyright);
}

/**
@@ -27,9 +27,10 @@ public XYTileSource(final String aName, final int aZoomMinLevel,
*/
public XYTileSource(final String aName, final int aZoomMinLevel,
final int aZoomMaxLevel, final int aTileSizePixels, final String aImageFilenameEnding,
final String[] aBaseUrl, final String copyright, final int pMaxConcurrent) {
final String[] aBaseUrl, final String copyright,
final TileSourcePolicy pTileSourcePolicy) {
super(aName, aZoomMinLevel, aZoomMaxLevel, aTileSizePixels,
aImageFilenameEnding, aBaseUrl,copyright, pMaxConcurrent);
aImageFilenameEnding, aBaseUrl,copyright, pTileSourcePolicy);
}

@Override

0 comments on commit 5dcf44e

Please sign in to comment.
You can’t perform that action at this time.