-
Notifications
You must be signed in to change notification settings - Fork 1
/
TLMapTileLayer.m
159 lines (132 loc) · 5.06 KB
/
TLMapTileLayer.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//
// TLMapTileLayer.m
// TileMap
//
// Created by Nathan Vander Wilt on 10/1/09.
// Copyright 2009 __MyCompanyName__. All rights reserved.
//
#import "TLMapTileLayer.h"
#import <QuartzCore/QuartzCore.h>
#import "NSAffineTransform+TLAdditions.h"
#import "NSIndexPath+TLMapTileAdditions.h"
#import "TLMapProjection.h"
#import "TLMapLayer.h"
#import "TLMapTilesetOSM.h"
NS_INLINE NSRect TLMapTileRect(NSIndexPath* tilePath);
@interface TLMapTileLayer () <TLMapTilesetDelegate>
@end
@implementation TLMapTileLayer
- (id)init {
self = [super init];
if (self) {
tileset = [TLMapTilesetOSM new];
[tileset setDelegate:self];
}
return self;
}
- (void)dealloc {
[tileset release];
[super dealloc];
}
- (TLMapTileset*)currentTileset {
return tileset;
}
- (NSAffineTransform*)transformFromMapToTilespace:(NSUInteger)detailLevel {
NSAffineTransform* transform = [NSAffineTransform transform];
NSUInteger numTilesAlongEdge = 1 << detailLevel;
NSRect tileBounds = self.currentTileset.bounds;
// tile space is flipped
[transform translateXBy:0.0f yBy:(CGFloat)numTilesAlongEdge];
[transform scaleXBy:1.0f yBy:-1.0f];
// fit map space to tile space
[transform scaleBy:(CGFloat)numTilesAlongEdge];
[transform scaleXBy:(1.0f / tileBounds.size.width)
yBy:(1.0f / tileBounds.size.height)];
[transform translateXBy:(-tileBounds.origin.x)
yBy:(-tileBounds.origin.y)];
return transform;
}
- (NSUInteger)currentDetailLevel {
const double levelAdjustment = 0.0;
// find how much of the map each unit (pixel) needs
NSSize unitSize = NSMakeSize(1.0f, 1.0f);
NSSize unitSizeInView = [self convertSizeFromBase:unitSize];
NSAffineTransform* viewToMap = [[self tl_mapHost] drawTransform];
[viewToMap invert];
NSSize unitSizeOnMap = [viewToMap transformSize:unitSizeInView];
// find how much of the map a pixel in tileset's level 0 contains
NSRect tileBounds = self.currentTileset.bounds;
NSUInteger tilePixelWidth = self.currentTileset.tileHeight;
NSUInteger tilePixelHeight = self.currentTileset.tileWidth;
NSSize pixelSize = NSMakeSize((tileBounds.size.width / (CGFloat)tilePixelWidth),
(tileBounds.size.height / (CGFloat)tilePixelHeight));
// determine detail level
double factorNeeded = MAX((pixelSize.width / unitSizeOnMap.width),
(pixelSize.height / unitSizeOnMap.height));
double exactDetailLevel = log2(factorNeeded);
NSInteger unclampedLevel = lround(exactDetailLevel + levelAdjustment);
NSUInteger maxLevel = self.currentTileset.levelsOfDetail - 1;
return MIN(maxLevel, MAX(0, unclampedLevel));
}
- (NSAffineTransform*)transformFromTilesToView {
NSUInteger detailLevel = [self currentDetailLevel];
NSAffineTransform* tilesToMap = [[self transformFromMapToTilespace:detailLevel]
tl_inverseTransform];
NSAffineTransform* mapToView = [[self tl_mapHost] drawTransform];
NSAffineTransform* tilesToView = [NSAffineTransform transform];
[tilesToView appendTransform:tilesToMap];
[tilesToView appendTransform:mapToView];
return tilesToView;
}
- (NSSet*)tilesInViewRect:(NSRect)rect {
NSAffineTransform* tilesToDrawing = [self transformFromTilesToView];
NSRect tilesRect = [[tilesToDrawing tl_inverseTransform] tl_transformRect:rect];
NSMutableSet* tilesInRect = [NSMutableSet set];
NSUInteger detailLevel = [self currentDetailLevel];
NSUInteger tileLimit = 1 << detailLevel;
NSInteger minTileX = (NSInteger)MAX(0, floor(tilesRect.origin.x));
NSInteger maxTileX = (NSInteger)MIN(tileLimit,
ceil(tilesRect.origin.x + tilesRect.size.width));
NSInteger minTileY = (NSInteger)MAX(0, floor(tilesRect.origin.y));
NSInteger maxTileY = (NSInteger)MIN(tileLimit,
ceil(tilesRect.origin.y + tilesRect.size.height));
for (NSInteger tileX = minTileX; tileX < maxTileX; ++tileX) {
for (NSInteger tileY = minTileY; tileY < maxTileY; ++tileY) {
NSIndexPath* tilePath = [NSIndexPath tl_indexPathForDetailLevel:detailLevel
column:tileX
row:tileY];
[tilesInRect addObject:tilePath];
}
}
return tilesInRect;
}
- (void)drawRect:(NSRect)rect {
NSSet* visibleTiles = [self tilesInViewRect:[self visibleRect]];
[(self.currentTileset) setVisibleTiles:visibleTiles];
//NSLog(@"%@", visibleTiles);
NSSet* redrawnTiles = [self tilesInViewRect:rect];
NSAffineTransform* tilesToDrawing = [self transformFromTilesToView];
for (NSIndexPath* tilePath in redrawnTiles) {
NSRect tileRect = TLMapTileRect(tilePath);
NSRect targetRect = [tilesToDrawing tl_transformRect:tileRect];
NSImage* tileImage = [(self.currentTileset) imageForTileIfAvailable:tilePath];
[tileImage drawInRect:targetRect
fromRect:NSZeroRect
operation:NSCompositeCopy
fraction:1.0f];
}
}
- (void)mapTileset:(TLMapTileset*)aTileset
didFetchTile:(NSIndexPath*)tilePath
{
(void)aTileset;
if (tilePath.tl_detailLevel == [self currentDetailLevel]) {
NSRect redrawRect = [[self transformFromTilesToView]
tl_transformRect:TLMapTileRect(tilePath)];
[self setNeedsDisplayInRect:redrawRect];
}
}
@end
NS_INLINE NSRect TLMapTileRect(NSIndexPath* tilePath) {
return NSMakeRect(tilePath.tl_column, tilePath.tl_row, 1.0f, 1.0f);
}