Skip to content

Commit c33e621

Browse files
committed
Simplify tile loading
Signed-off-by: Kyle Corry <[email protected]>
1 parent 05d5754 commit c33e621

File tree

13 files changed

+161
-197
lines changed

13 files changed

+161
-197
lines changed

app/src/main/java/com/kylecorry/trail_sense/shared/dem/map_layers/ElevationLayer.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ class ElevationLayer(taskRunner: MapLayerBackgroundTask2 = MapLayerBackgroundTas
1313

1414
init {
1515
preRenderBitmaps = true
16-
loader.useFirstImageSize = true
1716
}
1817

1918
fun setPreferences(prefs: ElevationMapLayerPreferences) {

app/src/main/java/com/kylecorry/trail_sense/shared/dem/map_layers/HillshadeLayer.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ class HillshadeLayer(taskRunner: MapLayerBackgroundTask2 = MapLayerBackgroundTas
1212

1313
init {
1414
preRenderBitmaps = true
15-
loader.useFirstImageSize = true
1615
alpha = 127
1716
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
1817
tilePaint.setBlendMode(BlendModeCompat.MULTIPLY)

app/src/main/java/com/kylecorry/trail_sense/shared/map_layers/tiles/IGeographicImageRegionLoader.kt

Lines changed: 0 additions & 10 deletions
This file was deleted.

app/src/main/java/com/kylecorry/trail_sense/shared/map_layers/tiles/TileLoader.kt

Lines changed: 16 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,8 @@
11
package com.kylecorry.trail_sense.shared.map_layers.tiles
22

33
import android.graphics.Bitmap
4-
import android.graphics.Canvas
5-
import android.graphics.Color
64
import android.util.Log
7-
import androidx.core.graphics.alpha
8-
import androidx.core.graphics.createBitmap
9-
import com.kylecorry.andromeda.bitmaps.operations.Conditional
10-
import com.kylecorry.andromeda.bitmaps.operations.Convert
11-
import com.kylecorry.andromeda.bitmaps.operations.ReplaceColor
12-
import com.kylecorry.andromeda.bitmaps.operations.applyOperationsOrNull
135
import com.kylecorry.luna.coroutines.onDefault
14-
import com.kylecorry.sol.science.geology.CoordinateBounds
15-
import com.kylecorry.trail_sense.shared.andromeda_temp.Parallel
16-
import com.kylecorry.trail_sense.tools.photo_maps.infrastructure.tiles.PhotoMapRegionLoader
17-
import kotlin.math.hypot
186

197
class TileLoader {
208

@@ -23,8 +11,6 @@ class TileLoader {
2311

2412
var lock = Any()
2513

26-
var useFirstImageSize: Boolean = false
27-
2814
var alwaysReloadTiles: Boolean = false
2915
var clearTileWhenNullResponse: Boolean = true
3016

@@ -39,138 +25,39 @@ class TileLoader {
3925

4026
suspend fun loadTiles(
4127
sourceSelector: TileSource,
42-
bounds: CoordinateBounds,
43-
metersPerPixel: Float,
44-
minZoom: Int = 0,
45-
backgroundColor: Int = Color.WHITE,
46-
// TODO: This is gross, rather than this it should handle the lifecycle of region loaders and make them distinct
47-
controlsPdfCache: Boolean = false
28+
tiles: List<Tile>
4829
) = onDefault {
49-
// Step 1: Split the visible area into tiles (geographic)
50-
val tiles = TileMath.getTiles(bounds, metersPerPixel.toDouble())
51-
if (tiles.size > 100) {
52-
Log.d("TileLoader", "Too many tiles to load: ${tiles.size}")
53-
return@onDefault
54-
}
55-
56-
if ((tiles.firstOrNull()?.z ?: 0) < minZoom) {
57-
return@onDefault
58-
}
59-
60-
// Step 2: For each tile, determine which map(s) will supply it.
61-
val tileSources = mutableMapOf<Tile, List<IGeographicImageRegionLoader>>()
62-
val loaders = sourceSelector.getRegionLoaders(tiles.map { it.getBounds() })
63-
for (i in tiles.indices) {
64-
val tile = tiles[i]
65-
val sources = loaders[i]
66-
if (sources.isNotEmpty()) {
67-
tileSources[tile] = sources
68-
}
69-
}
70-
71-
// TODO: Handle this cleanup elsewhere
72-
val allMaps = tileSources.values
73-
.flatten()
74-
.filterIsInstance<PhotoMapRegionLoader>()
75-
.map { it.map }
76-
.distinct()
77-
78-
if (controlsPdfCache) {
79-
PhotoMapRegionLoader.Companion.removeUnneededLoaders(allMaps)
30+
val tilesToLoad = if (alwaysReloadTiles) {
31+
tiles
32+
} else {
33+
tiles.filter { !tileCache.containsKey(it) }
8034
}
8135

8236
var hasChanges = false
8337

84-
val middleX = tileSources.keys.map { it.x }.average()
85-
val middleY = tileSources.keys.map { it.y }.average()
86-
87-
val sortedEntries = tileSources.entries
88-
.sortedBy { hypot(it.key.x - middleX, it.key.y - middleY) }
89-
90-
Parallel.forEach(sortedEntries.toList()) { source ->
91-
if (tileCache.containsKey(source.key) && !alwaysReloadTiles) {
92-
return@forEach
93-
}
94-
95-
val config = if (backgroundColor.alpha != 255) {
96-
Bitmap.Config.ARGB_8888
97-
} else {
98-
Bitmap.Config.RGB_565
99-
}
100-
101-
var canvas: Canvas? = null
102-
var image: Bitmap? = null
103-
104-
if (!useFirstImageSize) {
105-
image = createBitmap(source.key.size.width, source.key.size.height, config)
106-
image.eraseColor(backgroundColor)
107-
canvas = Canvas(image)
108-
}
109-
110-
source.value.reversed().forEachIndexed { index, loader ->
111-
val currentImage = loader.load(source.key)?.applyOperationsOrNull(
112-
Conditional(
113-
index > 0,
114-
ReplaceColor(
115-
Color.WHITE,
116-
Color.argb(127, 127, 127, 127),
117-
80f,
118-
true,
119-
inPlace = true
120-
)
121-
)
122-
)
123-
124-
if (currentImage != null) {
125-
if (useFirstImageSize) {
126-
image = createBitmap(currentImage.width, currentImage.height, config)
127-
canvas = Canvas(image)
128-
}
129-
130-
canvas?.drawBitmap(currentImage, 0f, 0f, null)
131-
currentImage.recycle()
132-
}
133-
}
134-
135-
// Remove transparency
136-
image = image?.applyOperationsOrNull(
137-
// Undo color replacement
138-
Conditional(
139-
backgroundColor.alpha != 255 && source.value.size > 1,
140-
ReplaceColor(
141-
Color.argb(127, 127, 127, 127),
142-
Color.WHITE,
143-
80f,
144-
true,
145-
inPlace = true
146-
)
147-
),
148-
Convert(config)
149-
)
150-
151-
hasChanges = true
38+
sourceSelector.load(tilesToLoad) { tile, image ->
15239
synchronized(lock) {
15340
if (clearTileWhenNullResponse || image != null) {
154-
val old = tileCache[source.key]
41+
val old = tileCache[tile]
15542
if (image == null) {
156-
tileCache -= source.key
43+
tileCache -= tile
15744
} else {
158-
tileCache += source.key to listOfNotNull(image)
45+
tileCache += tile to listOfNotNull(image)
15946
}
16047
old?.forEach { it.recycle() }
48+
hasChanges = true
16149
}
16250
}
16351
}
16452

16553
synchronized(lock) {
166-
tileCache.keys.forEach { key ->
167-
if (!tileSources.containsKey(key)) {
168-
tileCache[key]?.forEach { bitmap -> bitmap.recycle() }
169-
hasChanges = true
170-
}
54+
val tilesSet = tiles.toSet()
55+
val keysToRemove = tileCache.keys.filter { it !in tilesSet }
56+
keysToRemove.forEach { key ->
57+
tileCache[key]?.forEach { bitmap -> bitmap.recycle() }
58+
tileCache -= key
59+
hasChanges = true
17160
}
172-
173-
tileCache = tileCache.filterKeys { it in tileSources.keys }
17461
}
17562

17663
if (hasChanges) {
Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package com.kylecorry.trail_sense.shared.map_layers.tiles
22

3-
import com.kylecorry.sol.science.geology.CoordinateBounds
3+
import android.graphics.Bitmap
44

55
interface TileSource {
6-
suspend fun getRegionLoaders(bounds: CoordinateBounds): List<IGeographicImageRegionLoader>
7-
suspend fun getRegionLoaders(bounds: List<CoordinateBounds>): List<List<IGeographicImageRegionLoader>> {
8-
return bounds.map { getRegionLoaders(it) }
9-
}
6+
suspend fun load(tiles: List<Tile>, onLoaded: (Tile, Bitmap?) -> Unit)
107
}

app/src/main/java/com/kylecorry/trail_sense/shared/map_layers/ui/layers/BaseMapTileSource.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ import com.kylecorry.andromeda.bitmaps.operations.Conditional
77
import com.kylecorry.andromeda.bitmaps.operations.ReplaceColor
88
import com.kylecorry.andromeda.core.cache.AppServiceRegistry
99
import com.kylecorry.sol.math.geometry.Size
10-
import com.kylecorry.sol.science.geology.CoordinateBounds
1110
import com.kylecorry.sol.units.Coordinate
12-
import com.kylecorry.trail_sense.shared.map_layers.tiles.IGeographicImageRegionLoader
11+
import com.kylecorry.trail_sense.shared.map_layers.tiles.Tile
1312
import com.kylecorry.trail_sense.shared.map_layers.tiles.TileSource
1413
import com.kylecorry.trail_sense.tools.photo_maps.domain.MapCalibration
1514
import com.kylecorry.trail_sense.tools.photo_maps.domain.MapCalibrationPoint
@@ -93,8 +92,8 @@ class BaseMapTileSource : TileSource {
9392
)
9493
)
9594

96-
override suspend fun getRegionLoaders(bounds: CoordinateBounds): List<IGeographicImageRegionLoader> {
97-
return internalSelector.getRegionLoaders(bounds)
95+
override suspend fun load(tiles: List<Tile>, onLoaded: (Tile, android.graphics.Bitmap?) -> Unit) {
96+
internalSelector.load(tiles, onLoaded)
9897
}
9998

10099
companion object {

app/src/main/java/com/kylecorry/trail_sense/shared/map_layers/ui/layers/tiles/FullRegionMapTileLoader.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@ import com.kylecorry.andromeda.core.units.PercentBounds
77
import com.kylecorry.andromeda.core.units.PercentCoordinate
88
import com.kylecorry.sol.science.geology.CoordinateBounds
99
import com.kylecorry.trail_sense.shared.andromeda_temp.CorrectPerspective2
10-
import com.kylecorry.trail_sense.shared.map_layers.tiles.IGeographicImageRegionLoader
1110
import com.kylecorry.trail_sense.shared.map_layers.tiles.Tile
1211
import kotlinx.coroutines.sync.Mutex
1312
import kotlinx.coroutines.sync.withLock
1413

1514
abstract class FullRegionMapTileLoader(
1615
private val fullBounds: CoordinateBounds,
1716
private val outputSize: Size? = null
18-
) : IGeographicImageRegionLoader {
17+
) {
1918
private var fullImage: Bitmap? = null
2019
private var isStopped = false
2120
private val lock = Mutex()
@@ -26,7 +25,7 @@ abstract class FullRegionMapTileLoader(
2625
fullImage = null
2726
}
2827

29-
override suspend fun load(tile: Tile): Bitmap? {
28+
suspend fun load(tile: Tile): Bitmap? {
3029
val fullImage = lock.withLock {
3130
if (fullImage == null && !isStopped) {
3231
fullImage = loadFullImage(fullBounds, tile.z)
Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.kylecorry.trail_sense.shared.map_layers.ui.layers.tiles
22

3+
import android.graphics.Bitmap
34
import com.kylecorry.sol.science.geology.CoordinateBounds
5+
import com.kylecorry.trail_sense.shared.andromeda_temp.Parallel
46
import com.kylecorry.trail_sense.shared.andromeda_temp.from2
5-
import com.kylecorry.trail_sense.shared.map_layers.tiles.IGeographicImageRegionLoader
7+
import com.kylecorry.trail_sense.shared.map_layers.tiles.Tile
68
import com.kylecorry.trail_sense.shared.map_layers.tiles.TileSource
79
import kotlinx.coroutines.sync.Mutex
810
import kotlinx.coroutines.sync.withLock
@@ -14,25 +16,20 @@ abstract class FullRegionMapTileSource : TileSource {
1416

1517
abstract fun getLoader(fullBounds: CoordinateBounds): FullRegionMapTileLoader
1618

17-
override suspend fun getRegionLoaders(bounds: List<CoordinateBounds>): List<List<IGeographicImageRegionLoader>> {
18-
val fullBounds = CoordinateBounds.from2(bounds.flatMap {
19+
override suspend fun load(tiles: List<Tile>, onLoaded: (Tile, Bitmap?) -> Unit) {
20+
val fullBounds = CoordinateBounds.from2(tiles.flatMap {
21+
val bounds = it.getBounds()
1922
listOf(
20-
it.northWest,
21-
it.southEast
23+
bounds.northWest,
24+
bounds.southEast
2225
)
2326
})
24-
return loaderLock.withLock {
27+
loaderLock.withLock {
2528
lastLoader?.close()
2629
lastLoader = getLoader(fullBounds)
27-
bounds.map { listOfNotNull(lastLoader) }
28-
}
29-
}
30-
31-
override suspend fun getRegionLoaders(bounds: CoordinateBounds): List<IGeographicImageRegionLoader> {
32-
return loaderLock.withLock {
33-
lastLoader?.close()
34-
lastLoader = getLoader(bounds)
35-
listOfNotNull(lastLoader)
30+
Parallel.forEach(tiles) {
31+
onLoaded(it, lastLoader?.load(it))
32+
}
3633
}
3734
}
3835
}

0 commit comments

Comments
 (0)