We are just starting to work with Google Maps on Android and have a GeoServer set up to provide tiles which we would like to add as overlay on the map. So far, I have followed a few tutorials and references to get started.
The problem: While the url that I am generating in the getTileUrl
function in the TileProviderFactory
does indeed return a png image when I set a breakpoint and copy and paste the url into a browser, it does not load onto the map as an overlay on the Android device. There are no errors being thrown from what I can see and after reading this I am not sure if there will be any as they have indicated that the errors are being muted.
What I am wondering is if you can see any immediate issues in my code or have any suggestions for debugging where I will be able to tell if the application is actually communicating with my GeoServer to retrieve the image or not. I've looked at the log on the GeoServer and it seems as though only my browser requests are going through and it's not receiving any requests from Android (it's a bit difficult to tell because we have other applications using the server as well). The Android phone is connected by both wifi and cell and has gps enabled. As a last resort I have tried changing the tile overlay zIndex and setting it to visible but this didn't seem to make any difference.
EDIT: Android device is definitely NOT communicating with GeoServer at this point.
EDIT 2: Able to load static images from websites (like this) as overlays and found that I am getting the following exception on testing out an HTTP Request to the formed URL:
W/System.err(10601): java.net.SocketException: The operation timed out
W/System.err(10601): at org.apache.harmony.luni.platform.OSNetworkSystem
.connectStreamWithTimeoutSocketImpl(Native Method)
W/System.err(10601): at org.apache.harmony.luni.net.PlainSocketImpl
.connect(PlainSocketImpl.java:244)
W/System.err(10601): at org.apache.harmony.luni.net.PlainSocketImpl
.connect(PlainSocketImpl.java:533)
W/System.err(10601): at java.net.Socket
.connect(Socket.java:1074)
W/System.err(10601): at org.apache.http.conn.scheme.PlainSocketFactory
.connectSocket(PlainSocketFactory.java:119)
Thanks.
public class MapTestActivity extends FragmentActivity
implements LocationListener, LocationSource{
private GoogleMap mMap;
private OnLocationChangedListener mListener;
private LocationManager locationManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map_test);
setupLocationManager();
setupMapIfNeeded();
}
private void setupLocationManager() {
this.locationManager =
(LocationManager) getSystemService(LOCATION_SERVICE);
if (locationManager != null) {
boolean gpsIsEnabled = locationManager.isProviderEnabled(
LocationManager.GPS_PROVIDER);
boolean networkIsEnabled = locationManager.isProviderEnabled(
LocationManager.NETWORK_PROVIDER);
if(gpsIsEnabled) {
this.locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 5000L, 10F, this);
}
else if(networkIsEnabled) {
this.locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 5000L, 10F, this);
}
else {
//Show an error dialog that GPS is disabled...
}
}
else {
// Show some generic error dialog because
// something must have gone wrong with location manager.
}
}
private void setupMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the
// map.
if (mMap == null) {
// Try to obtain the map from the SupportMapFragment.
mMap = ((SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map)).getMap();
// Check if we were successful in obtaining the map.
if (mMap != null) {
setUpMap();
}
mMap.setLocationSource(this);
}
}
private void setUpMap() {
// TODO Auto-generated method stub
mMap.setMyLocationEnabled(true);
TileProvider geoServerTileProvider = TileProviderFactory
.getGeoServerTileProvider();
TileOverlay geoServerTileOverlay = mMap.addTileOverlay(
new TileOverlayOptions()
.tileProvider(geoServerTileProvider)
.zIndex(10000)
.visible(true));
}
// Non-relevant listener methods removed
}
public class TileProviderFactory {
public static GeoServerTileProvider getGeoServerTileProvider() {
String baseURL = "mytileserver.com";
String version = "1.3.0";
String request = "GetMap";
String format = "image/png";
String srs = "EPSG:900913";
String service = "WMS";
String width = "256";
String height = "256";
String styles = "";
String layers = "wtx:road_hazards";
final String URL_STRING = baseURL +
"&LAYERS=" + layers +
"&VERSION=" + version +
"&SERVICE=" + service +
"&REQUEST=" + request +
"&TRANSPARENT=TRUE&STYLES=" + styles +
"&FORMAT=" + format +
"&SRS=" + srs +
"&BBOX=%f,%f,%f,%f" +
"&WIDTH=" + width +
"&HEIGHT=" + height;
GeoServerTileProvider tileProvider =
new GeoServerTileProvider(256,256) {
@Override
public synchronized URL getTileUrl(int x, int y, int zoom) {
try {
double[] bbox = getBoundingBox(x, y, zoom);
String s = String.format(Locale.US, URL_STRING, bbox[MINX],
bbox[MINY], bbox[MAXX], bbox[MAXY]);
Log.d("GeoServerTileURL", s);
URL url = null;
try {
url = new URL(s);
}
catch (MalformedURLException e) {
throw new AssertionError(e);
}
return url;
}
catch (RuntimeException e) {
Log.d("GeoServerTileException",
"getTile x=" + x + ", y=" + y +
", zoomLevel=" + zoom +
" raised an exception", e);
throw e;
}
}
};
return tileProvider;
}
}
public abstract class GeoServerTileProvider extends UrlTileProvider{
// Web Mercator n/w corner of the map.
private static final double[] TILE_ORIGIN =
{-20037508.34789244, 20037508.34789244};
//array indexes for that data
private static final int ORIG_X = 0;
private static final int ORIG_Y = 1; // "
// Size of square world map in meters, using WebMerc projection.
private static final double MAP_SIZE = 20037508.34789244 * 2;
// array indexes for array to hold bounding boxes.
protected static final int MINX = 0;
protected static final int MINY = 1;
protected static final int MAXX = 2;
protected static final int MAXY = 3;
public GeoServerTileProvider(int width, int height) {
super(width, height);
}
// Return a web Mercator bounding box given tile x/y indexes and a zoom
// level.
protected double[] getBoundingBox(int x, int y, int zoom) {
double tileSize = MAP_SIZE / Math.pow(2, zoom);
double minx = TILE_ORIGIN[ORIG_X] + x * tileSize;
double maxx = TILE_ORIGIN[ORIG_X] + (x+1) * tileSize;
double miny = TILE_ORIGIN[ORIG_Y] - (y+1) * tileSize;
double maxy = TILE_ORIGIN[ORIG_Y] - y * tileSize;
double[] bbox = new double[4];
bbox[MINX] = minx;
bbox[MINY] = miny;
bbox[MAXX] = maxx;
bbox[MAXY] = maxy;
return bbox;
}
}
This turned out to be a network issue and completely unrelated to my implementation which is "correct". I guess this question will serve well as an example for others who are getting started with Android + GeoServer implementation so I will leave it up.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With