MapApp5 : MapView and Activity
Welcome to the fifth and final part of my tutorial on how to create a map app for Android without using Google™ APIs :).
Series outline:
- Series aim.
- Theory you need to know.
- App design.
- Writing a TilesManager.
- Writing a TilesProvider.
- Seeing results with MapView.
- Adding web support.
- Creating MapView in XML.
- Extra: Fuchs Maps.
______________________________________
So up until now we didn’t see any results :(, this is where everything changes :D.
In this part we’ll write three classes:
MapView: a custom view to render and manipulate the map.
MapAppActivity: the main (and only) activity for the app, mainly creates the MapView and handles activity state changes.
MapViewLocationListener : An extended LocationListener that knows how to deal with a MapView.
MapView:
First thing you should know about this view is that it contains two gps locations, one for the actual phone gps location and the other one for the view center. The first is to draw a marker the the users position and the second is to freely move\drag the map, so the longitude,latitude coordinates of the pixel at the center of the view is represented by the second gps location. If we want to enable auto follow we just make the second gps location match the first one, this way the user’s location will always be at the center of the view.
Let’s start with the fields and the constructor of MapView:
package com.mapapp.views; import java.util.Collection; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; import android.location.Location; import android.view.MotionEvent; import android.view.View; import com.mapapp.helpers.PointD; import com.mapapp.tileManagement.Tile; import com.mapapp.tileManagement.TilesManager; import com.mapapp.tileManagement.TilesProvider; public class MapView extends View { // Needed to pass to View constructor protected Context context; // MapView dimensions protected int viewWidth, viewHeight; // Provides us with tiles protected TilesProvider tileProvider; // Handles calculations protected TilesManager tileManager; // Different paints protected Paint fontPaint; protected Paint bitmapPaint = new Paint(); protected Paint circlePaint = new Paint(); // The location of the view center in longitude, latitude protected PointD seekLocation = new PointD(0, 0); // Location of the phone using Gps data protected Location gpsLocation = null; // If true then seekLocation will always match gpsLocation protected boolean autoFollow = false; // An image to draw at the phone's position protected Bitmap positionMarker; // touch position values kept for panning\dragging protected PointD lastTouchPos = new PointD(-1, -1); public MapView(Context context, int viewWidth, int viewHeight, TilesProvider tilesProvider, Bitmap positionMarker) { super(context); this.context = context; // Tiles provider is passed not created. // The idea is to hide the actual tiles source from the view // This way the view doesn't care whether the source is a database or the internet this.tileProvider = tilesProvider; // These values will be used later this.viewWidth = viewWidth; this.viewHeight = viewHeight; // Get the marker image this.positionMarker = positionMarker; // Creating a TilesManager assuming that the tile size is 256*256. // You might want to pass tile size as a parameter or even calculate it somehow tileManager = new TilesManager(256, viewWidth, viewHeight); // Initializes paints initPaints(); // Fetching tiles from the tilesProvider fetchTiles(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Setting width,height that was passed in the constructor as the view's dimensions setMeasuredDimension(viewWidth, viewHeight); }
The code is well commented I guess, since we’re extending a View we must override onMeasere and call
setMeasuredDimension inside it passing the dimensions from the constructors. This is to ensure that the view has the desired width and height.
Now to initPaints and fetchTiles:
void initPaints() { // Font paint is used to draw text fontPaint = new Paint(); fontPaint.setColor(Color.DKGRAY); fontPaint.setShadowLayer(1, 1, 1, Color.BLACK); fontPaint.setTextSize(20); // Used to draw a semi-transparent circle at the phone's gps location circlePaint.setARGB(70, 170, 170, 80); circlePaint.setAntiAlias(true); } void fetchTiles() { // Update tilesManager to have the center of the view as its location tileManager.setLocation(seekLocation.x, seekLocation.y); // Get the visible tiles indices as a Rect Rect visibleRegion = tileManager.getVisibleRegion(); // Tell tiles provider what tiles we need and which zoom level. // The tiles will be stored inside the tilesProvider. // We can get those tiles later when drawing the view tileProvider.fetchTiles(visibleRegion, tileManager.getZoom()); }
Hopefully you remember how TilesManager works, it takes a location and returns the range of tiles needed to fill the MapView, so we first set the location of the TilesManager to the center of our MapView (Longitude,Latitude coordinates) then we get the result as a rectangle, then we pass this rectangle to the TilesProvider which fetches all the tiles included in the rectangle and stores them in a Hashtable inside itself.
Next thing is drawing the map, since we’re extending a View we override onDraw to draw out view contents, onDraw takes a Canvas object as parameter, the canvas object is used to draw text\images\shapes on the view.
The problem here is how to figure out the right position to draw the tiles. It’s easy if we were drawing the full world map a, we just convert longitude & latitude to pixels and draw them on the map.
In our case we are only drawing part of the world map and we need to have seekLocation at the center of the MapView, so in onDraw we calculate an offset value and pass it to all other drawing functions.
here’s the code for that and I guess it’s well commented, an image after the code should explain more.
@Override protected void onDraw(Canvas canvas) { // Clear the view to grey canvas.drawARGB(255, 100, 100, 100); // To draw the map we need to find the position of the pixel representing the center of the view. // We need the position to be relative to the full world map, lets call this pixel position "pix" // pix.x will range from 0 to (2^zoom)*tileSize-1, same for pix.y // To draw anything on the map we subtract pix from the original position // It's just like dragging the map so that the pixel representing the gps location gets into the center of the view // In a square world map, // we need to know pix location as two values from 0.0 to 1.0 PointD pixRatio = TilesManager.calcRatio(seekLocation.x, seekLocation.y); // Full world map width in pixels int mapWidth = tileManager.mapSize() * 256; Point pix = new Point((int) (pixRatio.x * mapWidth), (int) (pixRatio.y * mapWidth)); /* * Subtracting pix from each tile position will result in pix being drawn at the top left corner of the view * To drag it to the center we add (viewWidth/2, viewHeight/2) to the final result * pos.x = pos.x - pix.x + viewWidth/2f * pos.x = pox.x - (pix.x - viewWidth/2f) * ---> offset.x = (pix.x - viewWidth/2f) * same for offset.y */ Point offset = new Point((int) (pix.x - viewWidth / 2f), (int) (pix.y - viewHeight / 2f)); // offset is now ready to use // Drawing tiles in a separate function to make the code more readable drawTiles(canvas, offset); // Draw the marker that pinpoints the user's location drawMarker(canvas, offset); }

Top left: we drag the map to make the marker have the position of (0,0) in MapView
Top right : we need to center the marker in MapView
Bottom left : the final result we want
So to draw anything in the map we first obtain its position in the full world map then subtract offset from that position, for tiles we just multiply the index of the tile with tile width in pixels, so tiles x positions are like: 0, 256, 512, 1024… since in our case tileWidth is equal to 256.
void drawTiles(Canvas canvas, Point offset) { // Get tiles from the Hashtable inside tilesProvider Collection tilesList = tileProvider.getTiles().values(); // x,y are the calculated offset // Go trough all the available tiles for (Tile tile : tilesList) { // We act as if we're drawing a map of the whole world at a specific // zoom level // The top left corner of the map occupies the pixel (0,0) of the // view int tileSize = tileManager.getTileSize(); long tileX = tile.x * tileSize; long tileY = tile.y * tileSize; // Subtract offset from the previous calculations long finalX = tileX - offset.x; long finalY = tileY - offset.y; // Draw the bitmap of the tiles using a simple paint canvas.drawBitmap(tile.img, finalX, finalY, bitmapPaint); } }
Important note: to draw a bitmap we used the simplest paint we can create, using other paints will slow down drawing!.
For the marker it’s the same, we just convert longitude latitude to pixels and subtract offset:
void drawMarker(Canvas canvas, Point offset) { // x,y are the calculated offset // Proceed only if a gps fix is available if (gpsLocation != null) { // Get marker position in pixels as if we're going to draw it on a // world map where the top left corner of the map occupies the (0,0) // pixel of the view Point markerPos = tileManager.lonLatToPixelXY(gpsLocation.getLongitude(), gpsLocation.getLatitude()); // Add offset to the marker position int markerX = markerPos.x - offset.x; int markerY = markerPos.y - offset.y; // Draw the marker and make sure you draw the center of the marker // at the marker location canvas.drawBitmap(positionMarker, markerX - positionMarker.getWidth() / 2, markerY - positionMarker.getHeight() / 2, bitmapPaint); // Around the marker we will draw a circle representing the accuracy of the gps fix // We first calculate its radius // Calculate how many meters one pixel represents float ground = (float) tileManager.calcGroundResolution(gpsLocation.getLatitude()); // Location.getAccuracy() returns the accuracy in meters. float rad = gpsLocation.getAccuracy() / ground; canvas.drawCircle(markerX, markerY, rad, circlePaint); // Just drawing location info int pen = 1; canvas.drawText("lon:" + gpsLocation.getLongitude(), 0, 20 * pen++, fontPaint); canvas.drawText("lat:" + gpsLocation.getLatitude(), 0, 20 * pen++, fontPaint); canvas.drawText("alt:" + gpsLocation.getAltitude(), 0, 20 * pen++, fontPaint); canvas.drawText("Zoom:" + tileManager.getZoom(), 0, 20 * pen++, fontPaint); } }
We’re done now with the drawing part, we need to enable the user to drag the map:
Update 28/10/2012: Instead of reinventing the wheel there’s a class called GestureDetector that can detect many touch actions like clicking, double clicking, scrolling, I didn’t update the code here because that will break the connection with the post comments, so here’s the better implementation of MapView: MapView.java
and here’s the original unmodified code 🙂 :
@Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN) { // Keep touch position for later use (dragging) lastTouchPos.x = (int) event.getX(); lastTouchPos.y = (int) event.getY(); return true; } else if (action == MotionEvent.ACTION_MOVE) { autoFollow = false; PointD current = new PointD(event.getX(), event.getY()); // Find how many pixels the users finger moved in both x and y PointD diff = new PointD(current.x - lastTouchPos.x, current.y - lastTouchPos.y); // In a full wolrd map, get the position of the center of the view in pixels Point pixels1 = tileManager.lonLatToPixelXY(seekLocation.x, seekLocation.y); // Subtract diff from that position Point pixels2 = new Point(pixels1.x - (int) diff.x, pixels1.y - (int) diff.y); // Reconvert the final result to longitude, latitude point PointD newSeek = tileManager.pixelXYToLonLat((int) pixels2.x, (int) pixels2.y); // Finally move the center of the view to the new location seekLocation = newSeek; // Refresh the view fetchTiles(); invalidate(); // Causes the view to redraw itself // Prepare for the next drag event lastTouchPos.x = current.x; lastTouchPos.y = current.y; return true; } return super.onTouchEvent(event); }
The MapView class is almost finished, we will just add some small functions, the name should explain what each one does:
// Fetch the tiles then draw, don't call to often public void refresh() { fetchTiles(); invalidate(); } // Like refresh but called from a non UI thread public void postRefresh() { fetchTiles(); postInvalidate(); } // Simply sets seek location to gpsLocation (if exists) public void followMarker() { if (gpsLocation != null) { seekLocation.x = gpsLocation.getLongitude(); seekLocation.y = gpsLocation.getLatitude(); autoFollow = true; fetchTiles(); invalidate(); } } public void zoomIn() { tileManager.zoomIn(); onMapZoomChanged(); } public void zoomOut() { tileManager.zoomOut(); onMapZoomChanged(); } protected void onMapZoomChanged() { tileProvider.clear(); fetchTiles(); invalidate(); } // Returns the gps coordinates of the user public Location getGpsLocation() { return gpsLocation; } // Returns the gps coordinates of our view center public PointD getSeekLocation() { return seekLocation; } // Centers the given gps coordinates in our view public void setSeekLocation(double longitude, double latitude) { seekLocation.x = longitude; seekLocation.y = latitude; } // Sets the marker position public void setGpsLocation(Location location) { setGpsLocation(location.getLongitude(), location.getLatitude(), location.getAltitude(), location.getAccuracy()); } // Sets the marker position public void setGpsLocation(double longitude, double latitude, double altitude, float accuracy) { if (gpsLocation == null) gpsLocation = new Location(""); gpsLocation.setLongitude(longitude); gpsLocation.setLatitude(latitude); gpsLocation.setAltitude(altitude); gpsLocation.setAccuracy(accuracy); if (autoFollow) followMarker(); } public int getZoom() { return tileManager.getZoom(); } public void setZoom(int zoom) { tileManager.setZoom(zoom); onMapZoomChanged(); }
Okay, that’s all for the MapView class, so this tutorial is almost over.
MapViewLocationListener:
a LocationListener is an interface that has a function we are interested in, it’s called
onLocationChanged(Location location);
Our location listener will implement this interface and whenever a change in location happens it will set the new location as the marker position in the MapView associated with this listener.
The full class code:
package com.mapapp.views; import android.location.Location; import android.location.LocationListener; import android.os.Bundle; public class MapViewLocationListener implements LocationListener { MapView mapView; boolean stopped = false; public MapViewLocationListener(MapView mapView) { this.mapView = mapView; } @Override public void onLocationChanged(Location location) { if (!stopped && location != null) { // Set location and update the mapView mapView.setGpsLocation(location.getLongitude(), location.getLatitude(), location.getAltitude(), location.getAccuracy()); mapView.postInvalidate(); } } public void stop() { stopped = true; mapView = null; } @Override public void onProviderDisabled(String provider) { } @Override public void onProviderEnabled(String provider) { } @Override public void onStatusChanged(String provider, int status, Bundle extras) { } }
MapAppActivity:
Very simple activity, it creates TilesProvider then creates a MapView passing the TilesProvider in the constructor, then the MapViewLocationListener is created and the MapView is passed in the constructor, what makes the class longer is adding activity state management, you don’t want the app to be reset when the user rotates his mobile, you also want the app to preserve the location in the map and the zoom level when another activity comes in front of your app. To do so we’ll override two functions in the activity: onSaveInstanceState, onRestoreInstanceState.
We also want the app to keep the seek location and zoom level of the MapView when the user closes the app and re opens it, to do so we will use the SharedPreferences.
Just one important note, I used onResume to initialize the app instead of on create for a simple reason, in the method onPause I’m releasing all the resources of the app, so I had to reload them when the activity starts again, the problem with onCreate is that it’s not always called, there are scenarios where the activity is started with the function onResume.
If you follow this link and scroll down to figure1 you see that an arrow goes from onPause (Where we unload our resources) directly to onResume, onCreate wasn’t called.
package com.mapapp.main; import android.app.Activity; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.location.Location; import android.location.LocationManager; import android.os.Bundle; import android.os.Environment; import android.view.Display; import android.view.KeyEvent; import com.mapapp.helpers.PointD; import com.mapapp.mapapp.R; import com.mapapp.tileManagement.TilesProvider; import com.mapapp.views.MapView; import com.mapapp.views.MapViewLocationListener; public class MapAppActivity extends Activity { // Constant strings used in onSaveInstanceState, onRestoreInstanceState private final class Save { public final static String GPS_LON = "gpsLon"; public final static String GPS_LAT = "gpsLAT"; public final static String GPS_ALT = "gpsALT"; public final static String GPS_ACC = "gpsAcc"; } // Constant strings to save settings in SharedPreferences // Also used for restoring settings private final class Pref { public final static String SEEK_LON = "seek_lon"; public final static String SEEK_LAT = "seek_lat"; public final static String ZOOM = "zoom"; } // Our only view, created in code MapView mapView; // Provides us with Tiles objects, passed to MapView TilesProvider tilesProvider; // Updates marker location in MapView MapViewLocationListener locationListener; Location savedGpsLocation; @Override protected void onResume() { // Create MapView initViews(); // Restore zoom and location data for the MapView restoreMapViewSettings(); // Creating and registering the location listener locationListener = new MapViewLocationListener(mapView); LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener); // Set our MapView as the main view for the activity setContentView(mapView); // Never ever forget this 🙂 super.onResume(); } void initViews() { // Creating the bitmap of the marker from the resources Bitmap marker = BitmapFactory.decodeResource(getResources(), R.drawable.marker); // Creating our database tilesProvider to pass it to our MapView String path = Environment.getExternalStorageDirectory() + "/mapapp/world.sqlitedb"; tilesProvider = new TilesProvider(path); // Creating the mapView and make sure it fills the screen Display display = getWindowManager().getDefaultDisplay(); mapView = new MapView(this, display.getWidth(), display.getHeight(), tilesProvider, marker); // If a location was saved while pausing the app then use it. if (savedGpsLocation != null) mapView.setGpsLocation(savedGpsLocation); // Update and draw the map view mapView.refresh(); } @Override protected void onPause() { // Save settings before leaving saveMapViewSettings(); // Mainly releases the MapView pointer inside the listener locationListener.stop(); // Unregistering our listener LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); locationManager.removeUpdates(locationListener); // Closes the source of the tiles (Database in our case) tilesProvider.close(); // Clears the tiles held in the tilesProvider tilesProvider.clear(); // Release mapView pointer mapView = null; super.onPause(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // Zooming if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_Z) { mapView.zoomIn(); return true; } // Zooming else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_X) { mapView.zoomOut(); return true; } // Enable auto follow if (keyCode == KeyEvent.KEYCODE_H || keyCode == KeyEvent.KEYCODE_FOCUS) { mapView.followMarker(); return true; } // Simulate being at some location, for testing only else if (keyCode == KeyEvent.KEYCODE_M || keyCode == KeyEvent.KEYCODE_MENU) { mapView.setGpsLocation(46.142578, -20.841015, 0, 182); mapView.invalidate(); return false; } return super.onKeyDown(keyCode, event); } // Called manually to restore settings from SharedPreferences void restoreMapViewSettings() { SharedPreferences pref = getSharedPreferences("View_Settings", MODE_PRIVATE); double lon, lat; int zoom; lon = Double.parseDouble(pref.getString(Pref.SEEK_LON, "0")); lat = Double.parseDouble(pref.getString(Pref.SEEK_LAT, "0")); zoom = pref.getInt(Pref.ZOOM, 0); mapView.setSeekLocation(lon, lat); mapView.setZoom(zoom); mapView.refresh(); } // Called manually to save settings in SharedPreferences void saveMapViewSettings() { SharedPreferences.Editor editor = getSharedPreferences("View_Settings", MODE_PRIVATE).edit(); PointD seekLocation = mapView.getSeekLocation(); editor.putString(Pref.SEEK_LON, Double.toString(seekLocation.x)); editor.putString(Pref.SEEK_LAT, Double.toString(seekLocation.y)); editor.putInt(Pref.ZOOM, mapView.getZoom()); editor.commit(); } @Override protected void onSaveInstanceState(Bundle outState) { if (mapView.getGpsLocation() != null) { outState.putDouble(Save.GPS_LON, mapView.getGpsLocation().getLongitude()); outState.putDouble(Save.GPS_LAT, mapView.getGpsLocation().getLatitude()); outState.putDouble(Save.GPS_ALT, mapView.getGpsLocation().getAltitude()); outState.putFloat(Save.GPS_ACC, mapView.getGpsLocation().getAccuracy()); } super.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { double gpsLon, gpsLat, gpsAlt; float gpsAcc; gpsLon = savedInstanceState.getDouble(Save.GPS_LON, 999); gpsLat = savedInstanceState.getDouble(Save.GPS_LAT, 999); gpsAlt = savedInstanceState.getDouble(Save.GPS_ALT, 999); gpsAcc = savedInstanceState.getFloat(Save.GPS_ACC, 999); if (gpsLon != 999 && gpsLat != 999 && gpsAlt != 999 && gpsAcc != 999) { savedGpsLocation = new Location(LocationManager.GPS_PROVIDER); savedGpsLocation.setLongitude(gpsLon); savedGpsLocation.setLatitude(gpsLat); savedGpsLocation.setAltitude(gpsAlt); savedGpsLocation.setAccuracy(gpsAcc); } super.onRestoreInstanceState(savedInstanceState); } }
Last things you need to do:
- add this image to your drawable folders
- add this permission to your AndroidManifest.xml<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION” />
- copy the world.sqlite database to /sdcard/mapapp/
The app should be working fine by now :D.
Here’s the final source code for the app : MapApp_Final Source Code.zip
And here’s the final Apk file: MapApp_Final.apk
I really hope you learned something from this tutorial 😀
It feels good to have your own app that displays a map and a location 😀
Please feel free to have any suggestions or notes about this tutorial since it’s my first on in Android programming :).
Want to support online maps? head to MapApp6: Web support :D.!
I want to download the map as sqlitedb foramt.please help me.
hlaingwintunn@gmail.com
There’s a project called Mobile Atlas Creator that can do that and more
http://mobac.sourceforge.net/
You can find lots of tutorials on the internet.
Just make sure you don’t download large areas or you will be blocked by maps servers.
Also make sure you read the the usage policy provided by the server you’re downloading the tiles from.
I want to moidfy this app.
Can I add point ,lines and polygon on this mapview?And then
I want to display extra information with popup box.
You can do that with ease, the map tiles are drawn onto a Canvas object, A Canvas has many draw methods so basically you can draw anything after the map is drawn.
You can make a class called for example MapOverlay that defines some special drawing methods, then in the MapView define an ArrayList of MapOverlays.
After you draw the map you loop through all the MapOverlays and draw them on the map.
Here’s a quick code:
in MapView:
Hope that helps 🙂
Thank you for your answer.
Thank you for your answer.
I have a tiles map(sqlitedb format).So I want to use my map instead of world map. My map have a tiles table with four fields(id TEXT,s int ,t int ,image BLOB).
id s t image
BDCACCACD 0 0 PNG
BDCACCABC 0 0 PNG
…………. etc
How do I work?
How to use this map instead of world map?Please sample code….
How to know maxlatitude,minlatitude,maxlongitude,minlongitude and radius for my map?
My map is yangon city map.I want to use this map on my android application.
Please help me.
Your map is also divided into tiles but the indexing is different, If you can download the map again using MobileAtlasCreator and choose the output format “RMaps format” the map will work fine without changing the code (Highly recommended), if that’s not possible you will need to know the following:
The length of the index indicates the zoom level, for example “BDCACCACD” is a tile at the zoom level 9

So in each zoom level you have four tiles:
The index of the green tile above should be AD
If you want to use the same code in the tutorial you’ll have to convert from one indexing method to another,
First let’s agree that A=”00″ (no x or y offset), B=”10″ (offset only in x), C=”01″ (offset in y), D=”11″ (offset in both x and y)
let’s say you have the following tile T(x,y,z)=(11,5,3) and you want to convert it to the ABCD format, first you convert x,y to binary representation with z+1 digits (four digits here)
Bin(x) = 1011
Bin(y) = 0101
Now if we merge two digits that have the index i from the binary representation
i0 = Dec(“10“) = B
i1 = Dec(“01“) = C
i2 = Dec(“10“) = B
i3 = Dec(“11“) = D
The final index we need is BCBD, that’s what you should query for in the database,
About the min\max coordinates it’s not easy to get these values and it won’t even be accurate, my guess is that you don’t need those values, just treat the map you have as any other map since the quad tree will take care of organizing the tiles for you.
Sorry for the delay in my response but it’s college exams week :(.
hi Fuchs, I found out my location using https://www.openstreetmap.org website…
Please take a look at this screenshot:
I export directly from there. But it seems not as “RMaps format” you just said. Can we use it?
And another thing i found,
My location on that map seems not very detail.
Logically if I go out of the building and turning on my GPS,
of course the coordinates would be updated each time i move,
right? So although the map is not clearly detail, the red-pin (marker) showing where I am still could put above that map right?
sorry i forgot to put the screenshot,

Here please:
Thank you for your reply.
I download the map as sqlitedb format using MobileAtlasCreator.Downloaded map is similar world map in this tutorial.So I used this map instead of world map because of your guideline.
So thank you very much.But ,when I start run the application ,the map is display smaller than the screen size.
I want to display the map with full screen.
How to modify or repair in this tutorial?
please help me.
1-If you are in zoom level 1 or 2 the map might not be large enough to fill the screen, you just need to zoom in more.
2-The tutorial assumes that all tiles are 256*256 images, the tiles you downloaded might be smaller, in the code try getting a single tile as a bitmap and check its dimensions, if they are actually smaller you have to change:
If the database you have isn’t large you can send it to me and I’ll see how to deal with the issue.
Thank you so much.I will send the small size database.I use the android emulator version 2.3.4.(screen size 4, storage 2GB)
what is your email address?
I’m very sorry for your time consuming.
Okay I tried the map you sent me and it works fine, the tiles are correctly sized, the space you find is preserved for tiles that are not available in the database, in other words:tiles you didn’t download.
There’s nothing you can really do, you can’t display what’s not there!
As I said before zooming in more will make the map fill the screen (If you download the tiles at higher zoom levels of course).
Since you’re downloading the tiles for a city you can select more zoom levels.
Hi Fuchs,
Thank for remember,I have a idea.let , my downloaded database is high zoom level(1 to 15),I used this zoom level for my application within zoom level 5 to 15. when i start run the application,the zoom level must start from level 5. I don’t want to use zoom level 1 to 4.
Can I ok ?Please give me advice.
And then ,I need to display the information onto mapview.
How to create the popup window?Please give me a sample code.
Thank you so much
The first one is easy:
In TilesManager.java line:146
About the second one, you can display whatever you want on top of the map, as I said earlier
https://ghoshehsoft.wordpress.com/2012/04/06/mapapp5-mapview-and-activity/#comment-351
What kind of info do you want to display and what do you mean with the popup window?
Hi fuchs,
Thank for remember.I want to display the location information from sqlitedb onto mapview .
1 . I marks the point on the mapview,and then marks the regions(polygon).
2. That points and region stored in sqlitedb.
3. I display the location information when i click the marked point.So ,when i click the marked point, the popup(baloons) box is appear on mapview with relevant infromation.
thank you for your answer.If you have any idea for above mentions.please share to me.
http://deckjockey.blogspot.com/2010/01/android-baloon-display-on-map.html?showComment=1340264193387#c1311029246885376769
Hi Fuchs,I reference above link for Popup box,But i am facing the error,My error is
MapView.LayoutParams(200,200,item.getPoint(),MapView.LayoutParams.BOTTOM_CENTER));
my MapView can not use LayoutParams.
How do I work?
Please help me
It will be easier for me if you send me some code.
– Define a layout xml for this layout and add views that will be inside this layout like:
– In your map activity (where you will have your mapview) create an instance of layout we defined:
– Now whenever you need to display the baloon, just call mapview’s addView method as:
Hi fuchs,
how to get the latitute and longitude when i click onto the mapview wherever.?
Please help me.
Hi, you can check the comments here as they describe how to get the coordinates of a click:
https://ghoshehsoft.wordpress.com/2012/03/09/building-a-map-app-for-android/#comment-332
Once you have the coordinates you can store them for later use, like comparing them to the click position.
Hi,fuchs
Thank for your reply.Now i get the lat & long when i click on the mapView because of your
GuideLine.
I need to know the next one.
How to add the hightlight area?
When I click the highlight area in mapview,the hightlight area is returnd with information box.
Example.
In the World map,when I click the Africa area any where in mapView.The message return
“This is Africa”.
But,I don’t want to use internet connection.
If you have any idea,Please Help me.
Thankyou.
I’m not sure how this will work without internet connection.
You can pre-download location names (through reverse geocoding) and store them in a database along with the longitude latitude and provide this database with your app, when you want the name of a specific location you pick the nearest match from the database.
One downside of this method is the accuracy issue, downloading more locations names will increase the accuracy and database size.
Hi Fuchs,
Can I convert the Would.sqlitedb to spatial database?
How to convert this is?
Please help me?
Thank you for your answer.
Hi, I’m not sure how is that possible, maybe it’s better to get the data as a spatial database.
I’ve done some googling and found this:
http://blog.sqlauthority.com/2010/03/30/sql-server-world-shapefile-download-and-upload-to-database-spatial-database/
In the article you’ll find links to download spatial data for the whole world.
I hope this helps.
Hi Fuchs,
Thank you for your answer.
how do i use this spatial data for whole world to display on the android emulator.
I want to use this spatial data instead of World.sqlitedb.
How should I do?
Please help me.
I’m sorry but I know nothing about this particular subject, You’ll have to examine how the spatial data is organized and if needed convert them to an Sqlite database to use on the mobile.
Hi Fuchs,
I want to add webserver on your project.
How do I add the webserver?
Can I store the sqlite database in android webserver?
Okay I started writing a short tutorial about that, it should be ready soon I guess 😀
Thank Fuchs,
I am waiting for you.you can sent to my email when your program is finished.
This is very improtant for me.
hlaingwintunn@gmail.com
Okay I’m cooking the tutorial now 🙂
Okay the tutorial is ready with full implementation :).
MapApp6: Web support
Hi fuchs,
Thank you for your tutorial.
You are great man!
you’re welcome my friend :).
can you tell me how to set map view to layout xml file and coding it in activity file. thanks
I haven’t tried that yet, you can check the second answer here on stacktoverflow, it contains explanation and example.
hi, i tried downloading the map using mobile atlas creator version 1.9.11, and selected RMaps Sqlite format, then i tried to use it in your application , but it opened a gray page! the map is not showing…knowing that i named the file world.sqlitedb and replaced it with your map…need to know what i am doing wrong and why isn’t it working…thx.
Hi, I downloaded the 1.9.11 version and tried it and it worked, just make sure to select the lower zoom levels in addition to the zoom levels you need.
Let’s say you want to download zoom level 5 then you should also include levels 0,1,2,3 and 4.
The reason for this is that you need something to guide you while zooming-in in the app till you reach the desired zoom level, otherwise you might get lost in the gray background.
I Hope this helps.
hi,
thxs this worked perfectly after i added zoom levels 0 to 4,. but what if i need to start zooming from a higher level ..like 7 or 8 how can i do it? and also, i need to use some detailed maps showing points like restaurants or gas stations, but unfortunately, there are some areas blocked from the map…how can i solve this? if it is not possible, can i use my own map, maybe convert it to rmaps sqlite format, also i don’t know how to do that!
thank you very much, this tutorial is very helpful, you saved me 🙂
Hi, the app saves the last zoom level the user used, so the next app launch will have the same zoom level when the app was closed.
If you want the app to always start at a specific zoom level you can simply change the line that restores the zoom level from the preferences, just go to MapAppActivity.restoreMapViewSettings method and you’ll see the line:
change it to the zoom level you want like this:
This way the app first run will start with gps coordinates equal to (0,0) and zoom level 7, you might also get lost here in the gray background since you might not have the tiles around (0,0) at zoom level 7, that’s why I don’t recommend messing with the starting zoom level since it’s better to let the user decide what zoom level he wants.
About the blocked areas this might happen when downloading OpenStreetMaps tiles quickly as this process will exhaust their servers, a solution is to download them slowly setting a speed limit on Mobile Atlas Creator, another (and better) solution is to download tiles only when needed which is explained in the tutorial part 6 Web Support.
thank you very much.
Hi fuchs,
Thank you for your tutorial.
I have tried to add a buttion (or image buttion) on the layout contain mapview. It is invisible. why?
Hi 🙂 did you add the button before or after the map view?
I’m writing a tutorial right now about this, in the mean time you can check this comment here (#95).
EDIT: The tutorial is finished, you can read it here : MapApp7 : Creating MapView in XML
hi, thank you for your help.. i need to plot many points on the map, not only one point, and draw a line between those points, how can i do this?
That can be done easily, it just depends on the points you want to draw, are they gps coordinates? if so then you need to convert each point to map pixel coordinates using
TilesManager.lonLatToPixelXY
and then subtract the offset (which is calculated in the mapView).
If you want to take a look at my implementation you can check the post I just published:
Fuchs Maps
You can take a look how things can be done and then implement it your own way.
If you need some explanation about the code tell me and I’ll give you more details.
Hi Fuchs,
I just noticed your app enable the GPS function. However when I turned on GPS in my nexus 7, there is nothing appeared? Is it supposed to have my location displayed on map?
Moreover, when I enabled track option, then saved it but the app announced that that record is empty.
Could u please tell me why or just guide me how to enable all requirements to have the options done successfully.
Best regards,
Oh my bad, my previous comment is for Fuchs Maps example.
Sorry for putting in wrong page
No need to apologize 🙂
I need some help with something I have no idea how to do.
Now that I can make this base of the map, is there anyway to add a layer above the current layout? Like an .KML file or anothers made by tiles too?
I’ll be glad for any help!
I guess that can be done.
First Approach : Double things
1- Add another TilesProvider to the MapView class. It could be just another TilesProvider with a different database as a source for tiles.
2- In MapView.fetchTiles() you have to fetch the tiles for the second layer (using same tileManager calculations), something like this:
otherTileProvider.fetchTiles(visibleRegion, tileManager.getZoom());
3- Create a second drawTiles in MapView that renders the tiles from the newly added TilesProvider.
4- Add call to the newly defined drawTiles just after the old one.
Second Approach : Use a Single Database with More Data
1- You can use a the same TilesProvider with a tiles database that contains (in addition to current columns) a column that specifies the layer which the tile belongs to. The query inside the TilesProvider doesn’t have to be changed since it only cares about x,y and z.
2- In MapView.drawTiles you do two iterations on the tiles you have, in the first iteration you draw the tile if it belongs to the base layer, in the second iteration you draw the tile if it belongs to the second layer.
I guess the second approach is better but it requires you to have control over the database contents, the first approach allows you to combine two different databases.
If you’ve been reading the tutorials you should be able to make sense of the above.
The above approaches still use RMaps sqlite database format, I never used KML but I guess the concepts are similar,
Let me know if you need any help.
That’s great mate..From last 2-3 days i was looking for something like this until i reached here and the way you explained things is great. I’m trying to develop this kind of app and i assume your tutorials are gonna help me a lot. Hats off 🙂 I just downloaded apk and placed database in sd card under folder name you have given. When i try to open app it gives message that Unfortunately, app stopped working. Any ideas? I’m using it on Sony Xperia Z. Thanks and once again please accept my gratitude 🙂
Hi Faisal and thanks for your interest. I’m sorry it took me a while to reply.
In order to know what exactly happened you need to check the LogCat in Eclipse\Android studio :).
Hi Fuchs! Thanks for the tutorial….! It really helped me a lot for making the
offline map under android 2.3 here. 😀
My question is if let say….
My layout is like this
Would it be possible if you guive me
how to put the map into the Red Box area?
if let’s say the red area is another small frameLayout / ImageView or something….
Hi :).
You simply need to:
1- Create the map view
2- Get a reference to the FrameLayout in red
3- Call myFrameLayout.addView(mapView);
If you need more details please let me know.
hi fuchs, i’m back. err,, i found a little bit confusing over here. Are you checking out your blogs lately ? I post some comments there that may need your help (tips).
Hi, is the problem about embedding the MapView in the red frame or is it about the map tiles source?
Hi there, I tried to download and install the app on my phone but it didnt work. A message shows the app cannt run because of error at opening app. I dont know what is going on and how to fix it. Could you help me please because I write an app and it uses offline map in emergency situation ?
Many thanks
Hi Phong, could you please check this comment?
Dear FUCHS, SALAM
my phone has ultra HD resolution and phone is showing maps with very small text size of addresses on MAPS … how can I increase that text size on maps ? 😦
Salam Farhan.
Unfortunately back then I used pixels to specify dimensions. To solve the issue you need to use dp units.
Hi Hisham,
First of thank you for the great efforts in putting together these series of posts.
I learnt a lot not only about the writing the app but also concepts.
It is really appreciated.
Now I am using newercurrent version of android SDK and seeing errors in opening “world.sqlitedb” file. Looks like there is an issue with sqllite itself.
I can post/send the exact error later. Have you built the mapapp with later version of android ?
Glad I could help 🙂
I really wish I have the time and patience to re-write this tutorial series since a lot of the code could have been written better.
What errors are you receiving?
Hi Hisham,
thanks for the reply. I understand you are busy. I did some debugging my self. I am seeing error with World.sqlitedb itself. The error is 14.
Could you please email me the World.sqlitedb file to me directly?
Thanks
You can get the file from here
http://dl.dropbox.com/u/12942790/Blog/MapApp/Final/World.sqlitedb
About the error you’re having, it would be helpful to paste the entire exception / stack trace