moagrius / TileView

TileView is a subclass of android.view.ViewGroup that asynchronously displays, pans and zooms tile-based images. Plugins are available for features like markers, hotspots, and path drawing.
MIT License
1.46k stars 337 forks source link

Add layer support for TileView #39

Closed apachemaven closed 10 years ago

apachemaven commented 10 years ago

Is it possible to add the layer support in the TileView? For example, one can add the satellite layer and the place name layer at the same time.

moagrius commented 10 years ago

i'm not 100% sure on what you mean, but you can add other layers / layouts / viewgroups to TileView. In general, you'll want these to be either FrameLayouts or RelativeLayouts (or a custom ViewGroup with similar functionality), because the layout mechanism will position all children at top-left (0,0).

technically, an intermediary viewgroup is used as a proxy parent, but this is all handled behind-the-scenes and addView methods are overriden.

is that about what you were asking about?

apachemaven commented 10 years ago

Hi, the layer I mentioned has nothing to do with the android widget. I mean something like this https://developers.google.com/maps/documentation/javascript/layers#LayersOverview. BTW, I am trying to add some online map tiles like google map, but I failed. This is the core code:

    TileView tileView = getTileView();

    // size of the original image at 100% scale
    tileView.setSize(256, 256); //google map use one tile of 256x256 to represent for  the whole world

    //the resolution is 0.0054931640625 at zoom level 8
    tileView.addDetailLevel(0.0054931640625f, "https://mts1.googleapis.com/vt?hl=en-us&x=%col%&y=%row%&z=8", "samples/boston-pedestrian.jpg");
    //the resolution is 0.00274658203125 at zoom level 9
    tileView.addDetailLevel(0.00274658203125f, "https://mts1.googleapis.com/vt?hl=en-us&x=%col%&y=%row%&z=9", "samples/boston-pedestrian.jpg");

    // some preferences...
    tileView.setScaleLimits(0, 1);
    tileView.setTransitionsEnabled(false);

    // the world bounds
    tileView.defineRelativeBounds(-180, 90, 180, -90);

    //chicago
    frameTo(41.850033, -87.6500523);

Anything wrong?

moagrius commented 10 years ago

As far as layer support, those would just be custom ViewGroups added to the TileView via addView, so technically "yes" it's supported, but those layers specifically aren't available out of the box. TileView is not intended to be a replacement for com.google.MapView.

For your example, it looks like you're trying to load tiles via HTTP. By default, TileView tries to find the tiles in assets. However, you can provide your own decoding implementation - and in fact one is included in the package for HTTP loading. You'd do something like this to have tiles loaded via HTTP but samples from assets:

tileView.setTileDecoder(new BitmapDecoderHttp());

FWIW, if you can change the HTTPS to HTTP you'll get better performance.

The second problem is how you're adding detail levels. The floats you're passing to the first argument aren't appropriate - this parameter does not set a scale per se, but rather indicates the scale at which that tile set should be shown. So with what you've supplied, you've told the to show the tiles from z=8 when the tile view's scale is 0.005 to 0.002, then from z=9 when it's below 0.002. For any scale greater than 0.005 it'd default to the closest match. The TileView will hardly ever be at any of the provided scales - at full size, the scale is 1.0, at half size it's 0.5, at quarter size is 0.25, etc.

Generally, you'll want to provide a detail level for 1.0 at a minimum. After that, it's common to go down by halves - 0.5, 0.25, 0.125, etc - the number of levels depends on your graphic resources. It's not required that you go down by halves, you could use 1.0, 0.75, and 0.5, for example.

If you're using google map tiles, is there a reason you're not use com.google.MapView? TileView is generally better suited for applications where MapView is not appropriate - like small areas (building interiors), fictional areas, highly custom tile or tiling behavior, non-map applications like large images, technical documents, etc.

HTH.

apachemaven commented 10 years ago

Does it mean that it not a good idea to use TileView for geographic information related application? BTW, can you tell me something about the implementation of TileView, for example, when user drag the map, what job will be done, and how the tiles will be loaded? Because I found that the operation(drag,move,tap) inside the TileView are rather smooth than others.

moagrius commented 10 years ago

Absolutely you can use it for geographic apps - it just looked like com.google.MapView might be a better fit. MapView does a great job in general, but of course there are limits - e.g., mapping a place that isn't real or registered, or a very small area (building interior) - TileView was created for those situations. Both have their role.

What happens during drag, move, tap? Well.. a lot. The parent class ZoomPanLayout has it's onTouchEvent method overridden - that's where most of touch interaction is handled, but you'll see that about half of the code in ZoomPanLayout is dedicated to managing gestures. Re: smoothness - IMO the widget is in general pretty heavily optimized, due to a lot of real-world use and user feedback. Hit-testing is done entirely by simple (fast) math, we load from assets with the faster RGB pattern, and bitmap data is enthusiastically managed (by default, only the tiles that intersect the screen exist at any one time). If you disable transitions, omit paths, and don't use down-samples, it can get close to CATileLayer on some devices (Nexus). That said, it could probably be faster if it used a OpenGL, but I suspect we'd lose some ease-of-use elsewhere (e.g., using real Views as markers), but I haven't investitaged that alternative enough to make educated comments.

Take a look at the demo app, and check out the "Real Map" implementation, so see how it might be used with geographic information.

HTH.

apachemaven commented 10 years ago

Well, thanks for your detailed explanation,and I wonder how do you locate the tiles? That's to say, how do you calculate the required tiles and there coordinate(the col and row value) ?

moagrius commented 10 years ago

generally people use either an online tiler, or photoshop (good for small images), or (most commonly) imagemagick. The tiles are generally named with the row and column, although directories can be helpful as well, and you can provide your own implementation of TileSetPatternParser.

For example, tiles might be saved in a folder call "tiles", then named like "tile-0_0.png" for the first (top left) tile, then each other in the same row might have the second number incremented (e.g., "tile-0_1.png, tile-0_2.png", etc), then as the next how is hit, the first number changes and the second number starts over (e.g., "tile-1_0.png", "tile-1_1.png", etc). This is all fairly arbitrary, and it can all be customized, but what I've just described is a pretty common approach. This is not unique to TileView, and is seen in other tile based widget, apps, sites, etc.

Here's a wiki on creating tiles, and here's one that includes tile generation and implementing it in an app.

apachemaven commented 10 years ago

Thanks for your reply, but I am afraid you misunderstand me. :(.

Take the code for example:

frameTo( 42.360025, -71.065663 );

How do you know which tiles should be loaded for the given point?

moagrius commented 10 years ago

We figure out the dimension of the viewport (getScrollX/Y and getWidth/Height), then compare that to the scaled value of the TileView, and the current tile set, and use some basic intersection math - this can be seen in the getIntersections method of the DetailLevel. These tiles are then scheduled with a (customized) AsyncTask, that uses a ThreadPoolExecutor for parallel execution, to be rendered.

Is that closer to what you were asking about?

apachemaven commented 10 years ago

That's to say, the center 42.360025, -71.065663 is not considered during the calculation? Generally, the center will be at the center of the view port.

moagrius commented 10 years ago

To center, we just subtract half the width and height from the final calculated position, as is shown in ZoomPanLayout.scrollToAndCenter

moagrius commented 10 years ago

BTW, frameTo is just a helper/sugar method in the demo app - it's not really "in the lib"...

apachemaven commented 10 years ago

Thanks, I got it. So kind of you. :+1:

moagrius commented 10 years ago

NP, HTH