nightkidfifa / MapView

A Fast, Memory Efficient android library to display tiled maps, with support of markers and paths.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Download

MapView

MapView is a Fast, Memory Efficient android library to display tiled maps with minimal effort.

val mapView = MapView(context)
val tileStreamProvider = object : TileStreamProvider {
   override fun getTileStream(row: Int, col: Int, zoomLvl: Int): InputStream? {
     return FileInputStream(File("path_of_tile")) // or it can be a remote http fetch
   }
}

val config = MapViewConfiguration(levelCount = 7, fullWidth = 25000, fullHeight = 12500,
                                  tileSize = 256, tileStreamProvider = tileStreamProvider)
                                  .setMaxScale(2f)

/* Configuration */
mapView.configure(config)

MapView shows only the visible part of a tiled map, and supports flinging, dragging, scaling, and rotating. It's also possible to add markers and paths.

This project holds the source code of this library, plus a demo app (which is useful to get started).

MapView 2.x.x is out!

This new major version brings performance improvements and a brand new feature: map rotation. To be consistent with previous version, this is disabled by default. To enable it, use MapViewConfiguration.enableRotation(). You will find a code example inside the demo RotatingMapFragment.

When enabling rotation, the MapView handles rotation gestures by default. If you only want to rotate the map through APIs, then you should use enableRotation(handleRotationGesture = false). The MapView has a new API setAngle:

/**
 * Programmatically set the rotation angle of the MapView, in decimal degrees.
 * It should be called after the [MapView] configuration and after the [MapView] has been laid out.
 * Attempts to set the angle before [MapView] has been laid out will be ignored.
 */
fun MapView.setAngle(angle: AngleDegree)

Migrating from 1.x.x

There are some breaking changes, although most of them are just package refactoring. The interface ScaleChangeListener has been removed. If you relied on this, have a look at ReferentialOwnerinterface. There's an example of usage inside the RotatingMapFragment demo.

Installation

Add this to your module's build.gradle

implementation 'com.peterlaurence:mapview:2.0.4'

Origin and motivation

As a long time contributor to TileView, which is a reference in this area, I wanted to see the performance we would have using Kotlin coroutines. The result was beyond my expectations and this is why I'm sharing it the world. The overall design can be seen here. The focus has been on efficiency (no thread contention thanks to asynchronous programming, and the load on the main thread is low enough to maximize the fps).

A special thanks goes to Mike (@moagrius), as this library wouldn't exist without his first contributions.

Principles

Deep-zoom map

MapView is optimized to display maps that have several levels, like this:

Each next level is twice bigger than the former, and provides more details. Overall, this looks like a pyramid. Another common name is "deep-zoom" map. This library comes with a demo app, which shows basic usage of the MapView and bundles a map in the assets. Looking at structure of this map, you have a real example of a deep-zoom map.

MapView can also be used with single level maps.

Usage

To use the MapView, you have to follow these steps:

  1. Create a MapView instance
val mapView = MapView(context)
  1. Create a TileStreamProvider. See below for the details.
  2. Create a MapViewConfiguration. See below for the details.
  3. Apply the configuration
mapView.configure(config)

Convention

MapView uses the convention that the last level is at scale 1. So all levels have scales between 0 and 1. Even though you don't have to be aware of the details, it's important to know that. For example, if you set the max scale to 2, it means that the last level will be allowed to be upscaled to twice its original size (since the last level is at scale 1). This convention allows for a simple configuration.

Technical documentation

This section explains in details the configuration. But once configured, you can do a lot of things with your MapView instance. There is just one thing to remember: MapView extends GestureLayout and this last class has a ton of features (the source code is well documented). You can:

  • add listeners to events like pan, fling, zoom..
  • programmatically scroll and center to a position
  • respond to various touch events by subclassing MapView and overload related methods declared in GestureLayout

This list isn't complete. A dedicated section will be added.

MapViewConfiguration

The MapView must be configured using a MapViewConfiguration. It holds the mandatory parameters to build a MapView

Then, you can set optional properties by calling available methods on your MapViewConfiguration instance. Here is an example:

val config = MapViewConfiguration(levelCount = 7, fullWidth = 25000, fullHeight = 12500,
                                  tileSize = 256, tileStreamProvider = tileStreamProvider)
                                  .setMaxScale(2f)

See documentation here. Below is a description of mandatory parameters:

levelCount

The provided MapViewConfiguration.levelCount will define the zoomLevels index that the provided MapViewConfiguration.tileStreamProvider will be given for its TileStreamProvider.zoomLevels. The zoomLevels will be in the range [0 ; MapViewConfiguration.levelCount-1].

fullWidth and fullHeight

These are respectively the width and height in pixels of the map at scale 1 (that is, the width and height of the last level). In other words, if you put together all the tiles of the last level, you would obtain a big image. fullWidth and fullHeight are dimentions in pixels of this big image.

tileSize

The size of the tiles in pixels, which are assumed to be squares and always of the same size for all levels. For now, MapView don't support rectangular tiles or tiles of heterogeneous sizes.

tileStreamProvider

See the section below.

TileStreamProvider

The MapView will request tiles using the convention that each levels has its tiles organized like this:

But note that MapView isn't opinionated about that. This is one of the purpose of this TileStreamProvider:

interface TileStreamProvider {
    fun getTileStream(row: Int, col: Int, zoomLvl: Int): InputStream?
}

Your implementation of this interface does the necessary coordinate translation (if required). This is where you do your HTTP request if you have remote tiles, or fetch from a local database (or file system).

API documentation

API documentation has its own wiki page.

ReferentialOwner

When the scale and/or the rotation of the MapView change, some of the child views might have to change accordingly. For that purpose, you can register a ReferentialOwner to the MapView.

A ReferentialOwner is an interface:

interface ReferentialOwner {
    var referentialData: ReferentialData
}

And ReferentialData holds several useful properties:

data class ReferentialData(var rotationEnabled: Boolean = true,
                           var angle: AngleDegree = 0f,
                           var scale: Float = 0f,
                           var centerX: Double = 0.0,
                           var centerY: Double = 0.0) : Parcelable

A ReferentialOwner should be registered to the MapView:

mapView.addReferentialOwner(refOwner)
// If you need to unregister it:
mapView.removeReferentialOwner(refOwner)

From inside your ReferentialOwner implementation, you can have any logic you want. You can rotate some markers, rotate complex views taking into account the centerX and centerY properties, etc.

There's an example of usage at RotatingMapFragment.

Create a deep-zoom map

If you don't have already such a map and you need to make one from a big image, follow this tutorial.

About

A Fast, Memory Efficient android library to display tiled maps, with support of markers and paths.

License:GNU General Public License v3.0


Languages

Language:Kotlin 100.0%