summaryrefslogtreecommitdiff
path: root/platform/darwin/src/MGLTileSource.mm
blob: eef3b33430ef87ebdf30903cbc5d9c7791b890e0 (plain)
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
#import "MGLTileSource_Private.h"

#import "MGLAttributionInfo_Private.h"
#import "MGLGeometry_Private.h"
#import "MGLRasterDEMSource.h"
#import "NSString+MGLAdditions.h"
#import "NSValue+MGLAdditions.h"

#if TARGET_OS_IPHONE
    #import <UIKit/UIKit.h>
#else
    #import <Cocoa/Cocoa.h>
#endif

#include <mbgl/util/tileset.hpp>

const MGLTileSourceOption MGLTileSourceOptionMinimumZoomLevel = @"MGLTileSourceOptionMinimumZoomLevel";
const MGLTileSourceOption MGLTileSourceOptionMaximumZoomLevel = @"MGLTileSourceOptionMaximumZoomLevel";
const MGLTileSourceOption MGLTileSourceOptionCoordinateBounds = @"MGLTileSourceOptionCoordinateBounds";
const MGLTileSourceOption MGLTileSourceOptionAttributionHTMLString = @"MGLTileSourceOptionAttributionHTMLString";
const MGLTileSourceOption MGLTileSourceOptionAttributionInfos = @"MGLTileSourceOptionAttributionInfos";
const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem = @"MGLTileSourceOptionTileCoordinateSystem";
const MGLTileSourceOption MGLTileSourceOptionDEMEncoding = @"MGLTileSourceOptionDEMEncoding";

@implementation MGLTileSource

- (NSURL *)configurationURL {
    [NSException raise:MGLAbstractClassException
                format:@"MGLTileSource is an abstract class"];
    return nil;
}

- (NSArray<MGLAttributionInfo *> *)attributionInfos {
    return [self attributionInfosWithFontSize:0 linkColor:nil];
}

- (NSArray<MGLAttributionInfo *> *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor {
    return [MGLAttributionInfo attributionInfosFromHTMLString:self.attributionHTMLString
                                                     fontSize:fontSize
                                                    linkColor:linkColor];
}

- (NSString *)attributionHTMLString {
    [NSException raise:MGLAbstractClassException
                format:@"MGLTileSource is an abstract class"];
    return nil;
}

@end

mbgl::Tileset MGLTileSetFromTileURLTemplates(NSArray<NSString *> *tileURLTemplates, NSDictionary<MGLTileSourceOption, id> * _Nullable options) {
    mbgl::Tileset tileSet;

    for (NSString *tileURLTemplate in tileURLTemplates) {
        tileSet.tiles.push_back(tileURLTemplate.UTF8String);
    }

    // set the minimum / maximum zoom range to the values specified by this class if they
    // were set. otherwise, use the core objects default values
    if (NSNumber *minimumZoomLevel = options[MGLTileSourceOptionMinimumZoomLevel]) {
        if (![minimumZoomLevel isKindOfClass:[NSNumber class]]) {
            [NSException raise:NSInvalidArgumentException
                        format:@"MGLTileSourceOptionMinimumZoomLevel must be set to an NSNumber."];
        }
        tileSet.zoomRange.min = minimumZoomLevel.integerValue;
    }
    if (NSNumber *maximumZoomLevel = options[MGLTileSourceOptionMaximumZoomLevel]) {
        if (![maximumZoomLevel isKindOfClass:[NSNumber class]]) {
            [NSException raise:NSInvalidArgumentException
                        format:@"MGLTileSourceOptionMinimumZoomLevel must be set to an NSNumber."];
        }
        tileSet.zoomRange.max = maximumZoomLevel.integerValue;
    }
    if (tileSet.zoomRange.min > tileSet.zoomRange.max) {
        [NSException raise:NSInvalidArgumentException
                    format:@"MGLTileSourceOptionMinimumZoomLevel must be less than MGLTileSourceOptionMaximumZoomLevel."];
    }
    
    if (NSValue *coordinateBounds = options[MGLTileSourceOptionCoordinateBounds]) {
        if (![coordinateBounds isKindOfClass:[NSValue class]]
            && strcmp(coordinateBounds.objCType, @encode(MGLCoordinateBounds)) == 0) {
            [NSException raise:NSInvalidArgumentException
                        format:@"MGLTileSourceOptionCoordinateBounds must be set to an NSValue containing an MGLCoordinateBounds."];
        }
        tileSet.bounds = MGLLatLngBoundsFromCoordinateBounds(coordinateBounds.MGLCoordinateBoundsValue);
    }

    if (NSString *attribution = options[MGLTileSourceOptionAttributionHTMLString]) {
        if (![attribution isKindOfClass:[NSString class]]) {
            [NSException raise:NSInvalidArgumentException
                        format:@"MGLTileSourceOptionAttributionHTMLString must be set to a string."];
        }
        tileSet.attribution = attribution.UTF8String;
    }

    if (NSArray *attributionInfos = options[MGLTileSourceOptionAttributionInfos]) {
        if (![attributionInfos isKindOfClass:[NSArray class]]) {
            [NSException raise:NSInvalidArgumentException
                        format:@"MGLTileSourceOptionAttributionInfos must be set to a string."];
        }

        NSAttributedString *attributedString = [MGLAttributionInfo attributedStringForAttributionInfos:attributionInfos];
#if TARGET_OS_IPHONE
        static NSString * const NSExcludedElementsDocumentAttribute = @"ExcludedElements";
#endif
        NSDictionary *documentAttributes = @{
            NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
            NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding),
            // The attribution string is meant to be a simple, inline fragment, not a full-fledged, validating document.
            NSExcludedElementsDocumentAttribute: @[@"XML", @"DOCTYPE", @"html", @"head", @"meta", @"title", @"style", @"body", @"p"],
        };
        NSData *data = [attributedString dataFromRange:attributedString.mgl_wholeRange documentAttributes:documentAttributes error:NULL];
        NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        tileSet.attribution = html.UTF8String;
    }

    if (NSNumber *tileCoordinateSystemNumber = options[MGLTileSourceOptionTileCoordinateSystem]) {
        if (![tileCoordinateSystemNumber isKindOfClass:[NSValue class]]) {
            [NSException raise:NSInvalidArgumentException
                        format:@"MGLTileSourceOptionTileCoordinateSystem must be set to an NSValue or NSNumber."];
        }
        MGLTileCoordinateSystem tileCoordinateSystem;
        [tileCoordinateSystemNumber getValue:&tileCoordinateSystem];
        switch (tileCoordinateSystem) {
            case MGLTileCoordinateSystemXYZ:
                tileSet.scheme = mbgl::Tileset::Scheme::XYZ;
                break;
            case MGLTileCoordinateSystemTMS:
                tileSet.scheme = mbgl::Tileset::Scheme::TMS;
                break;
        }
    }

    if (NSNumber *demEncodingNumber = options[MGLTileSourceOptionDEMEncoding]) {
        if (![demEncodingNumber isKindOfClass:[NSValue class]]) {
            [NSException raise:NSInvalidArgumentException
                        format:@"MGLTileSourceOptionDEMEncoding must be set to an NSValue or NSNumber."];
        }
        MGLDEMEncoding demEncoding;
        [demEncodingNumber getValue:&demEncoding];
        switch (demEncoding) {
            case MGLDEMEncodingMapbox:
                tileSet.encoding = mbgl::Tileset::DEMEncoding::Mapbox;
                break;
            case MGLDEMEncodingTerrarium:
                tileSet.encoding = mbgl::Tileset::DEMEncoding::Terrarium;
                break;
        }
    }

    return tileSet;
}