ios: apply clang-format to Objective-C++ sources (#4060)
This commit is contained in:
@@ -20,4 +20,5 @@ Language: Cpp
|
|||||||
Language: ObjC
|
Language: ObjC
|
||||||
BasedOnStyle: Google
|
BasedOnStyle: Google
|
||||||
ObjCSpaceAfterProperty: true
|
ObjCSpaceAfterProperty: true
|
||||||
|
AccessModifierOffset: -2
|
||||||
...
|
...
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ repos:
|
|||||||
rev: v20.1.8
|
rev: v20.1.8
|
||||||
hooks:
|
hooks:
|
||||||
- id: clang-format
|
- id: clang-format
|
||||||
files: '.*\.(hpp|cpp|h)'
|
files: '.*\.(hpp|cpp|h|mm)'
|
||||||
exclude: 'vendor/.*'
|
exclude: 'vendor/.*'
|
||||||
|
|
||||||
- repo: https://github.com/keith/pre-commit-buildifier
|
- repo: https://github.com/keith/pre-commit-buildifier
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
#import "ExampleCustomDrawableStyleLayer.h"
|
#import "ExampleCustomDrawableStyleLayer.h"
|
||||||
#import "MLNStyleLayer.h"
|
|
||||||
#import "MLNCustomDrawableStyleLayer.h"
|
#import "MLNCustomDrawableStyleLayer.h"
|
||||||
|
#import "MLNStyleLayer.h"
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
#include <mbgl/util/image+MLNAdditions.hpp>
|
|
||||||
#include <mbgl/layermanager/layer_factory.hpp>
|
#include <mbgl/layermanager/layer_factory.hpp>
|
||||||
#include <mbgl/style/layer.hpp>
|
#include <mbgl/style/layer.hpp>
|
||||||
#include <mbgl/style/layers/custom_drawable_layer.hpp>
|
#include <mbgl/style/layers/custom_drawable_layer.hpp>
|
||||||
#include <mbgl/util/constants.hpp>
|
#include <mbgl/util/constants.hpp>
|
||||||
|
#include <mbgl/util/image+MLNAdditions.hpp>
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
@interface MLNCustomDrawableStyleLayer (Internal)
|
@interface MLNCustomDrawableStyleLayer (Internal)
|
||||||
- (instancetype)initWithPendingLayer:(std::unique_ptr<mbgl::style::Layer>)pendingLayer;
|
- (instancetype)initWithPendingLayer:(std::unique_ptr<mbgl::style::Layer>)pendingLayer;
|
||||||
@@ -22,8 +22,8 @@ class ExampleCustomDrawableStyleLayerHost;
|
|||||||
@implementation ExampleCustomDrawableStyleLayer
|
@implementation ExampleCustomDrawableStyleLayer
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||||
auto layer = std::make_unique<mbgl::style::CustomDrawableLayer>(identifier.UTF8String,
|
auto layer = std::make_unique<mbgl::style::CustomDrawableLayer>(
|
||||||
std::make_unique<ExampleCustomDrawableStyleLayerHost>(self));
|
identifier.UTF8String, std::make_unique<ExampleCustomDrawableStyleLayerHost>(self));
|
||||||
return self = [super initWithPendingLayer:std::move(layer)];
|
return self = [super initWithPendingLayer:std::move(layer)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,15 +37,15 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void initialize() override {
|
void initialize() override {
|
||||||
if (layerRef == nil) return;
|
if (layerRef == nil)
|
||||||
else if (layer == nil) layer = layerRef;
|
return;
|
||||||
|
else if (layer == nil)
|
||||||
|
layer = layerRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(Interface& interface) override {
|
void update(Interface &interface) override {
|
||||||
|
|
||||||
// if we have built our drawable(s) already, either update or skip
|
// if we have built our drawable(s) already, either update or skip
|
||||||
if (interface.getDrawableCount())
|
if (interface.getDrawableCount()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
constexpr float extent = mbgl::util::EXTENT;
|
constexpr float extent = mbgl::util::EXTENT;
|
||||||
|
|
||||||
@@ -57,15 +57,21 @@ public:
|
|||||||
interface.setTileID({11, 327, 792});
|
interface.setTileID({11, 327, 792});
|
||||||
|
|
||||||
constexpr auto numLines = 6;
|
constexpr auto numLines = 6;
|
||||||
Interface::LineOptions options[numLines] {
|
Interface::LineOptions options[numLines]{
|
||||||
{/*geometry=*/{}, /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f, /*width=*/8.0f, /*color=*/Color::red() },
|
{/*geometry=*/{}, /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f,
|
||||||
{/*geometry=*/{}, /*blur=*/4.0f, /*opacity=*/1.0f, /*gapWidth=*/2.0f, /*offset=*/-1.0f, /*width=*/4.0f, /*color=*/Color::blue() },
|
/*width=*/8.0f, /*color=*/Color::red()},
|
||||||
{/*geometry=*/{}, /*blur=*/16.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/2.0f, /*width=*/16.0f, /*color=*/Color(1.f, 0.5f, 0, 0.5f) },
|
{/*geometry=*/{}, /*blur=*/4.0f, /*opacity=*/1.0f, /*gapWidth=*/2.0f, /*offset=*/-1.0f,
|
||||||
{/*geometry=*/{}, /*blur=*/2.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/-2.0f, /*width=*/2.0f, /*color=*/Color(1.f, 1.f, 0, 0.3f) },
|
/*width=*/4.0f, /*color=*/Color::blue()},
|
||||||
{/*geometry=*/{}, /*blur=*/0.5f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/0.5f, /*width=*/0.5f, /*color=*/Color::black() },
|
{/*geometry=*/{}, /*blur=*/16.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/2.0f,
|
||||||
{/*geometry=*/{}, /*blur=*/24.0f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/-5.0f, /*width=*/24.0f, /*color=*/Color(1.f, 0, 1.f, 0.2f) },
|
/*width=*/16.0f, /*color=*/Color(1.f, 0.5f, 0, 0.5f)},
|
||||||
|
{/*geometry=*/{}, /*blur=*/2.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/-2.0f,
|
||||||
|
/*width=*/2.0f, /*color=*/Color(1.f, 1.f, 0, 0.3f)},
|
||||||
|
{/*geometry=*/{}, /*blur=*/0.5f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/0.5f,
|
||||||
|
/*width=*/0.5f, /*color=*/Color::black()},
|
||||||
|
{/*geometry=*/{}, /*blur=*/24.0f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/-5.0f,
|
||||||
|
/*width=*/24.0f, /*color=*/Color(1.f, 0, 1.f, 0.2f)},
|
||||||
};
|
};
|
||||||
for(auto& opt: options) {
|
for (auto &opt : options) {
|
||||||
opt.geometry.beginCap = style::LineCapType::Butt;
|
opt.geometry.beginCap = style::LineCapType::Butt;
|
||||||
opt.geometry.endCap = style::LineCapType::Butt;
|
opt.geometry.endCap = style::LineCapType::Butt;
|
||||||
opt.geometry.joinType = style::LineJoinType::Miter;
|
opt.geometry.joinType = style::LineJoinType::Miter;
|
||||||
@@ -74,11 +80,12 @@ public:
|
|||||||
constexpr auto numPoints = 10;
|
constexpr auto numPoints = 10;
|
||||||
GeometryCoordinates polyline;
|
GeometryCoordinates polyline;
|
||||||
for (auto ipoint{0}; ipoint < numPoints; ++ipoint) {
|
for (auto ipoint{0}; ipoint < numPoints; ++ipoint) {
|
||||||
polyline.emplace_back(ipoint * extent / numPoints, std::sin(ipoint * 2 * M_PI / numPoints) * extent / numLines / 2.f);
|
polyline.emplace_back(ipoint * extent / numPoints,
|
||||||
|
std::sin(ipoint * 2 * M_PI / numPoints) * extent / numLines / 2.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto index {0}; index < numLines; ++index) {
|
for (auto index{0}; index < numLines; ++index) {
|
||||||
for(auto &p : polyline) {
|
for (auto &p : polyline) {
|
||||||
p.y += extent / numLines;
|
p.y += extent / numLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,15 +105,21 @@ public:
|
|||||||
interface.setTileID({11, 327, 792});
|
interface.setTileID({11, 327, 792});
|
||||||
|
|
||||||
constexpr auto numLines = 6;
|
constexpr auto numLines = 6;
|
||||||
Interface::LineOptions options[numLines] {
|
Interface::LineOptions options[numLines]{
|
||||||
{/*geometry=*/{}, /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f, /*width=*/8.0f, /*color=*/Color::red() },
|
{/*geometry=*/{}, /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f,
|
||||||
{/*geometry=*/{}, /*blur=*/4.0f, /*opacity=*/1.0f, /*gapWidth=*/2.0f, /*offset=*/-1.0f, /*width=*/4.0f, /*color=*/Color::blue() },
|
/*width=*/8.0f, /*color=*/Color::red()},
|
||||||
{/*geometry=*/{}, /*blur=*/16.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/2.0f, /*width=*/16.0f, /*color=*/Color(1.f, 0.5f, 0, 0.5f) },
|
{/*geometry=*/{}, /*blur=*/4.0f, /*opacity=*/1.0f, /*gapWidth=*/2.0f, /*offset=*/-1.0f,
|
||||||
{/*geometry=*/{}, /*blur=*/2.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/-2.0f, /*width=*/2.0f, /*color=*/Color(1.f, 1.f, 0, 0.3f) },
|
/*width=*/4.0f, /*color=*/Color::blue()},
|
||||||
{/*geometry=*/{}, /*blur=*/0.5f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/0.5f, /*width=*/0.5f, /*color=*/Color::black() },
|
{/*geometry=*/{}, /*blur=*/16.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/2.0f,
|
||||||
{/*geometry=*/{}, /*blur=*/24.0f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/-5.0f, /*width=*/24.0f, /*color=*/Color(1.f, 0, 1.f, 0.2f) },
|
/*width=*/16.0f, /*color=*/Color(1.f, 0.5f, 0, 0.5f)},
|
||||||
|
{/*geometry=*/{}, /*blur=*/2.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/-2.0f,
|
||||||
|
/*width=*/2.0f, /*color=*/Color(1.f, 1.f, 0, 0.3f)},
|
||||||
|
{/*geometry=*/{}, /*blur=*/0.5f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/0.5f,
|
||||||
|
/*width=*/0.5f, /*color=*/Color::black()},
|
||||||
|
{/*geometry=*/{}, /*blur=*/24.0f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/-5.0f,
|
||||||
|
/*width=*/24.0f, /*color=*/Color(1.f, 0, 1.f, 0.2f)},
|
||||||
};
|
};
|
||||||
for(auto& opt: options) {
|
for (auto &opt : options) {
|
||||||
opt.geometry.beginCap = style::LineCapType::Butt;
|
opt.geometry.beginCap = style::LineCapType::Butt;
|
||||||
opt.geometry.endCap = style::LineCapType::Butt;
|
opt.geometry.endCap = style::LineCapType::Butt;
|
||||||
opt.geometry.joinType = style::LineJoinType::Miter;
|
opt.geometry.joinType = style::LineJoinType::Miter;
|
||||||
@@ -115,11 +128,12 @@ public:
|
|||||||
constexpr auto numPoints = 10;
|
constexpr auto numPoints = 10;
|
||||||
GeometryCoordinates polyline;
|
GeometryCoordinates polyline;
|
||||||
for (auto ipoint{0}; ipoint < numPoints; ++ipoint) {
|
for (auto ipoint{0}; ipoint < numPoints; ++ipoint) {
|
||||||
polyline.emplace_back(ipoint * extent / numPoints, std::sin(ipoint * 2 * M_PI / numPoints) * extent / numLines / 2.f);
|
polyline.emplace_back(ipoint * extent / numPoints,
|
||||||
|
std::sin(ipoint * 2 * M_PI / numPoints) * extent / numLines / 2.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto index {0}; index < numLines; ++index) {
|
for (auto index{0}; index < numLines; ++index) {
|
||||||
for(auto &p : polyline) {
|
for (auto &p : polyline) {
|
||||||
if (0 == index) p.y += 0.25f * extent / numLines;
|
if (0 == index) p.y += 0.25f * extent / numLines;
|
||||||
p.y += extent / numLines;
|
p.y += extent / numLines;
|
||||||
}
|
}
|
||||||
@@ -131,11 +145,11 @@ public:
|
|||||||
interface.addPolyline(polyline, Interface::LineShaderType::WideVector);
|
interface.addPolyline(polyline, Interface::LineShaderType::WideVector);
|
||||||
|
|
||||||
// add clone
|
// add clone
|
||||||
for(auto &p : polyline) {
|
for (auto &p : polyline) {
|
||||||
p.y += 0.05f * extent / numLines;
|
p.y += 0.05f * extent / numLines;
|
||||||
}
|
}
|
||||||
interface.addPolyline(polyline, Interface::LineShaderType::WideVector);
|
interface.addPolyline(polyline, Interface::LineShaderType::WideVector);
|
||||||
for(auto &p : polyline) {
|
for (auto &p : polyline) {
|
||||||
p.y -= 0.05f * extent / numLines;
|
p.y -= 0.05f * extent / numLines;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,19 +165,19 @@ public:
|
|||||||
GeometryCollection geometry{
|
GeometryCollection geometry{
|
||||||
{
|
{
|
||||||
// ring 1
|
// ring 1
|
||||||
{static_cast<int16_t>(extent* 0.1f), static_cast<int16_t>(extent* 0.2f)},
|
{static_cast<int16_t>(extent * 0.1f), static_cast<int16_t>(extent * 0.2f)},
|
||||||
{static_cast<int16_t>(extent* 0.5f), static_cast<int16_t>(extent* 0.5f)},
|
{static_cast<int16_t>(extent * 0.5f), static_cast<int16_t>(extent * 0.5f)},
|
||||||
{static_cast<int16_t>(extent* 0.7f), static_cast<int16_t>(extent* 0.5f)},
|
{static_cast<int16_t>(extent * 0.7f), static_cast<int16_t>(extent * 0.5f)},
|
||||||
{static_cast<int16_t>(extent* 0.5f), static_cast<int16_t>(extent* 1.0f)},
|
{static_cast<int16_t>(extent * 0.5f), static_cast<int16_t>(extent * 1.0f)},
|
||||||
{static_cast<int16_t>(extent* 0.0f), static_cast<int16_t>(extent* 0.5f)},
|
{static_cast<int16_t>(extent * 0.0f), static_cast<int16_t>(extent * 0.5f)},
|
||||||
{static_cast<int16_t>(extent* 0.1f), static_cast<int16_t>(extent* 0.2f)},
|
{static_cast<int16_t>(extent * 0.1f), static_cast<int16_t>(extent * 0.2f)},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// ring 2
|
// ring 2
|
||||||
{static_cast<int16_t>(extent* 0.1f), static_cast<int16_t>(extent* 0.25f)},
|
{static_cast<int16_t>(extent * 0.1f), static_cast<int16_t>(extent * 0.25f)},
|
||||||
{static_cast<int16_t>(extent* 0.15f), static_cast<int16_t>(extent* 0.5f)},
|
{static_cast<int16_t>(extent * 0.15f), static_cast<int16_t>(extent * 0.5f)},
|
||||||
{static_cast<int16_t>(extent* 0.25f), static_cast<int16_t>(extent* 0.45f)},
|
{static_cast<int16_t>(extent * 0.25f), static_cast<int16_t>(extent * 0.45f)},
|
||||||
{static_cast<int16_t>(extent* 0.1f), static_cast<int16_t>(extent* 0.25f)},
|
{static_cast<int16_t>(extent * 0.1f), static_cast<int16_t>(extent * 0.25f)},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -181,23 +195,29 @@ public:
|
|||||||
// set tile
|
// set tile
|
||||||
interface.setTileID({11, 327, 789});
|
interface.setTileID({11, 327, 789});
|
||||||
|
|
||||||
GeometryCoordinate position {static_cast<int16_t>(extent* 0.5f), static_cast<int16_t>(extent* 0.5f)};
|
GeometryCoordinate position{static_cast<int16_t>(extent * 0.5f),
|
||||||
|
static_cast<int16_t>(extent * 0.5f)};
|
||||||
|
|
||||||
// load image
|
// load image
|
||||||
UIImage *assetImage = [UIImage imageNamed:@"pin"];
|
UIImage *assetImage = [UIImage imageNamed:@"pin"];
|
||||||
assert(assetImage.CGImage != NULL);
|
assert(assetImage.CGImage != NULL);
|
||||||
std::shared_ptr<PremultipliedImage> image = std::make_shared<PremultipliedImage>(MLNPremultipliedImageFromCGImage(assetImage.CGImage));
|
std::shared_ptr<PremultipliedImage> image = std::make_shared<PremultipliedImage>(
|
||||||
|
MLNPremultipliedImageFromCGImage(assetImage.CGImage));
|
||||||
|
|
||||||
// set symbol options
|
// set symbol options
|
||||||
Interface::SymbolOptions options;
|
Interface::SymbolOptions options;
|
||||||
options.texture = interface.context.createTexture2D();
|
options.texture = interface.context.createTexture2D();
|
||||||
options.texture->setImage(image);
|
options.texture->setImage(image);
|
||||||
options.texture->setSamplerConfiguration({gfx::TextureFilterType::Linear, gfx::TextureWrapType::Clamp, gfx::TextureWrapType::Clamp});
|
options.texture->setSamplerConfiguration({gfx::TextureFilterType::Linear,
|
||||||
const std::array<std::array<float, 2>, 2> textureCoordinates = {{{0.0f, 0.08f}, {1.0f, 0.9f}}};
|
gfx::TextureWrapType::Clamp,
|
||||||
|
gfx::TextureWrapType::Clamp});
|
||||||
|
const std::array<std::array<float, 2>, 2> textureCoordinates = {
|
||||||
|
{{0.0f, 0.08f}, {1.0f, 0.9f}}};
|
||||||
const float xspan = textureCoordinates[1][0] - textureCoordinates[0][0];
|
const float xspan = textureCoordinates[1][0] - textureCoordinates[0][0];
|
||||||
const float yspan = textureCoordinates[1][1] - textureCoordinates[0][1];
|
const float yspan = textureCoordinates[1][1] - textureCoordinates[0][1];
|
||||||
assert(xspan > 0.0f && yspan > 0.0f);
|
assert(xspan > 0.0f && yspan > 0.0f);
|
||||||
options.size = {static_cast<uint32_t>(image->size.width * xspan), static_cast<uint32_t>(image->size.height * yspan)};
|
options.size = {static_cast<uint32_t>(image->size.width * xspan),
|
||||||
|
static_cast<uint32_t>(image->size.height * yspan)};
|
||||||
options.anchor = {0.5f, 0.95f};
|
options.anchor = {0.5f, 0.95f};
|
||||||
options.angleDegrees = 45.0f;
|
options.angleDegrees = 45.0f;
|
||||||
options.scaleWithMap = true;
|
options.scaleWithMap = true;
|
||||||
@@ -213,14 +233,20 @@ public:
|
|||||||
using namespace mbgl;
|
using namespace mbgl;
|
||||||
|
|
||||||
// add polyline with geographic coordinates
|
// add polyline with geographic coordinates
|
||||||
Interface::LineOptions options = {/*geometry=*/{}, /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f, /*width=*/12.0f, /*color=*/{.0f, .0f, .0f, .5f} };
|
Interface::LineOptions options = {/*geometry=*/{},
|
||||||
|
/*blur=*/0.0f,
|
||||||
|
/*opacity=*/1.0f,
|
||||||
|
/*gapWidth=*/0.0f,
|
||||||
|
/*offset=*/0.0f,
|
||||||
|
/*width=*/12.0f,
|
||||||
|
/*color=*/{.0f, .0f, .0f, .5f}};
|
||||||
options.geometry.beginCap = style::LineCapType::Square;
|
options.geometry.beginCap = style::LineCapType::Square;
|
||||||
options.geometry.endCap = style::LineCapType::Square;
|
options.geometry.endCap = style::LineCapType::Square;
|
||||||
options.geometry.joinType = style::LineJoinType::Bevel;
|
options.geometry.joinType = style::LineJoinType::Bevel;
|
||||||
options.geometry.type = FeatureType::LineString;
|
options.geometry.type = FeatureType::LineString;
|
||||||
interface.setLineOptions(options);
|
interface.setLineOptions(options);
|
||||||
|
|
||||||
LineString<double> polyline_geo {
|
LineString<double> polyline_geo{
|
||||||
// San Francisco
|
// San Francisco
|
||||||
{-122.38186800073211, 37.77466003457463},
|
{-122.38186800073211, 37.77466003457463},
|
||||||
{-122.3869373450997, 37.774352128895615},
|
{-122.3869373450997, 37.774352128895615},
|
||||||
@@ -245,8 +271,13 @@ public:
|
|||||||
// set tile
|
// set tile
|
||||||
interface.setTileID({11, 327, 790});
|
interface.setTileID({11, 327, 790});
|
||||||
|
|
||||||
Interface::LineOptions options
|
Interface::LineOptions options{/*geometry=*/{},
|
||||||
{/*geometry=*/{}, /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f, /*width=*/7.0f, /*color=*/{1.0f, 0, 0, .5f} };
|
/*blur=*/0.0f,
|
||||||
|
/*opacity=*/1.0f,
|
||||||
|
/*gapWidth=*/0.0f,
|
||||||
|
/*offset=*/0.0f,
|
||||||
|
/*width=*/7.0f,
|
||||||
|
/*color=*/{1.0f, 0, 0, .5f}};
|
||||||
options.geometry.beginCap = style::LineCapType::Round;
|
options.geometry.beginCap = style::LineCapType::Round;
|
||||||
options.geometry.endCap = style::LineCapType::Round;
|
options.geometry.endCap = style::LineCapType::Round;
|
||||||
options.geometry.joinType = style::LineJoinType::Round;
|
options.geometry.joinType = style::LineJoinType::Round;
|
||||||
@@ -255,17 +286,17 @@ public:
|
|||||||
GeometryCollection polyline_tile{
|
GeometryCollection polyline_tile{
|
||||||
{
|
{
|
||||||
// ring 1
|
// ring 1
|
||||||
{static_cast<int16_t>(extent* 0.1f), static_cast<int16_t>(extent* 0.2f)},
|
{static_cast<int16_t>(extent * 0.1f), static_cast<int16_t>(extent * 0.2f)},
|
||||||
{static_cast<int16_t>(extent* 0.5f), static_cast<int16_t>(extent* 0.5f)},
|
{static_cast<int16_t>(extent * 0.5f), static_cast<int16_t>(extent * 0.5f)},
|
||||||
{static_cast<int16_t>(extent* 0.7f), static_cast<int16_t>(extent* 0.5f)},
|
{static_cast<int16_t>(extent * 0.7f), static_cast<int16_t>(extent * 0.5f)},
|
||||||
{static_cast<int16_t>(extent* 0.5f), static_cast<int16_t>(extent* 1.0f)},
|
{static_cast<int16_t>(extent * 0.5f), static_cast<int16_t>(extent * 1.0f)},
|
||||||
{static_cast<int16_t>(extent* 0.0f), static_cast<int16_t>(extent* 0.5f)},
|
{static_cast<int16_t>(extent * 0.0f), static_cast<int16_t>(extent * 0.5f)},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// ring 2
|
// ring 2
|
||||||
{static_cast<int16_t>(extent* 0.1f), static_cast<int16_t>(extent* 0.25f)},
|
{static_cast<int16_t>(extent * 0.1f), static_cast<int16_t>(extent * 0.25f)},
|
||||||
{static_cast<int16_t>(extent* 0.15f), static_cast<int16_t>(extent* 0.5f)},
|
{static_cast<int16_t>(extent * 0.15f), static_cast<int16_t>(extent * 0.5f)},
|
||||||
{static_cast<int16_t>(extent* 0.25f), static_cast<int16_t>(extent* 0.45f)},
|
{static_cast<int16_t>(extent * 0.25f), static_cast<int16_t>(extent * 0.45f)},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -273,7 +304,6 @@ public:
|
|||||||
interface.setLineOptions(options);
|
interface.setLineOptions(options);
|
||||||
interface.addPolyline(polyline_tile[0], Interface::LineShaderType::WideVector);
|
interface.addPolyline(polyline_tile[0], Interface::LineShaderType::WideVector);
|
||||||
interface.addPolyline(polyline_tile[1], Interface::LineShaderType::WideVector);
|
interface.addPolyline(polyline_tile[1], Interface::LineShaderType::WideVector);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// finish
|
// finish
|
||||||
@@ -286,7 +316,8 @@ public:
|
|||||||
layerRef = layer;
|
layerRef = layer;
|
||||||
layer = nil;
|
layer = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
__weak ExampleCustomDrawableStyleLayer * layerRef;
|
__weak ExampleCustomDrawableStyleLayer *layerRef;
|
||||||
ExampleCustomDrawableStyleLayer * layer = nil;
|
ExampleCustomDrawableStyleLayer *layer = nil;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,28 +2,23 @@
|
|||||||
|
|
||||||
@implementation PluginLayerExample
|
@implementation PluginLayerExample
|
||||||
|
|
||||||
|
|
||||||
// This is the layer type in the style that is used
|
// This is the layer type in the style that is used
|
||||||
+(MLNPluginLayerCapabilities *)layerCapabilities {
|
+ (MLNPluginLayerCapabilities *)layerCapabilities {
|
||||||
|
|
||||||
MLNPluginLayerCapabilities *tempResult = [[MLNPluginLayerCapabilities alloc] init];
|
MLNPluginLayerCapabilities *tempResult = [[MLNPluginLayerCapabilities alloc] init];
|
||||||
tempResult.layerID = @"plugin-layer-test";
|
tempResult.layerID = @"plugin-layer-test";
|
||||||
tempResult.requiresPass3D = YES;
|
tempResult.requiresPass3D = YES;
|
||||||
return tempResult;
|
return tempResult;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The overrides
|
// The overrides
|
||||||
-(void)onRenderLayer {
|
- (void)onRenderLayer {
|
||||||
NSLog(@"PluginLayerExample: On Render Layer");
|
NSLog(@"PluginLayerExample: On Render Layer");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)onUpdateLayer {
|
- (void)onUpdateLayer {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)onUpdateLayerProperties:(NSDictionary *)layerProperties {
|
- (void)onUpdateLayerProperties:(NSDictionary *)layerProperties {
|
||||||
// NSLog(@"Layer Properties: %@", layerProperties);
|
// NSLog(@"Layer Properties: %@", layerProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,7 @@
|
|||||||
#import <MetalKit/MetalKit.h>
|
#import <MetalKit/MetalKit.h>
|
||||||
#import "Mapbox.h"
|
#import "Mapbox.h"
|
||||||
|
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
|
||||||
vector_float2 position;
|
vector_float2 position;
|
||||||
vector_float4 color;
|
vector_float4 color;
|
||||||
} Vertex;
|
} Vertex;
|
||||||
@@ -26,20 +25,15 @@ typedef struct
|
|||||||
float _offsetY;
|
float _offsetY;
|
||||||
float _scale;
|
float _scale;
|
||||||
float _r, _g, _b, _a;
|
float _r, _g, _b, _a;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@implementation PluginLayerExampleMetalRendering
|
@implementation PluginLayerExampleMetalRendering
|
||||||
|
|
||||||
|
|
||||||
// This is a static class method that is used by the MLNMapView to create the
|
// This is a static class method that is used by the MLNMapView to create the
|
||||||
// required marshalling classes to interface with the MapLibre core.
|
// required marshalling classes to interface with the MapLibre core.
|
||||||
+(MLNPluginLayerCapabilities *)layerCapabilities {
|
+ (MLNPluginLayerCapabilities *)layerCapabilities {
|
||||||
|
|
||||||
MLNPluginLayerCapabilities *tempResult = [[MLNPluginLayerCapabilities alloc] init];
|
MLNPluginLayerCapabilities *tempResult = [[MLNPluginLayerCapabilities alloc] init];
|
||||||
tempResult.layerID = @"plugin-layer-metal-rendering";
|
tempResult.layerID = @"plugin-layer-metal-rendering";
|
||||||
tempResult.requiresPass3D = YES;
|
tempResult.requiresPass3D = YES;
|
||||||
@@ -60,43 +54,42 @@ typedef struct
|
|||||||
];
|
];
|
||||||
|
|
||||||
return tempResult;
|
return tempResult;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)createShaders:(MLNMapView *)mapView {
|
- (void)createShaders:(MLNMapView *)mapView {
|
||||||
MLNBackendResource* resource = [mapView backendResource];
|
MLNBackendResource *resource = [mapView backendResource];
|
||||||
|
|
||||||
NSString *shaderSource = @
|
NSString *shaderSource =
|
||||||
|
@
|
||||||
" #include <metal_stdlib>\n"
|
" #include <metal_stdlib>\n"
|
||||||
" using namespace metal;\n"
|
" using namespace metal;\n"
|
||||||
" typedef struct\n"
|
" typedef struct\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" vector_float2 position;\n"
|
" vector_float2 position;\n"
|
||||||
" vector_float4 color;\n"
|
" vector_float4 color;\n"
|
||||||
" } Vertex;\n"
|
" } Vertex;\n"
|
||||||
" struct RasterizerData\n"
|
" struct RasterizerData\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" float4 position [[position]];\n"
|
" float4 position [[position]];\n"
|
||||||
" float4 color;\n"
|
" float4 color;\n"
|
||||||
" };\n"
|
" };\n"
|
||||||
" vertex RasterizerData\n"
|
" vertex RasterizerData\n"
|
||||||
" vertexShader(uint vertexID [[vertex_id]],\n"
|
" vertexShader(uint vertexID [[vertex_id]],\n"
|
||||||
" constant Vertex *vertices [[buffer(0)]],\n"
|
" constant Vertex *vertices [[buffer(0)]],\n"
|
||||||
" constant vector_uint2 *viewportSizePointer [[buffer(1)]])\n"
|
" constant vector_uint2 *viewportSizePointer [[buffer(1)]])\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" RasterizerData out;\n"
|
" RasterizerData out;\n"
|
||||||
" float2 pixelSpacePosition = vertices[vertexID].position.xy;\n"
|
" float2 pixelSpacePosition = vertices[vertexID].position.xy;\n"
|
||||||
" vector_float2 viewportSize = vector_float2(*viewportSizePointer);\n"
|
" vector_float2 viewportSize = vector_float2(*viewportSizePointer);\n"
|
||||||
" out.position = vector_float4(0.0, 0.0, 0.0, 1.0);\n"
|
" out.position = vector_float4(0.0, 0.0, 0.0, 1.0);\n"
|
||||||
" out.position.xy = pixelSpacePosition / (viewportSize / 2.0);\n"
|
" out.position.xy = pixelSpacePosition / (viewportSize / 2.0);\n"
|
||||||
" out.color = vertices[vertexID].color;\n"
|
" out.color = vertices[vertexID].color;\n"
|
||||||
" return out;\n"
|
" return out;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" fragment float4 fragmentShader(RasterizerData in [[stage_in]])\n"
|
" fragment float4 fragmentShader(RasterizerData in [[stage_in]])\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" return in.color;\n"
|
" return in.color;\n"
|
||||||
" }\n";
|
" }\n";
|
||||||
|
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
id<MTLDevice> _device = resource.device;
|
id<MTLDevice> _device = resource.device;
|
||||||
@@ -120,30 +113,28 @@ typedef struct
|
|||||||
|
|
||||||
// Notice that we don't configure the stencilTest property, leaving stencil testing disabled
|
// Notice that we don't configure the stencilTest property, leaving stencil testing disabled
|
||||||
MTLDepthStencilDescriptor *depthStencilDescriptor = [[MTLDepthStencilDescriptor alloc] init];
|
MTLDepthStencilDescriptor *depthStencilDescriptor = [[MTLDepthStencilDescriptor alloc] init];
|
||||||
depthStencilDescriptor.depthCompareFunction = MTLCompareFunctionAlways; // Or another value as needed
|
depthStencilDescriptor.depthCompareFunction =
|
||||||
|
MTLCompareFunctionAlways; // Or another value as needed
|
||||||
depthStencilDescriptor.depthWriteEnabled = NO;
|
depthStencilDescriptor.depthWriteEnabled = NO;
|
||||||
|
|
||||||
_depthStencilStateWithoutStencil = [_device newDepthStencilStateWithDescriptor:depthStencilDescriptor];
|
_depthStencilStateWithoutStencil =
|
||||||
|
[_device newDepthStencilStateWithDescriptor:depthStencilDescriptor];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Setup the rendering environment
|
// Setup the rendering environment
|
||||||
-(void)setupRendering:(MLNMapView *)mapView {
|
- (void)setupRendering:(MLNMapView *)mapView {
|
||||||
[self createShaders:mapView];
|
[self createShaders:mapView];
|
||||||
if (_scale == 0) {
|
if (_scale == 0) {
|
||||||
_scale = 1;
|
_scale = 1;
|
||||||
}
|
}
|
||||||
// Set the initial
|
// Set the initial
|
||||||
_a = 1;
|
_a = 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The overrides
|
// The overrides
|
||||||
-(void)onRenderLayer:(MLNMapView *)mapView
|
- (void)onRenderLayer:(MLNMapView *)mapView
|
||||||
renderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder {
|
renderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder {
|
||||||
|
MLNBackendResource *resource = [mapView backendResource];
|
||||||
MLNBackendResource* resource = [mapView backendResource];
|
|
||||||
|
|
||||||
if (_pipelineState == nil) {
|
if (_pipelineState == nil) {
|
||||||
[self setupRendering:mapView];
|
[self setupRendering:mapView];
|
||||||
@@ -153,16 +144,15 @@ typedef struct
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vector_uint2 _viewportSize;
|
vector_uint2 _viewportSize;
|
||||||
_viewportSize.x = resource.mtkView.drawableSize.width;
|
_viewportSize.x = resource.mtkView.drawableSize.width;
|
||||||
_viewportSize.y = resource.mtkView.drawableSize.height;
|
_viewportSize.y = resource.mtkView.drawableSize.height;
|
||||||
|
|
||||||
Vertex triangleVerticesWithColor[] = {
|
Vertex triangleVerticesWithColor[] = {
|
||||||
// 2D positions, RGBA colors
|
// 2D positions, RGBA colors
|
||||||
{ { (250 + _offsetX) * _scale, (-250 + _offsetY) * _scale }, { 1, 1, 1, _a } },
|
{{(250 + _offsetX) * _scale, (-250 + _offsetY) * _scale}, {1, 1, 1, _a}},
|
||||||
{ { (-250 + _offsetX) * _scale, (-250 + _offsetY) * _scale }, { 1, 1, 1, _a} },
|
{{(-250 + _offsetX) * _scale, (-250 + _offsetY) * _scale}, {1, 1, 1, _a}},
|
||||||
{ { (0 + _offsetX) * _scale, (250 + _offsetY) * _scale }, { _r, _g, _b, _a } },
|
{{(0 + _offsetX) * _scale, (250 + _offsetY) * _scale}, {_r, _g, _b, _a}},
|
||||||
};
|
};
|
||||||
|
|
||||||
[renderEncoder setRenderPipelineState:_pipelineState];
|
[renderEncoder setRenderPipelineState:_pipelineState];
|
||||||
@@ -173,23 +163,19 @@ typedef struct
|
|||||||
length:sizeof(triangleVerticesWithColor)
|
length:sizeof(triangleVerticesWithColor)
|
||||||
atIndex:0];
|
atIndex:0];
|
||||||
|
|
||||||
[renderEncoder setVertexBytes:&_viewportSize
|
[renderEncoder setVertexBytes:&_viewportSize length:sizeof(_viewportSize) atIndex:1];
|
||||||
length:sizeof(_viewportSize)
|
|
||||||
atIndex:1];
|
|
||||||
|
|
||||||
// Draw the triangle.
|
// Draw the triangle.
|
||||||
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
|
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
|
||||||
vertexStart:0
|
|
||||||
vertexCount:3];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)onUpdateLayer:(MLNPluginLayerDrawingContext)drawingContext {
|
- (void)onUpdateLayer:(MLNPluginLayerDrawingContext)drawingContext {
|
||||||
// This is called before the render call and is a place where
|
// This is called before the render call and is a place where
|
||||||
// any animations/etc can be updated
|
// any animations/etc can be updated
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)onUpdateLayerProperties:(NSDictionary *)layerProperties {
|
- (void)onUpdateLayerProperties:(NSDictionary *)layerProperties {
|
||||||
//NSLog(@"Metal Layer Rendering Properties: %@", layerProperties);
|
// NSLog(@"Metal Layer Rendering Properties: %@", layerProperties);
|
||||||
|
|
||||||
// These two properties are in the "properties" section of the style sheet and
|
// These two properties are in the "properties" section of the style sheet and
|
||||||
// are passed in at layer creation. These are not expression based properties
|
// are passed in at layer creation. These are not expression based properties
|
||||||
@@ -214,7 +200,7 @@ typedef struct
|
|||||||
|
|
||||||
NSString *fillColor = [layerProperties objectForKey:@"fill-color"];
|
NSString *fillColor = [layerProperties objectForKey:@"fill-color"];
|
||||||
if (fillColor) {
|
if (fillColor) {
|
||||||
//NSLog(@"Fill Color: %@", fillColor);
|
// NSLog(@"Fill Color: %@", fillColor);
|
||||||
fillColor = [fillColor stringByReplacingOccurrencesOfString:@"rgba(" withString:@""];
|
fillColor = [fillColor stringByReplacingOccurrencesOfString:@"rgba(" withString:@""];
|
||||||
fillColor = [fillColor stringByReplacingOccurrencesOfString:@")" withString:@""];
|
fillColor = [fillColor stringByReplacingOccurrencesOfString:@")" withString:@""];
|
||||||
NSArray *components = [fillColor componentsSeparatedByString:@","];
|
NSArray *components = [fillColor componentsSeparatedByString:@","];
|
||||||
@@ -225,9 +211,7 @@ typedef struct
|
|||||||
_b = [[components objectAtIndex:2] floatValue] / 255.0;
|
_b = [[components objectAtIndex:2] floatValue] / 255.0;
|
||||||
_a = [[components objectAtIndex:3] floatValue];
|
_a = [[components objectAtIndex:3] floatValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -11,11 +11,9 @@ class Collator::Impl {
|
|||||||
public:
|
public:
|
||||||
Impl(bool caseSensitive, bool diacriticSensitive, const std::optional<std::string>& locale_)
|
Impl(bool caseSensitive, bool diacriticSensitive, const std::optional<std::string>& locale_)
|
||||||
: options((caseSensitive ? 0 : NSCaseInsensitiveSearch) |
|
: options((caseSensitive ? 0 : NSCaseInsensitiveSearch) |
|
||||||
(diacriticSensitive ? 0 : NSDiacriticInsensitiveSearch))
|
(diacriticSensitive ? 0 : NSDiacriticInsensitiveSearch)),
|
||||||
, locale(locale_ ?
|
locale(locale_ ? [[NSLocale alloc] initWithLocaleIdentifier:@((*locale_).c_str())]
|
||||||
[[NSLocale alloc] initWithLocaleIdentifier:@((*locale_).c_str())] :
|
: [NSLocale currentLocale]) {}
|
||||||
[NSLocale currentLocale])
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool operator==(const Impl& other) const {
|
bool operator==(const Impl& other) const {
|
||||||
return options == other.options &&
|
return options == other.options &&
|
||||||
@@ -37,31 +35,30 @@ public:
|
|||||||
// Following documentation at:
|
// Following documentation at:
|
||||||
// https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/LanguageandLocaleIDs/LanguageandLocaleIDs.html#//apple_ref/doc/uid/10000171i-CH15-SW9
|
// https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/LanguageandLocaleIDs/LanguageandLocaleIDs.html#//apple_ref/doc/uid/10000171i-CH15-SW9
|
||||||
// We expect NSLocale to accept BCP 47 tags as localeIdentifier inputs, but the output format
|
// We expect NSLocale to accept BCP 47 tags as localeIdentifier inputs, but the output format
|
||||||
// may append the region tag with an "_". Changing that to a "-" makes the identifier BCP 47 compliant.
|
// may append the region tag with an "_". Changing that to a "-" makes the identifier BCP 47
|
||||||
// Experimentally, "zh-Hans-HK" and "zh-Hans_HK" both round trip -- if the second is used by
|
// compliant. Experimentally, "zh-Hans-HK" and "zh-Hans_HK" both round trip -- if the second is
|
||||||
|
// used by
|
||||||
// `currentLocale`, we don't want to return the underscore.
|
// `currentLocale`, we don't want to return the underscore.
|
||||||
return [[locale localeIdentifier] stringByReplacingOccurrencesOfString:@"_" withString:@"-"].UTF8String;
|
return [[locale localeIdentifier] stringByReplacingOccurrencesOfString:@"_" withString:@"-"]
|
||||||
|
.UTF8String;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NSStringCompareOptions options;
|
NSStringCompareOptions options;
|
||||||
NSLocale* locale;
|
NSLocale* locale;
|
||||||
};
|
};
|
||||||
|
|
||||||
Collator::Collator(bool caseSensitive, bool diacriticSensitive, const std::optional<std::string>& locale_)
|
Collator::Collator(bool caseSensitive, bool diacriticSensitive,
|
||||||
: impl(std::make_shared<Impl>(caseSensitive, diacriticSensitive, locale_))
|
const std::optional<std::string>& locale_)
|
||||||
{}
|
: impl(std::make_shared<Impl>(caseSensitive, diacriticSensitive, locale_)) {}
|
||||||
|
|
||||||
bool Collator::operator==(const Collator& other) const {
|
bool Collator::operator==(const Collator& other) const { return *impl == *(other.impl); }
|
||||||
return *impl == *(other.impl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Collator::compare(const std::string& lhs, const std::string& rhs) const {
|
int Collator::compare(const std::string& lhs, const std::string& rhs) const {
|
||||||
return impl->compare(lhs, rhs);
|
return impl->compare(lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Collator::resolvedLocale() const {
|
std::string Collator::resolvedLocale() const { return impl->resolvedLocale(); }
|
||||||
return impl->resolvedLocale();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace platform
|
} // namespace platform
|
||||||
} // namespace mbgl
|
} // namespace mbgl
|
||||||
|
|||||||
@@ -3,17 +3,17 @@
|
|||||||
#include <mbgl/storage/resource_options.hpp>
|
#include <mbgl/storage/resource_options.hpp>
|
||||||
#include <mbgl/storage/response.hpp>
|
#include <mbgl/storage/response.hpp>
|
||||||
|
|
||||||
|
#include <mbgl/util/async_request.hpp>
|
||||||
|
#include <mbgl/util/async_task.hpp>
|
||||||
#include <mbgl/util/client_options.hpp>
|
#include <mbgl/util/client_options.hpp>
|
||||||
#include <mbgl/util/http_header.hpp>
|
#include <mbgl/util/http_header.hpp>
|
||||||
#include <mbgl/util/async_task.hpp>
|
|
||||||
#include <mbgl/util/async_request.hpp>
|
|
||||||
#include <mbgl/util/version.hpp>
|
#include <mbgl/util/version.hpp>
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
#include <mbgl/interface/native_apple_interface.h>
|
#include <mbgl/interface/native_apple_interface.h>
|
||||||
#include <mutex>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
@interface MBGLBundleCanary : NSObject
|
@interface MBGLBundleCanary : NSObject
|
||||||
@end
|
@end
|
||||||
@@ -26,12 +26,10 @@ namespace mbgl {
|
|||||||
// Data that is shared between the requesting thread and the thread running the completion handler.
|
// Data that is shared between the requesting thread and the thread running the completion handler.
|
||||||
class HTTPRequestShared {
|
class HTTPRequestShared {
|
||||||
public:
|
public:
|
||||||
HTTPRequestShared(Response& response_, util::AsyncTask& async_)
|
HTTPRequestShared(Response &response_, util::AsyncTask &async_)
|
||||||
: response(response_),
|
: response(response_), async(async_) {}
|
||||||
async(async_) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void notify(const Response& response_) {
|
void notify(const Response &response_) {
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock(mutex);
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
response = response_;
|
response = response_;
|
||||||
@@ -48,16 +46,14 @@ private:
|
|||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
bool cancelled = false;
|
bool cancelled = false;
|
||||||
|
|
||||||
Response& response;
|
Response &response;
|
||||||
util::AsyncTask& async;
|
util::AsyncTask &async;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HTTPRequest : public AsyncRequest {
|
class HTTPRequest : public AsyncRequest {
|
||||||
public:
|
public:
|
||||||
HTTPRequest(FileSource::Callback callback_)
|
HTTPRequest(FileSource::Callback callback_)
|
||||||
: shared(std::make_shared<HTTPRequestShared>(response, async)),
|
: shared(std::make_shared<HTTPRequestShared>(response, async)), callback(callback_) {}
|
||||||
callback(callback_) {
|
|
||||||
}
|
|
||||||
|
|
||||||
~HTTPRequest() override {
|
~HTTPRequest() override {
|
||||||
shared->cancel();
|
shared->cancel();
|
||||||
@@ -68,25 +64,26 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<HTTPRequestShared> shared;
|
std::shared_ptr<HTTPRequestShared> shared;
|
||||||
NSURLSessionDataTask* task = nil;
|
NSURLSessionDataTask *task = nil;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FileSource::Callback callback;
|
FileSource::Callback callback;
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
util::AsyncTask async { [this] {
|
util::AsyncTask async{[this] {
|
||||||
// Calling `callback` may result in deleting `this`. Copy data to temporaries first.
|
// Calling `callback` may result in deleting `this`. Copy data to temporaries first.
|
||||||
auto callback_ = callback;
|
auto callback_ = callback;
|
||||||
auto response_ = response;
|
auto response_ = response;
|
||||||
callback_(response_);
|
callback_(response_);
|
||||||
} };
|
}};
|
||||||
};
|
};
|
||||||
|
|
||||||
class HTTPFileSource::Impl {
|
class HTTPFileSource::Impl {
|
||||||
public:
|
public:
|
||||||
Impl(const ResourceOptions& resourceOptions_, const ClientOptions& clientOptions_)
|
Impl(const ResourceOptions &resourceOptions_, const ClientOptions &clientOptions_)
|
||||||
: resourceOptions(resourceOptions_.clone()), clientOptions(clientOptions_.clone()) {
|
: resourceOptions(resourceOptions_.clone()), clientOptions(clientOptions_.clone()) {
|
||||||
NSURLSessionConfiguration *sessionConfig = MLNNativeNetworkManager.sharedManager.sessionConfiguration;
|
NSURLSessionConfiguration *sessionConfig =
|
||||||
|
MLNNativeNetworkManager.sharedManager.sessionConfiguration;
|
||||||
session = [NSURLSession sessionWithConfiguration:sessionConfig];
|
session = [NSURLSession sessionWithConfiguration:sessionConfig];
|
||||||
|
|
||||||
if (sessionConfig.HTTPAdditionalHeaders[@"User-Agent"] == nil) {
|
if (sessionConfig.HTTPAdditionalHeaders[@"User-Agent"] == nil) {
|
||||||
@@ -102,12 +99,12 @@ public:
|
|||||||
void setClientOptions(ClientOptions options);
|
void setClientOptions(ClientOptions options);
|
||||||
ClientOptions getClientOptions();
|
ClientOptions getClientOptions();
|
||||||
|
|
||||||
NSURLSession* session = nil;
|
NSURLSession *session = nil;
|
||||||
NSString* userAgent = nil;
|
NSString *userAgent = nil;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NSString* getUserAgent() const;
|
NSString *getUserAgent() const;
|
||||||
NSBundle* getSDKBundle() const;
|
NSBundle *getSDKBundle() const;
|
||||||
ResourceOptions resourceOptions;
|
ResourceOptions resourceOptions;
|
||||||
ClientOptions clientOptions;
|
ClientOptions clientOptions;
|
||||||
};
|
};
|
||||||
@@ -116,17 +113,11 @@ void HTTPFileSource::Impl::setResourceOptions(ResourceOptions options) {
|
|||||||
resourceOptions = options;
|
resourceOptions = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceOptions HTTPFileSource::Impl::getResourceOptions() {
|
ResourceOptions HTTPFileSource::Impl::getResourceOptions() { return resourceOptions.clone(); }
|
||||||
return resourceOptions.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPFileSource::Impl::setClientOptions(ClientOptions options) {
|
void HTTPFileSource::Impl::setClientOptions(ClientOptions options) { clientOptions = options; }
|
||||||
clientOptions = options;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientOptions HTTPFileSource::Impl::getClientOptions() {
|
ClientOptions HTTPFileSource::Impl::getClientOptions() { return clientOptions.clone(); }
|
||||||
return clientOptions.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *HTTPFileSource::Impl::getUserAgent() const {
|
NSString *HTTPFileSource::Impl::getUserAgent() const {
|
||||||
NSMutableArray *userAgentComponents = [NSMutableArray array];
|
NSMutableArray *userAgentComponents = [NSMutableArray array];
|
||||||
@@ -134,8 +125,12 @@ NSString *HTTPFileSource::Impl::getUserAgent() const {
|
|||||||
NSBundle *appBundle = [NSBundle mainBundle];
|
NSBundle *appBundle = [NSBundle mainBundle];
|
||||||
if (appBundle) {
|
if (appBundle) {
|
||||||
NSString *appName = appBundle.infoDictionary[@"CFBundleName"];
|
NSString *appName = appBundle.infoDictionary[@"CFBundleName"];
|
||||||
[userAgentComponents addObject:[NSString stringWithFormat:@"%@/%@",
|
[userAgentComponents
|
||||||
appName.length ? appName : appBundle.infoDictionary[@"CFBundleIdentifier"],
|
addObject:[NSString
|
||||||
|
stringWithFormat:@"%@/%@",
|
||||||
|
appName.length
|
||||||
|
? appName
|
||||||
|
: appBundle.infoDictionary[@"CFBundleIdentifier"],
|
||||||
appBundle.infoDictionary[@"CFBundleShortVersionString"]]];
|
appBundle.infoDictionary[@"CFBundleShortVersionString"]]];
|
||||||
} else {
|
} else {
|
||||||
[userAgentComponents addObject:[NSProcessInfo processInfo].processName];
|
[userAgentComponents addObject:[NSProcessInfo processInfo].processName];
|
||||||
@@ -145,8 +140,9 @@ NSString *HTTPFileSource::Impl::getUserAgent() const {
|
|||||||
if (sdkBundle) {
|
if (sdkBundle) {
|
||||||
NSString *versionString = sdkBundle.infoDictionary[@"CFBundleShortVersionString"];
|
NSString *versionString = sdkBundle.infoDictionary[@"CFBundleShortVersionString"];
|
||||||
if (versionString) {
|
if (versionString) {
|
||||||
[userAgentComponents addObject:[NSString stringWithFormat:@"%@/%@",
|
[userAgentComponents
|
||||||
sdkBundle.infoDictionary[@"CFBundleName"], versionString]];
|
addObject:[NSString stringWithFormat:@"%@/%@", sdkBundle.infoDictionary[@"CFBundleName"],
|
||||||
|
versionString]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,8 +167,9 @@ NSString *HTTPFileSource::Impl::getUserAgent() const {
|
|||||||
NSString *systemVersion = nil;
|
NSString *systemVersion = nil;
|
||||||
if ([NSProcessInfo instancesRespondToSelector:@selector(operatingSystemVersion)]) {
|
if ([NSProcessInfo instancesRespondToSelector:@selector(operatingSystemVersion)]) {
|
||||||
NSOperatingSystemVersion osVersion = [NSProcessInfo processInfo].operatingSystemVersion;
|
NSOperatingSystemVersion osVersion = [NSProcessInfo processInfo].operatingSystemVersion;
|
||||||
systemVersion = [NSString stringWithFormat:@"%ld.%ld.%ld",
|
systemVersion =
|
||||||
(long)osVersion.majorVersion, (long)osVersion.minorVersion, (long)osVersion.patchVersion];
|
[NSString stringWithFormat:@"%ld.%ld.%ld", (long)osVersion.majorVersion,
|
||||||
|
(long)osVersion.minorVersion, (long)osVersion.patchVersion];
|
||||||
}
|
}
|
||||||
if (systemVersion) {
|
if (systemVersion) {
|
||||||
[userAgentComponents addObject:[NSString stringWithFormat:@"%@/%@", systemName, systemVersion]];
|
[userAgentComponents addObject:[NSString stringWithFormat:@"%@/%@", systemName, systemVersion]];
|
||||||
@@ -206,28 +203,26 @@ NSBundle *HTTPFileSource::Impl::getSDKBundle() const {
|
|||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTPFileSource::HTTPFileSource(const ResourceOptions& resourceOptions, const ClientOptions& clientOptions)
|
HTTPFileSource::HTTPFileSource(const ResourceOptions &resourceOptions,
|
||||||
: impl(std::make_unique<Impl>(resourceOptions, clientOptions)) {
|
const ClientOptions &clientOptions)
|
||||||
}
|
: impl(std::make_unique<Impl>(resourceOptions, clientOptions)) {}
|
||||||
|
|
||||||
HTTPFileSource::~HTTPFileSource() = default;
|
HTTPFileSource::~HTTPFileSource() = default;
|
||||||
|
|
||||||
MLN_APPLE_EXPORT
|
MLN_APPLE_EXPORT
|
||||||
BOOL isValidMapboxEndpoint(NSURL *url) {
|
BOOL isValidMapboxEndpoint(NSURL *url) {
|
||||||
return ([url.host isEqualToString:@"mapbox.com"] ||
|
return ([url.host isEqualToString:@"mapbox.com"] || [url.host hasSuffix:@".mapbox.com"] ||
|
||||||
[url.host hasSuffix:@".mapbox.com"] ||
|
[url.host isEqualToString:@"mapbox.cn"] || [url.host hasSuffix:@".mapbox.cn"]);
|
||||||
[url.host isEqualToString:@"mapbox.cn"] ||
|
|
||||||
[url.host hasSuffix:@".mapbox.cn"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MLN_APPLE_EXPORT
|
MLN_APPLE_EXPORT
|
||||||
NSURL *resourceURL(const Resource& resource) {
|
NSURL *resourceURL(const Resource &resource) {
|
||||||
|
|
||||||
NSURL *url = [NSURL URLWithString:@(resource.url.c_str())];
|
NSURL *url = [NSURL URLWithString:@(resource.url.c_str())];
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
||||||
if (isValidMapboxEndpoint(url)) {
|
if (isValidMapboxEndpoint(url)) {
|
||||||
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
|
NSURLComponents *components = [NSURLComponents componentsWithURL:url
|
||||||
|
resolvingAgainstBaseURL:NO];
|
||||||
NSMutableArray *queryItems = [NSMutableArray array];
|
NSMutableArray *queryItems = [NSMutableArray array];
|
||||||
|
|
||||||
if (resource.usage == Resource::Usage::Offline) {
|
if (resource.usage == Resource::Usage::Offline) {
|
||||||
@@ -245,9 +240,11 @@ NSURL *resourceURL(const Resource& resource) {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, std::function<void(Response)> callback) {
|
std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource &resource,
|
||||||
|
std::function<void(Response)> callback) {
|
||||||
auto request = std::make_unique<HTTPRequest>(std::move(callback));
|
auto request = std::make_unique<HTTPRequest>(std::move(callback));
|
||||||
auto shared = request->shared; // Explicit copy so that it also gets copied into the completion handler block below.
|
auto shared = request->shared; // Explicit copy so that it also gets copied into the completion
|
||||||
|
// handler block below.
|
||||||
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
NSURL *url = resourceURL(resource);
|
NSURL *url = resourceURL(resource);
|
||||||
@@ -255,16 +252,15 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource,
|
|||||||
|
|
||||||
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
|
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
|
||||||
if (resource.priorEtag) {
|
if (resource.priorEtag) {
|
||||||
[req addValue:@(resource.priorEtag->c_str())
|
[req addValue:@(resource.priorEtag->c_str()) forHTTPHeaderField:@"If-None-Match"];
|
||||||
forHTTPHeaderField:@"If-None-Match"];
|
|
||||||
} else if (resource.priorModified) {
|
} else if (resource.priorModified) {
|
||||||
[req addValue:@(util::rfc1123(*resource.priorModified).c_str())
|
[req addValue:@(util::rfc1123(*resource.priorModified).c_str())
|
||||||
forHTTPHeaderField:@"If-Modified-Since"];
|
forHTTPHeaderField:@"If-Modified-Since"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource.dataRange) {
|
if (resource.dataRange) {
|
||||||
NSString *rangeHeader = [NSString stringWithFormat:@"bytes=%lld-%lld",
|
NSString *rangeHeader = [NSString
|
||||||
static_cast<long long>(resource.dataRange->first),
|
stringWithFormat:@"bytes=%lld-%lld", static_cast<long long>(resource.dataRange->first),
|
||||||
static_cast<long long>(resource.dataRange->second)];
|
static_cast<long long>(resource.dataRange->second)];
|
||||||
[req setValue:rangeHeader forHTTPHeaderField:@"Range"];
|
[req setValue:rangeHeader forHTTPHeaderField:@"Range"];
|
||||||
}
|
}
|
||||||
@@ -298,18 +294,18 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource,
|
|||||||
|
|
||||||
request->task = [session
|
request->task = [session
|
||||||
dataTaskWithRequest:req
|
dataTaskWithRequest:req
|
||||||
completionHandler:^(NSData* data, NSURLResponse* res, NSError* error) {
|
completionHandler:^(NSData *data, NSURLResponse *res, NSError *error) {
|
||||||
session = nil;
|
session = nil;
|
||||||
|
|
||||||
if ([networkManager.delegate respondsToSelector:@selector(didReceiveResponse:)]) {
|
if ([networkManager.delegate respondsToSelector:@selector(didReceiveResponse:)]) {
|
||||||
MLNInternalNetworkResponse *networkResponse = [MLNInternalNetworkResponse responseWithData:data urlResponse:res error:error];
|
MLNInternalNetworkResponse *networkResponse =
|
||||||
|
[MLNInternalNetworkResponse responseWithData:data urlResponse:res error:error];
|
||||||
networkResponse = [networkManager.delegate didReceiveResponse:networkResponse];
|
networkResponse = [networkManager.delegate didReceiveResponse:networkResponse];
|
||||||
data = networkResponse.data;
|
data = networkResponse.data;
|
||||||
res = networkResponse.response;
|
res = networkResponse.response;
|
||||||
error = networkResponse.error;
|
error = networkResponse.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (error && [error code] == NSURLErrorCancelled) {
|
if (error && [error code] == NSURLErrorCancelled) {
|
||||||
[MLNNativeNetworkManager.sharedManager cancelDownloadEventForResponse:res];
|
[MLNNativeNetworkManager.sharedManager cancelDownloadEventForResponse:res];
|
||||||
return;
|
return;
|
||||||
@@ -319,11 +315,13 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource,
|
|||||||
using Error = Response::Error;
|
using Error = Response::Error;
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
[MLNNativeNetworkManager.sharedManager errorLog:@"Requesting: %@ failed with error: %@", req.URL, error.debugDescription];
|
[MLNNativeNetworkManager.sharedManager
|
||||||
|
errorLog:@"Requesting: %@ failed with error: %@", req.URL,
|
||||||
|
error.debugDescription];
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
response.data =
|
response.data =
|
||||||
std::make_shared<std::string>((const char*)[data bytes], [data length]);
|
std::make_shared<std::string>((const char *)[data bytes], [data length]);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ([error code]) {
|
switch ([error code]) {
|
||||||
@@ -352,7 +350,9 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource,
|
|||||||
}
|
}
|
||||||
} else if ([res isKindOfClass:[NSHTTPURLResponse class]]) {
|
} else if ([res isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||||
const long responseCode = [(NSHTTPURLResponse *)res statusCode];
|
const long responseCode = [(NSHTTPURLResponse *)res statusCode];
|
||||||
[MLNNativeNetworkManager.sharedManager debugLog:@"Requesting %@ returned responseCode: %lu", res.URL.relativePath, responseCode];
|
[MLNNativeNetworkManager.sharedManager
|
||||||
|
debugLog:@"Requesting %@ returned responseCode: %lu", res.URL.relativePath,
|
||||||
|
responseCode];
|
||||||
|
|
||||||
NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields];
|
NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields];
|
||||||
NSString *cache_control = [headers objectForKey:@"Cache-Control"];
|
NSString *cache_control = [headers objectForKey:@"Cache-Control"];
|
||||||
@@ -378,7 +378,8 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (responseCode == 200 || responseCode == 206) {
|
if (responseCode == 200 || responseCode == 206) {
|
||||||
response.data = std::make_shared<std::string>((const char *)[data bytes], [data length]);
|
response.data =
|
||||||
|
std::make_shared<std::string>((const char *)[data bytes], [data length]);
|
||||||
} else if (responseCode == 204 || (responseCode == 404 && isTile)) {
|
} else if (responseCode == 204 || (responseCode == 404 && isTile)) {
|
||||||
response.noContent = true;
|
response.noContent = true;
|
||||||
} else if (responseCode == 304) {
|
} else if (responseCode == 304) {
|
||||||
@@ -401,18 +402,21 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource,
|
|||||||
xRateLimitReset = std::string([xReset UTF8String]);
|
xRateLimitReset = std::string([xReset UTF8String]);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.error = std::make_unique<Error>(Error::Reason::RateLimit, "HTTP status code 429", http::parseRetryHeaders(retryAfter, xRateLimitReset));
|
|
||||||
} else if (responseCode >= 500 && responseCode < 600) {
|
|
||||||
response.error =
|
response.error =
|
||||||
std::make_unique<Error>(Error::Reason::Server, std::string{ "HTTP status code " } +
|
std::make_unique<Error>(Error::Reason::RateLimit, "HTTP status code 429",
|
||||||
std::to_string(responseCode));
|
http::parseRetryHeaders(retryAfter, xRateLimitReset));
|
||||||
|
} else if (responseCode >= 500 && responseCode < 600) {
|
||||||
|
response.error = std::make_unique<Error>(
|
||||||
|
Error::Reason::Server,
|
||||||
|
std::string{"HTTP status code "} + std::to_string(responseCode));
|
||||||
} else {
|
} else {
|
||||||
response.error =
|
response.error =
|
||||||
std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } +
|
std::make_unique<Error>(Error::Reason::Other, std::string{"HTTP status code "} +
|
||||||
std::to_string(responseCode));
|
std::to_string(responseCode));
|
||||||
}
|
}
|
||||||
} else if ([url isFileURL]) {
|
} else if ([url isFileURL]) {
|
||||||
response.data = std::make_shared<std::string>((const char *)[data bytes], [data length]);
|
response.data =
|
||||||
|
std::make_shared<std::string>((const char *)[data bytes], [data length]);
|
||||||
} else {
|
} else {
|
||||||
// This should never happen.
|
// This should never happen.
|
||||||
response.error = std::make_unique<Error>(Error::Reason::Other,
|
response.error = std::make_unique<Error>(Error::Reason::Other,
|
||||||
@@ -432,16 +436,12 @@ void HTTPFileSource::setResourceOptions(ResourceOptions options) {
|
|||||||
impl->setResourceOptions(options.clone());
|
impl->setResourceOptions(options.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceOptions HTTPFileSource::getResourceOptions() {
|
ResourceOptions HTTPFileSource::getResourceOptions() { return impl->getResourceOptions(); }
|
||||||
return impl->getResourceOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPFileSource::setClientOptions(ClientOptions options) {
|
void HTTPFileSource::setClientOptions(ClientOptions options) {
|
||||||
impl->setClientOptions(options.clone());
|
impl->setClientOptions(options.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientOptions HTTPFileSource::getClientOptions() {
|
ClientOptions HTTPFileSource::getClientOptions() { return impl->getClientOptions(); }
|
||||||
return impl->getClientOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
} // namespace mbgl
|
||||||
|
|||||||
@@ -34,19 +34,18 @@ CGImageRef CGImageCreateWithMLNPremultipliedImage(mbgl::PremultipliedImage&& src
|
|||||||
constexpr const size_t bytesPerPixel = 4;
|
constexpr const size_t bytesPerPixel = 4;
|
||||||
constexpr const size_t bitsPerPixel = bitsPerComponent * bytesPerPixel;
|
constexpr const size_t bitsPerPixel = bitsPerComponent * bytesPerPixel;
|
||||||
const size_t bytesPerRow = bytesPerPixel * src.size.width;
|
const size_t bytesPerRow = bytesPerPixel * src.size.width;
|
||||||
const auto bitmapInfo = static_cast<uint32_t>(kCGBitmapByteOrderDefault) | static_cast<uint32_t>(kCGImageAlphaPremultipliedLast);
|
const auto bitmapInfo = static_cast<uint32_t>(kCGBitmapByteOrderDefault) |
|
||||||
|
static_cast<uint32_t>(kCGImageAlphaPremultipliedLast);
|
||||||
|
|
||||||
return CGImageCreate(src.size.width, src.size.height, bitsPerComponent, bitsPerPixel,
|
return CGImageCreate(src.size.width, src.size.height, bitsPerComponent, bitsPerPixel, bytesPerRow,
|
||||||
bytesPerRow, *colorSpace,
|
*colorSpace, bitmapInfo, *provider, NULL, false, kCGRenderingIntentDefault);
|
||||||
bitmapInfo, *provider,
|
|
||||||
NULL, false, kCGRenderingIntentDefault);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mbgl::PremultipliedImage MLNPremultipliedImageFromCGImage(CGImageRef src) {
|
mbgl::PremultipliedImage MLNPremultipliedImageFromCGImage(CGImageRef src) {
|
||||||
const size_t width = CGImageGetWidth(src);
|
const size_t width = CGImageGetWidth(src);
|
||||||
const size_t height = CGImageGetHeight(src);
|
const size_t height = CGImageGetHeight(src);
|
||||||
|
|
||||||
mbgl::PremultipliedImage image({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) });
|
mbgl::PremultipliedImage image({static_cast<uint32_t>(width), static_cast<uint32_t>(height)});
|
||||||
|
|
||||||
CGColorSpaceHandle colorSpace(CGColorSpaceCreateDeviceRGB());
|
CGColorSpaceHandle colorSpace(CGColorSpaceCreateDeviceRGB());
|
||||||
if (!colorSpace) {
|
if (!colorSpace) {
|
||||||
@@ -56,10 +55,11 @@ mbgl::PremultipliedImage MLNPremultipliedImageFromCGImage(CGImageRef src) {
|
|||||||
constexpr const size_t bitsPerComponent = 8;
|
constexpr const size_t bitsPerComponent = 8;
|
||||||
constexpr const size_t bytesPerPixel = 4;
|
constexpr const size_t bytesPerPixel = 4;
|
||||||
const size_t bytesPerRow = bytesPerPixel * width;
|
const size_t bytesPerRow = bytesPerPixel * width;
|
||||||
const auto bitmapInfo = static_cast<uint32_t>(kCGBitmapByteOrderDefault) | static_cast<uint32_t>(kCGImageAlphaPremultipliedLast);
|
const auto bitmapInfo = static_cast<uint32_t>(kCGBitmapByteOrderDefault) |
|
||||||
|
static_cast<uint32_t>(kCGImageAlphaPremultipliedLast);
|
||||||
|
|
||||||
CGContextHandle context(CGBitmapContextCreate(
|
CGContextHandle context(CGBitmapContextCreate(image.data.get(), width, height, bitsPerComponent,
|
||||||
image.data.get(), width, height, bitsPerComponent, bytesPerRow, *colorSpace, bitmapInfo));
|
bytesPerRow, *colorSpace, bitmapInfo));
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw std::runtime_error("CGBitmapContextCreate failed");
|
throw std::runtime_error("CGBitmapContextCreate failed");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
#include <mbgl/text/local_glyph_rasterizer.hpp>
|
#include <mbgl/text/local_glyph_rasterizer.hpp>
|
||||||
|
#include <mbgl/util/constants.hpp>
|
||||||
#include <mbgl/util/i18n.hpp>
|
#include <mbgl/util/i18n.hpp>
|
||||||
#include <mbgl/util/logging.hpp>
|
#include <mbgl/util/logging.hpp>
|
||||||
#include <mbgl/util/platform.hpp>
|
#include <mbgl/util/platform.hpp>
|
||||||
#include <mbgl/util/constants.hpp>
|
|
||||||
|
|
||||||
#include <mbgl/interface/native_apple_interface.h>
|
#include <mbgl/interface/native_apple_interface.h>
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <CoreText/CoreText.h>
|
#import <CoreText/CoreText.h>
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
#import <ImageIO/ImageIO.h>
|
#import <ImageIO/ImageIO.h>
|
||||||
|
|
||||||
#import "CFHandle.hpp"
|
#import "CFHandle.hpp"
|
||||||
@@ -78,11 +78,13 @@ public:
|
|||||||
glyph rasterization globally. The system font is a good default value to
|
glyph rasterization globally. The system font is a good default value to
|
||||||
pass into this constructor.
|
pass into this constructor.
|
||||||
*/
|
*/
|
||||||
Impl(const std::optional<std::string> fallbackFontNames_)
|
Impl(const std::optional<std::string> fallbackFontNames_) {
|
||||||
{
|
fallbackFontNames =
|
||||||
fallbackFontNames = [[NSUserDefaults standardUserDefaults] stringArrayForKey:@"MLNIdeographicFontFamilyName"];
|
[[NSUserDefaults standardUserDefaults] stringArrayForKey:@"MLNIdeographicFontFamilyName"];
|
||||||
if (fallbackFontNames_) {
|
if (fallbackFontNames_) {
|
||||||
fallbackFontNames = [fallbackFontNames ?: @[] arrayByAddingObjectsFromArray:[@(fallbackFontNames_->c_str()) componentsSeparatedByString:@"\n"]];
|
fallbackFontNames = [fallbackFontNames ?: @[]
|
||||||
|
arrayByAddingObjectsFromArray:[@(fallbackFontNames_->c_str())
|
||||||
|
componentsSeparatedByString:@"\n"]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,14 +107,16 @@ public:
|
|||||||
|
|
||||||
@post The caller is responsible for releasing the font descriptor.
|
@post The caller is responsible for releasing the font descriptor.
|
||||||
*/
|
*/
|
||||||
CTFontDescriptorRef createFontDescriptor(const FontStack& fontStack) {
|
CTFontDescriptorRef createFontDescriptor(const FontStack &fontStack) {
|
||||||
NSMutableArray *fontNames = [NSMutableArray arrayWithCapacity:fontStack.size() + fallbackFontNames.count];
|
NSMutableArray *fontNames =
|
||||||
for (auto& fontName : fontStack) {
|
[NSMutableArray arrayWithCapacity:fontStack.size() + fallbackFontNames.count];
|
||||||
|
for (auto &fontName : fontStack) {
|
||||||
// Per the MapLibre Style Spec, the text-font property comes
|
// Per the MapLibre Style Spec, the text-font property comes
|
||||||
// with these last resort fonts by default, but they shouldn’t take
|
// with these last resort fonts by default, but they shouldn’t take
|
||||||
// precedence over any application or system fallback font that may
|
// precedence over any application or system fallback font that may
|
||||||
// be more appropriate to the current device.
|
// be more appropriate to the current device.
|
||||||
if (fontName != util::LAST_RESORT_ALPHABETIC_FONT && fontName != util::LAST_RESORT_PAN_UNICODE_FONT) {
|
if (fontName != util::LAST_RESORT_ALPHABETIC_FONT &&
|
||||||
|
fontName != util::LAST_RESORT_PAN_UNICODE_FONT) {
|
||||||
[fontNames addObject:@(fontName.c_str())];
|
[fontNames addObject:@(fontName.c_str())];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,7 +124,7 @@ public:
|
|||||||
|
|
||||||
if (!fontNames.count) {
|
if (!fontNames.count) {
|
||||||
NSDictionary *fontAttributes = @{
|
NSDictionary *fontAttributes = @{
|
||||||
(NSString *)kCTFontSizeAttribute: @(util::ONE_EM),
|
(NSString *)kCTFontSizeAttribute : @(util::ONE_EM),
|
||||||
};
|
};
|
||||||
return CTFontDescriptorCreateWithAttributes((CFDictionaryRef)fontAttributes);
|
return CTFontDescriptorCreateWithAttributes((CFDictionaryRef)fontAttributes);
|
||||||
}
|
}
|
||||||
@@ -128,17 +132,19 @@ public:
|
|||||||
// Apply the first font name to the returned font descriptor; apply the
|
// Apply the first font name to the returned font descriptor; apply the
|
||||||
// rest of the font names to the cascade list.
|
// rest of the font names to the cascade list.
|
||||||
CFStringRef mainFontName = (__bridge CFStringRef)fontNames.firstObject;
|
CFStringRef mainFontName = (__bridge CFStringRef)fontNames.firstObject;
|
||||||
CFMutableArrayRefHandle fallbackDescriptors(CFArrayCreateMutable(kCFAllocatorDefault, fontNames.count, &kCFTypeArrayCallBacks));
|
CFMutableArrayRefHandle fallbackDescriptors(
|
||||||
|
CFArrayCreateMutable(kCFAllocatorDefault, fontNames.count, &kCFTypeArrayCallBacks));
|
||||||
for (NSString *name in [fontNames subarrayWithRange:NSMakeRange(1, fontNames.count - 1)]) {
|
for (NSString *name in [fontNames subarrayWithRange:NSMakeRange(1, fontNames.count - 1)]) {
|
||||||
NSDictionary *fontAttributes = @{
|
NSDictionary *fontAttributes = @{
|
||||||
(NSString *)kCTFontSizeAttribute: @(util::ONE_EM),
|
(NSString *)kCTFontSizeAttribute : @(util::ONE_EM),
|
||||||
// This attribute is technically supposed to be a font’s
|
// This attribute is technically supposed to be a font’s
|
||||||
// PostScript name, but Core Text will fall back to matching
|
// PostScript name, but Core Text will fall back to matching
|
||||||
// font display names and font family names.
|
// font display names and font family names.
|
||||||
(NSString *)kCTFontNameAttribute: name,
|
(NSString *)kCTFontNameAttribute : name,
|
||||||
};
|
};
|
||||||
|
|
||||||
CTFontDescriptorRefHandle descriptor(CTFontDescriptorCreateWithAttributes((CFDictionaryRef)fontAttributes));
|
CTFontDescriptorRefHandle descriptor(
|
||||||
|
CTFontDescriptorCreateWithAttributes((CFDictionaryRef)fontAttributes));
|
||||||
CFArrayAppendValue(*fallbackDescriptors, *descriptor);
|
CFArrayAppendValue(*fallbackDescriptors, *descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,15 +154,14 @@ public:
|
|||||||
kCTFontCascadeListAttribute,
|
kCTFontCascadeListAttribute,
|
||||||
};
|
};
|
||||||
CFTypeRef values[] = {
|
CFTypeRef values[] = {
|
||||||
(__bridge CFNumberRef)@(util::ONE_EM),
|
(__bridge CFNumberRef) @(util::ONE_EM),
|
||||||
mainFontName,
|
mainFontName,
|
||||||
*fallbackDescriptors,
|
*fallbackDescriptors,
|
||||||
};
|
};
|
||||||
|
|
||||||
CFDictionaryRefHandle attributes(
|
CFDictionaryRefHandle attributes(
|
||||||
CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys,
|
CFDictionaryCreate(kCFAllocatorDefault, (const void **)&keys, (const void **)&values,
|
||||||
(const void**)&values, sizeof(keys) / sizeof(keys[0]),
|
sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks,
|
||||||
&kCFTypeDictionaryKeyCallBacks,
|
|
||||||
&kCFTypeDictionaryValueCallBacks));
|
&kCFTypeDictionaryValueCallBacks));
|
||||||
return CTFontDescriptorCreateWithAttributes(*attributes);
|
return CTFontDescriptorCreateWithAttributes(*attributes);
|
||||||
}
|
}
|
||||||
@@ -165,12 +170,10 @@ private:
|
|||||||
NSArray<NSString *> *fallbackFontNames;
|
NSArray<NSString *> *fallbackFontNames;
|
||||||
};
|
};
|
||||||
|
|
||||||
LocalGlyphRasterizer::LocalGlyphRasterizer(const std::optional<std::string>& fontFamily)
|
LocalGlyphRasterizer::LocalGlyphRasterizer(const std::optional<std::string> &fontFamily)
|
||||||
: impl(std::make_unique<Impl>(fontFamily))
|
: impl(std::make_unique<Impl>(fontFamily)) {}
|
||||||
{}
|
|
||||||
|
|
||||||
LocalGlyphRasterizer::~LocalGlyphRasterizer()
|
LocalGlyphRasterizer::~LocalGlyphRasterizer() {}
|
||||||
{}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns whether the rasterizer can rasterize a glyph for the given codepoint.
|
Returns whether the rasterizer can rasterize a glyph for the given codepoint.
|
||||||
@@ -178,7 +181,7 @@ LocalGlyphRasterizer::~LocalGlyphRasterizer()
|
|||||||
@param glyphID A font-agnostic Unicode codepoint, not a glyph index.
|
@param glyphID A font-agnostic Unicode codepoint, not a glyph index.
|
||||||
@returns Whether a glyph for the codepoint can be rasterized.
|
@returns Whether a glyph for the codepoint can be rasterized.
|
||||||
*/
|
*/
|
||||||
bool LocalGlyphRasterizer::canRasterizeGlyph(const FontStack&, GlyphID glyphID) {
|
bool LocalGlyphRasterizer::canRasterizeGlyph(const FontStack &, GlyphID glyphID) {
|
||||||
#if MBGL_DARWIN_NO_REMOTE_FONTS
|
#if MBGL_DARWIN_NO_REMOTE_FONTS
|
||||||
return impl->isEnabled();
|
return impl->isEnabled();
|
||||||
#else
|
#else
|
||||||
@@ -197,36 +200,39 @@ bool LocalGlyphRasterizer::canRasterizeGlyph(const FontStack&, GlyphID glyphID)
|
|||||||
@param isBold use kCTFontBoldTrait if it is true.
|
@param isBold use kCTFontBoldTrait if it is true.
|
||||||
@returns An image containing the glyph.
|
@returns An image containing the glyph.
|
||||||
*/
|
*/
|
||||||
PremultipliedImage drawGlyphBitmap(GlyphID glyphID, CTFontRef font, GlyphMetrics& metrics, BOOL isBold) {
|
PremultipliedImage drawGlyphBitmap(GlyphID glyphID, CTFontRef font, GlyphMetrics &metrics,
|
||||||
CFStringRefHandle string(CFStringCreateWithCharacters(NULL, reinterpret_cast<UniChar*>(&glyphID), 1));
|
BOOL isBold) {
|
||||||
|
CFStringRefHandle string(
|
||||||
|
CFStringCreateWithCharacters(NULL, reinterpret_cast<UniChar *>(&glyphID), 1));
|
||||||
if (!string) {
|
if (!string) {
|
||||||
throw std::runtime_error("Unable to create string from codepoint");
|
throw std::runtime_error("Unable to create string from codepoint");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a bold variant of the font
|
// Create a bold variant of the font
|
||||||
CTFontRefHandle boldFont(CTFontCreateCopyWithSymbolicTraits(font, 0.0, NULL, kCTFontBoldTrait, kCTFontBoldTrait));
|
CTFontRefHandle boldFont(
|
||||||
|
CTFontCreateCopyWithSymbolicTraits(font, 0.0, NULL, kCTFontBoldTrait, kCTFontBoldTrait));
|
||||||
if (!boldFont) {
|
if (!boldFont) {
|
||||||
CFStringRefHandle familyNameHandle(CTFontCopyFamilyName(font));
|
CFStringRefHandle familyNameHandle(CTFontCopyFamilyName(font));
|
||||||
NSString* familyName = (__bridge NSString *)(*familyNameHandle);
|
NSString *familyName = (__bridge NSString *)(*familyNameHandle);
|
||||||
std::string stdFamilyName(familyName.UTF8String);
|
std::string stdFamilyName(familyName.UTF8String);
|
||||||
Log::Error(Event::General, "Unable to create bold font for " + stdFamilyName);
|
Log::Error(Event::General, "Unable to create bold font for " + stdFamilyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
CTFontRef drawFont = isBold && boldFont ? *boldFont : font;
|
CTFontRef drawFont = isBold && boldFont ? *boldFont : font;
|
||||||
|
|
||||||
CFStringRef keys[] = { kCTFontAttributeName };
|
CFStringRef keys[] = {kCTFontAttributeName};
|
||||||
CFTypeRef values[] = { drawFont };
|
CFTypeRef values[] = {drawFont};
|
||||||
|
|
||||||
CFDictionaryRefHandle attributes(
|
CFDictionaryRefHandle attributes(
|
||||||
CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys,
|
CFDictionaryCreate(kCFAllocatorDefault, (const void **)&keys, (const void **)&values,
|
||||||
(const void**)&values, sizeof(keys) / sizeof(keys[0]),
|
sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks,
|
||||||
&kCFTypeDictionaryKeyCallBacks,
|
|
||||||
&kCFTypeDictionaryValueCallBacks));
|
&kCFTypeDictionaryValueCallBacks));
|
||||||
if (!attributes) {
|
if (!attributes) {
|
||||||
throw std::runtime_error("Unable to create attributed string attributes dictionary");
|
throw std::runtime_error("Unable to create attributed string attributes dictionary");
|
||||||
}
|
}
|
||||||
|
|
||||||
CFAttributedStringRefHandle attrString(CFAttributedStringCreate(kCFAllocatorDefault, *string, *attributes));
|
CFAttributedStringRefHandle attrString(
|
||||||
|
CFAttributedStringCreate(kCFAllocatorDefault, *string, *attributes));
|
||||||
if (!attrString) {
|
if (!attrString) {
|
||||||
throw std::runtime_error("Unable to create attributed string");
|
throw std::runtime_error("Unable to create attributed string");
|
||||||
}
|
}
|
||||||
@@ -249,15 +255,11 @@ PremultipliedImage drawGlyphBitmap(GlyphID glyphID, CTFontRef font, GlyphMetrics
|
|||||||
constexpr const size_t bitsPerComponent = 8;
|
constexpr const size_t bitsPerComponent = 8;
|
||||||
constexpr const size_t bytesPerPixel = 4;
|
constexpr const size_t bytesPerPixel = 4;
|
||||||
const size_t bytesPerRow = bytesPerPixel * size.width;
|
const size_t bytesPerRow = bytesPerPixel * size.width;
|
||||||
const auto bitmapInfo = static_cast<uint32_t>(kCGBitmapByteOrderDefault) | static_cast<uint32_t>(kCGImageAlphaPremultipliedLast);
|
const auto bitmapInfo = static_cast<uint32_t>(kCGBitmapByteOrderDefault) |
|
||||||
|
static_cast<uint32_t>(kCGImageAlphaPremultipliedLast);
|
||||||
|
|
||||||
CGContextHandle context(CGBitmapContextCreate(
|
CGContextHandle context(CGBitmapContextCreate(rgbaBitmap.data.get(), size.width, size.height,
|
||||||
rgbaBitmap.data.get(),
|
bitsPerComponent, bytesPerRow, *colorSpace,
|
||||||
size.width,
|
|
||||||
size.height,
|
|
||||||
bitsPerComponent,
|
|
||||||
bytesPerRow,
|
|
||||||
*colorSpace,
|
|
||||||
bitmapInfo));
|
bitmapInfo));
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw std::runtime_error("CGBitmapContextCreate failed");
|
throw std::runtime_error("CGBitmapContextCreate failed");
|
||||||
@@ -294,7 +296,7 @@ PremultipliedImage drawGlyphBitmap(GlyphID glyphID, CTFontRef font, GlyphMetrics
|
|||||||
@returns A glyph representation of the given codepoint with its bitmap data and
|
@returns A glyph representation of the given codepoint with its bitmap data and
|
||||||
metrics set.
|
metrics set.
|
||||||
*/
|
*/
|
||||||
Glyph LocalGlyphRasterizer::rasterizeGlyph(const FontStack& fontStack, GlyphID glyphID) {
|
Glyph LocalGlyphRasterizer::rasterizeGlyph(const FontStack &fontStack, GlyphID glyphID) {
|
||||||
Glyph manufacturedGlyph;
|
Glyph manufacturedGlyph;
|
||||||
CTFontDescriptorRefHandle descriptor(impl->createFontDescriptor(fontStack));
|
CTFontDescriptorRefHandle descriptor(impl->createFontDescriptor(fontStack));
|
||||||
CTFontRefHandle font(CTFontCreateWithFontDescriptor(*descriptor, 0.0, NULL));
|
CTFontRefHandle font(CTFontCreateWithFontDescriptor(*descriptor, 0.0, NULL));
|
||||||
@@ -307,12 +309,14 @@ Glyph LocalGlyphRasterizer::rasterizeGlyph(const FontStack& fontStack, GlyphID g
|
|||||||
// Only check the first font name to detect if the user prefers using bold
|
// Only check the first font name to detect if the user prefers using bold
|
||||||
if (!fontStack.empty()) {
|
if (!fontStack.empty()) {
|
||||||
std::string lowercaseFont = platform::lowercase(fontStack.front());
|
std::string lowercaseFont = platform::lowercase(fontStack.front());
|
||||||
if (lowercaseFont.find("bold") != std::string::npos && lowercaseFont.find("semibold") == std::string::npos) {
|
if (lowercaseFont.find("bold") != std::string::npos &&
|
||||||
|
lowercaseFont.find("semibold") == std::string::npos) {
|
||||||
isBold = YES;
|
isBold = YES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PremultipliedImage rgbaBitmap = drawGlyphBitmap(glyphID, *font, manufacturedGlyph.metrics, isBold);
|
PremultipliedImage rgbaBitmap =
|
||||||
|
drawGlyphBitmap(glyphID, *font, manufacturedGlyph.metrics, isBold);
|
||||||
|
|
||||||
Size size(manufacturedGlyph.metrics.width, manufacturedGlyph.metrics.height);
|
Size size(manufacturedGlyph.metrics.width, manufacturedGlyph.metrics.height);
|
||||||
// Copy alpha values from RGBA bitmap into the AlphaImage output
|
// Copy alpha values from RGBA bitmap into the AlphaImage output
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
#include <mbgl/util/logging.hpp>
|
|
||||||
#include <mbgl/util/enum.hpp>
|
#include <mbgl/util/enum.hpp>
|
||||||
|
#include <mbgl/util/logging.hpp>
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
namespace mbgl {
|
namespace mbgl {
|
||||||
|
|
||||||
void Log::platformRecord(EventSeverity severity, const std::string &msg) {
|
void Log::platformRecord(EventSeverity severity, const std::string &msg) {
|
||||||
NSString *message =
|
NSString *message = [[NSString alloc] initWithBytes:msg.data()
|
||||||
[[NSString alloc] initWithBytes:msg.data() length:msg.size() encoding:NSUTF8StringEncoding];
|
length:msg.size()
|
||||||
|
encoding:NSUTF8StringEncoding];
|
||||||
NSLog(@"[%s] %@", Enum<EventSeverity>::toString(severity), message);
|
NSLog(@"[%s] %@", Enum<EventSeverity>::toString(severity), message);
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace mbgl
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#include <mbgl/platform/thread.hpp>
|
||||||
#include <mbgl/util/logging.hpp>
|
#include <mbgl/util/logging.hpp>
|
||||||
#include <mbgl/util/platform.hpp>
|
#include <mbgl/util/platform.hpp>
|
||||||
#include <mbgl/platform/thread.hpp>
|
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
@@ -42,11 +42,9 @@ void setCurrentThreadPriority(double priority) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void attachThread() {
|
void attachThread() {}
|
||||||
}
|
|
||||||
|
|
||||||
void detachThread() {
|
void detachThread() {}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
} // namespace platform
|
||||||
}
|
} // namespace mbgl
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ namespace platform {
|
|||||||
|
|
||||||
std::string formatNumber(double number, const std::string& localeId, const std::string& currency,
|
std::string formatNumber(double number, const std::string& localeId, const std::string& currency,
|
||||||
uint8_t minFractionDigits, uint8_t maxFractionDigits) {
|
uint8_t minFractionDigits, uint8_t maxFractionDigits) {
|
||||||
|
|
||||||
// Create a new formatter each time to avoid state pollution between calls
|
// Create a new formatter each time to avoid state pollution between calls
|
||||||
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
|
NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
|
||||||
|
|
||||||
numberFormatter.locale = !localeId.empty() ? [NSLocale localeWithLocaleIdentifier:@(localeId.c_str())] : nil;
|
numberFormatter.locale =
|
||||||
|
!localeId.empty() ? [NSLocale localeWithLocaleIdentifier:@(localeId.c_str())] : nil;
|
||||||
numberFormatter.currencyCode = !currency.empty() ? @(currency.c_str()) : nil;
|
numberFormatter.currencyCode = !currency.empty() ? @(currency.c_str()) : nil;
|
||||||
|
|
||||||
if (currency.empty()) {
|
if (currency.empty()) {
|
||||||
@@ -21,7 +21,7 @@ std::string formatNumber(double number, const std::string& localeId, const std::
|
|||||||
} else {
|
} else {
|
||||||
numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
|
numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
|
||||||
}
|
}
|
||||||
NSString *formatted = [numberFormatter stringFromNumber:@(number)];
|
NSString* formatted = [numberFormatter stringFromNumber:@(number)];
|
||||||
std::string result = std::string([formatted UTF8String]);
|
std::string result = std::string([formatted UTF8String]);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ std::string uppercase(const std::string &string) {
|
|||||||
encoding:NSUTF8StringEncoding
|
encoding:NSUTF8StringEncoding
|
||||||
freeWhenDone:NO];
|
freeWhenDone:NO];
|
||||||
NSString *uppercase = [original uppercaseString];
|
NSString *uppercase = [original uppercaseString];
|
||||||
std::string result{[uppercase cStringUsingEncoding : NSUTF8StringEncoding],
|
std::string result{[uppercase cStringUsingEncoding:NSUTF8StringEncoding],
|
||||||
[uppercase lengthOfBytesUsingEncoding:NSUTF8StringEncoding]};
|
[uppercase lengthOfBytesUsingEncoding:NSUTF8StringEncoding]};
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ std::string lowercase(const std::string &string) {
|
|||||||
encoding:NSUTF8StringEncoding
|
encoding:NSUTF8StringEncoding
|
||||||
freeWhenDone:NO];
|
freeWhenDone:NO];
|
||||||
NSString *lowercase = [original lowercaseString];
|
NSString *lowercase = [original lowercaseString];
|
||||||
std::string result{[lowercase cStringUsingEncoding : NSUTF8StringEncoding],
|
std::string result{[lowercase cStringUsingEncoding:NSUTF8StringEncoding],
|
||||||
[lowercase lengthOfBytesUsingEncoding:NSUTF8StringEncoding]};
|
[lowercase lengthOfBytesUsingEncoding:NSUTF8StringEncoding]};
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
_actionJournalOptionsInternal = std::make_unique<mbgl::util::ActionJournalOptions>();
|
_actionJournalOptionsInternal = std::make_unique<mbgl::util::ActionJournalOptions>();
|
||||||
|
|
||||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||||
_actionJournalOptionsInternal->withPath([paths.firstObject stringByAppendingPathComponent:@""].UTF8String);
|
_actionJournalOptionsInternal->withPath(
|
||||||
|
[paths.firstObject stringByAppendingPathComponent:@""].UTF8String);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -27,11 +28,11 @@
|
|||||||
return _actionJournalOptionsInternal->enabled();
|
return _actionJournalOptionsInternal->enabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setPath:(NSString*)value {
|
- (void)setPath:(NSString *)value {
|
||||||
_actionJournalOptionsInternal->withPath(value.UTF8String);
|
_actionJournalOptionsInternal->withPath(value.UTF8String);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString*)path {
|
- (NSString *)path {
|
||||||
return [NSString stringWithUTF8String:_actionJournalOptionsInternal->path().c_str()];
|
return [NSString stringWithUTF8String:_actionJournalOptionsInternal->path().c_str()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,37 @@
|
|||||||
#import "MLNAttributionInfo_Private.h"
|
#import "MLNAttributionInfo_Private.h"
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#else
|
#else
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#import "MLNSettings.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
#import "MLNMapCamera.h"
|
#import "MLNMapCamera.h"
|
||||||
|
#import "MLNSettings.h"
|
||||||
#import "NSArray+MLNAdditions.h"
|
#import "NSArray+MLNAdditions.h"
|
||||||
#import "NSBundle+MLNAdditions.h"
|
#import "NSBundle+MLNAdditions.h"
|
||||||
#import "NSString+MLNAdditions.h"
|
#import "NSString+MLNAdditions.h"
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@implementation MLNAttributionInfo
|
@implementation MLNAttributionInfo
|
||||||
|
|
||||||
+ (NSArray<MLNAttributionInfo *> *)attributionInfosFromHTMLString:(nullable NSString *)htmlString fontSize:(CGFloat)fontSize linkColor:(nullable MLNColor *)linkColor {
|
+ (NSArray<MLNAttributionInfo *> *)attributionInfosFromHTMLString:(nullable NSString *)htmlString
|
||||||
|
fontSize:(CGFloat)fontSize
|
||||||
|
linkColor:(nullable MLNColor *)linkColor {
|
||||||
if (!htmlString) {
|
if (!htmlString) {
|
||||||
return @[];
|
return @[];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSDictionary *options = @{
|
NSDictionary *options = @{
|
||||||
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
|
NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType,
|
||||||
NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding),
|
NSCharacterEncodingDocumentAttribute : @(NSUTF8StringEncoding),
|
||||||
};
|
};
|
||||||
// Apply a bogus, easily detectable style rule to any feedback link, since
|
// Apply a bogus, easily detectable style rule to any feedback link, since
|
||||||
// NSAttributedString doesn’t preserve the class attribute.
|
// NSAttributedString doesn’t preserve the class attribute.
|
||||||
NSMutableString *css = [NSMutableString stringWithString:
|
NSMutableString *css = [NSMutableString
|
||||||
@"html { font-family: -apple-system, -apple-system-font, sans-serif; }"
|
stringWithString:@"html { font-family: -apple-system, -apple-system-font, sans-serif; }"
|
||||||
@".mapbox-improve-map { -webkit-text-stroke-width: 1000px; }"];
|
@".mapbox-improve-map { -webkit-text-stroke-width: 1000px; }"];
|
||||||
if (fontSize) {
|
if (fontSize) {
|
||||||
NSString *sizeRule = [NSString stringWithFormat:@"font-size: %.1fpx;", fontSize];
|
NSString *sizeRule = [NSString stringWithFormat:@"font-size: %.1fpx;", fontSize];
|
||||||
@@ -58,17 +60,18 @@
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
[linkColor getRed:&red green:&green blue:&blue alpha:&alpha];
|
[linkColor getRed:&red green:&green blue:&blue alpha:&alpha];
|
||||||
[css appendFormat:
|
[css appendFormat:@"a:link { color: rgba(%f%%, %f%%, %f%%, %f); }", red * 100, green * 100,
|
||||||
@"a:link { color: rgba(%f%%, %f%%, %f%%, %f); }",
|
blue * 100, alpha];
|
||||||
red * 100, green * 100, blue * 100, alpha];
|
|
||||||
}
|
}
|
||||||
NSString *styledHTML = [NSString stringWithFormat:@"<style type='text/css'>%@</style>%@", css, htmlString];
|
NSString *styledHTML =
|
||||||
|
[NSString stringWithFormat:@"<style type='text/css'>%@</style>%@", css, htmlString];
|
||||||
NSData *htmlData = [styledHTML dataUsingEncoding:NSUTF8StringEncoding];
|
NSData *htmlData = [styledHTML dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
__block NSMutableAttributedString *attributedString;
|
__block NSMutableAttributedString *attributedString;
|
||||||
dispatch_block_t initialization = ^{
|
dispatch_block_t initialization = ^{
|
||||||
// This initializer should be called from a global or main queue. https://developer.apple.com/documentation/foundation/nsattributedstring/1524613-initwithdata
|
// This initializer should be called from a global or main queue.
|
||||||
|
// https://developer.apple.com/documentation/foundation/nsattributedstring/1524613-initwithdata
|
||||||
attributedString = [[NSMutableAttributedString alloc] initWithData:htmlData
|
attributedString = [[NSMutableAttributedString alloc] initWithData:htmlData
|
||||||
options:options
|
options:options
|
||||||
documentAttributes:nil
|
documentAttributes:nil
|
||||||
@@ -81,18 +84,20 @@
|
|||||||
initialization();
|
initialization();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithHTML:htmlData
|
NSMutableAttributedString *attributedString =
|
||||||
|
[[NSMutableAttributedString alloc] initWithHTML:htmlData
|
||||||
options:options
|
options:options
|
||||||
documentAttributes:nil];
|
documentAttributes:nil];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NSMutableArray *infos = [NSMutableArray array];
|
NSMutableArray *infos = [NSMutableArray array];
|
||||||
[attributedString enumerateAttribute:NSLinkAttributeName
|
[attributedString
|
||||||
|
enumerateAttribute:NSLinkAttributeName
|
||||||
inRange:attributedString.mgl_wholeRange
|
inRange:attributedString.mgl_wholeRange
|
||||||
options:0
|
options:0
|
||||||
usingBlock:
|
usingBlock:^(id _Nullable value, NSRange range, BOOL *_Nonnull stop) {
|
||||||
^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
|
MLNCAssert(!value || [value isKindOfClass:[NSURL class]],
|
||||||
MLNCAssert(!value || [value isKindOfClass:[NSURL class]], @"If present, URL attribute must be an NSURL.");
|
@"If present, URL attribute must be an NSURL.");
|
||||||
|
|
||||||
// Detect feedback links by the bogus style rule applied above.
|
// Detect feedback links by the bogus style rule applied above.
|
||||||
NSNumber *strokeWidth = [attributedString attribute:NSStrokeWidthAttributeName
|
NSNumber *strokeWidth = [attributedString attribute:NSStrokeWidthAttributeName
|
||||||
@@ -104,8 +109,10 @@
|
|||||||
[attributedString removeAttribute:NSStrokeWidthAttributeName range:range];
|
[attributedString removeAttribute:NSStrokeWidthAttributeName range:range];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up strings by stripping punctuation and whitespace (often present for the web).
|
// Clean up strings by stripping punctuation and whitespace (often present for the
|
||||||
NSMutableCharacterSet *charset = [NSMutableCharacterSet whitespaceAndNewlineCharacterSet];
|
// web).
|
||||||
|
NSMutableCharacterSet *charset =
|
||||||
|
[NSMutableCharacterSet whitespaceAndNewlineCharacterSet];
|
||||||
[charset formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
|
[charset formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
|
||||||
NSAttributedString *title = [[attributedString attributedSubstringFromRange:range]
|
NSAttributedString *title = [[attributedString attributedSubstringFromRange:range]
|
||||||
mgl_attributedStringByTrimmingCharactersInSet:charset];
|
mgl_attributedStringByTrimmingCharactersInSet:charset];
|
||||||
@@ -118,16 +125,19 @@
|
|||||||
// Remove the link, because it forces the text to be blue on macOS 10.12
|
// Remove the link, because it forces the text to be blue on macOS 10.12
|
||||||
// and above.
|
// and above.
|
||||||
NSMutableAttributedString *unlinkedTitle = [title mutableCopy];
|
NSMutableAttributedString *unlinkedTitle = [title mutableCopy];
|
||||||
[unlinkedTitle removeAttribute:NSLinkAttributeName range:unlinkedTitle.mgl_wholeRange];
|
[unlinkedTitle removeAttribute:NSLinkAttributeName
|
||||||
|
range:unlinkedTitle.mgl_wholeRange];
|
||||||
|
|
||||||
MLNAttributionInfo *info = [[MLNAttributionInfo alloc] initWithTitle:unlinkedTitle URL:value];
|
MLNAttributionInfo *info = [[MLNAttributionInfo alloc] initWithTitle:unlinkedTitle
|
||||||
|
URL:value];
|
||||||
info.feedbackLink = isFeedbackLink;
|
info.feedbackLink = isFeedbackLink;
|
||||||
[infos addObject:info];
|
[infos addObject:info];
|
||||||
}];
|
}];
|
||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSAttributedString *)attributedStringForAttributionInfos:(NSArray<MLNAttributionInfo *> *)attributionInfos {
|
+ (NSAttributedString *)attributedStringForAttributionInfos:
|
||||||
|
(NSArray<MLNAttributionInfo *> *)attributionInfos {
|
||||||
NSMutableArray *titles = [NSMutableArray arrayWithCapacity:attributionInfos.count];
|
NSMutableArray *titles = [NSMutableArray arrayWithCapacity:attributionInfos.count];
|
||||||
for (MLNAttributionInfo *info in attributionInfos) {
|
for (MLNAttributionInfo *info in attributionInfos) {
|
||||||
NSMutableAttributedString *title = info.title.mutableCopy;
|
NSMutableAttributedString *title = info.title.mutableCopy;
|
||||||
@@ -147,27 +157,29 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)copyWithZone:(nullable NSZone *)zone
|
- (id)copyWithZone:(nullable NSZone *)zone {
|
||||||
{
|
MLNAttributionInfo *info = [[[self class] allocWithZone:zone] initWithTitle:_title URL:_URL];
|
||||||
MLNAttributionInfo *info = [[[self class] allocWithZone:zone] initWithTitle:_title
|
|
||||||
URL:_URL];
|
|
||||||
info.feedbackLink = _feedbackLink;
|
info.feedbackLink = _feedbackLink;
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSAttributedString *)titleWithStyle:(MLNAttributionInfoStyle)style
|
- (NSAttributedString *)titleWithStyle:(MLNAttributionInfoStyle)style {
|
||||||
{
|
NSString *openStreetMap =
|
||||||
NSString *openStreetMap = NSLocalizedStringWithDefaultValue(@"OSM_FULL_NAME", @"Foundation", nil, @"OpenStreetMap", @"OpenStreetMap full name attribution");
|
NSLocalizedStringWithDefaultValue(@"OSM_FULL_NAME", @"Foundation", nil, @"OpenStreetMap",
|
||||||
NSString *OSM = NSLocalizedStringWithDefaultValue(@"OSM_SHORT_NAME", @"Foundation", nil, @"OSM", @"OpenStreetMap short name attribution");
|
@"OpenStreetMap full name attribution");
|
||||||
|
NSString *OSM = NSLocalizedStringWithDefaultValue(@"OSM_SHORT_NAME", @"Foundation", nil, @"OSM",
|
||||||
|
@"OpenStreetMap short name attribution");
|
||||||
|
|
||||||
NSMutableAttributedString *title = [[NSMutableAttributedString alloc] initWithAttributedString:self.title];
|
NSMutableAttributedString *title =
|
||||||
|
[[NSMutableAttributedString alloc] initWithAttributedString:self.title];
|
||||||
[title removeAttribute:NSUnderlineStyleAttributeName range:NSMakeRange(0, [title.string length])];
|
[title removeAttribute:NSUnderlineStyleAttributeName range:NSMakeRange(0, [title.string length])];
|
||||||
|
|
||||||
BOOL isAbbreviated = (style == MLNAttributionInfoStyleShort);
|
BOOL isAbbreviated = (style == MLNAttributionInfoStyleShort);
|
||||||
|
|
||||||
if ([title.string rangeOfString:@"OpenStreetMap"].location != NSNotFound) {
|
if ([title.string rangeOfString:@"OpenStreetMap"].location != NSNotFound) {
|
||||||
[title.mutableString replaceOccurrencesOfString:@"OpenStreetMap" withString:isAbbreviated ? OSM : openStreetMap
|
[title.mutableString replaceOccurrencesOfString:@"OpenStreetMap"
|
||||||
|
withString:isAbbreviated ? OSM : openStreetMap
|
||||||
options:NSCaseInsensitiveSearch
|
options:NSCaseInsensitiveSearch
|
||||||
range:NSMakeRange(0, [title.mutableString length])];
|
range:NSMakeRange(0, [title.mutableString length])];
|
||||||
}
|
}
|
||||||
@@ -176,7 +188,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)object {
|
- (BOOL)isEqual:(id)object {
|
||||||
return [object isKindOfClass:[self class]] && [[object title] isEqual:self.title] && [[object URL] isEqual:self.URL];
|
return [object isKindOfClass:[self class]] && [[object title] isEqual:self.title] &&
|
||||||
|
[[object URL] isEqual:self.URL];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)hash {
|
- (NSUInteger)hash {
|
||||||
@@ -210,7 +223,8 @@
|
|||||||
- (void)growArrayByAddingAttributionInfo:(MLNAttributionInfo *)info {
|
- (void)growArrayByAddingAttributionInfo:(MLNAttributionInfo *)info {
|
||||||
__block BOOL didInsertInfo = NO;
|
__block BOOL didInsertInfo = NO;
|
||||||
__block BOOL shouldAddInfo = YES;
|
__block BOOL shouldAddInfo = YES;
|
||||||
[self enumerateObjectsUsingBlock:^(MLNAttributionInfo * _Nonnull existingInfo, NSUInteger idx, BOOL * _Nonnull stop) {
|
[self enumerateObjectsUsingBlock:^(MLNAttributionInfo *_Nonnull existingInfo, NSUInteger idx,
|
||||||
|
BOOL *_Nonnull stop) {
|
||||||
switch ([info subsetCompare:existingInfo]) {
|
switch ([info subsetCompare:existingInfo]) {
|
||||||
case NSOrderedDescending:
|
case NSOrderedDescending:
|
||||||
// The existing info object is a subset of the one we’re adding.
|
// The existing info object is a subset of the one we’re adding.
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#import "MLNComputedShapeSource_Private.h"
|
#import "MLNComputedShapeSource_Private.h"
|
||||||
|
|
||||||
#import "MLNMapView_Private.h"
|
|
||||||
#import "MLNSource_Private.h"
|
|
||||||
#import "MLNShape_Private.h"
|
|
||||||
#import "MLNGeometry_Private.h"
|
#import "MLNGeometry_Private.h"
|
||||||
|
#import "MLNMapView_Private.h"
|
||||||
#import "MLNShapeCollection.h"
|
#import "MLNShapeCollection.h"
|
||||||
|
#import "MLNShape_Private.h"
|
||||||
|
#import "MLNSource_Private.h"
|
||||||
|
|
||||||
#include <mbgl/map/map.hpp>
|
#include <mbgl/map/map.hpp>
|
||||||
#include <mbgl/style/sources/custom_geometry_source.hpp>
|
#include <mbgl/style/sources/custom_geometry_source.hpp>
|
||||||
@@ -13,10 +13,13 @@
|
|||||||
|
|
||||||
const MLNExceptionName MLNInvalidDatasourceException = @"MLNInvalidDatasourceException";
|
const MLNExceptionName MLNInvalidDatasourceException = @"MLNInvalidDatasourceException";
|
||||||
|
|
||||||
const MLNShapeSourceOption MLNShapeSourceOptionWrapsCoordinates = @"MLNShapeSourceOptionWrapsCoordinates";
|
const MLNShapeSourceOption MLNShapeSourceOptionWrapsCoordinates =
|
||||||
const MLNShapeSourceOption MLNShapeSourceOptionClipsCoordinates = @"MLNShapeSourceOptionClipsCoordinates";
|
@"MLNShapeSourceOptionWrapsCoordinates";
|
||||||
|
const MLNShapeSourceOption MLNShapeSourceOptionClipsCoordinates =
|
||||||
|
@"MLNShapeSourceOptionClipsCoordinates";
|
||||||
|
|
||||||
mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NSDictionary<MLNShapeSourceOption, id> *options) {
|
mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(
|
||||||
|
NSDictionary<MLNShapeSourceOption, id> *options) {
|
||||||
mbgl::style::CustomGeometrySource::Options sourceOptions;
|
mbgl::style::CustomGeometrySource::Options sourceOptions;
|
||||||
|
|
||||||
if (NSNumber *value = options[MLNShapeSourceOptionMinimumZoomLevel]) {
|
if (NSNumber *value = options[MLNShapeSourceOptionMinimumZoomLevel]) {
|
||||||
@@ -90,13 +93,15 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
|
|||||||
@property (nonatomic, weak, nullable) id<MLNComputedShapeSourceDataSource> dataSource;
|
@property (nonatomic, weak, nullable) id<MLNComputedShapeSourceDataSource> dataSource;
|
||||||
@property (nonatomic, nullable) mbgl::style::CustomGeometrySource *rawSource;
|
@property (nonatomic, nullable) mbgl::style::CustomGeometrySource *rawSource;
|
||||||
|
|
||||||
- (instancetype)initForSource:(MLNComputedShapeSource*)source tile:(const mbgl::CanonicalTileID&)tileId;
|
- (instancetype)initForSource:(MLNComputedShapeSource *)source
|
||||||
|
tile:(const mbgl::CanonicalTileID &)tileId;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MLNComputedShapeSourceFetchOperation
|
@implementation MLNComputedShapeSourceFetchOperation
|
||||||
|
|
||||||
- (instancetype)initForSource:(MLNComputedShapeSource*)source tile:(const mbgl::CanonicalTileID&)tileID {
|
- (instancetype)initForSource:(MLNComputedShapeSource *)source
|
||||||
|
tile:(const mbgl::CanonicalTileID &)tileID {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
_z = tileID.z;
|
_z = tileID.z;
|
||||||
_x = tileID.x;
|
_x = tileID.x;
|
||||||
@@ -104,7 +109,8 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
|
|||||||
_dataSourceImplementsFeaturesForTile = source.dataSourceImplementsFeaturesForTile;
|
_dataSourceImplementsFeaturesForTile = source.dataSourceImplementsFeaturesForTile;
|
||||||
_dataSourceImplementsFeaturesForBounds = source.dataSourceImplementsFeaturesForBounds;
|
_dataSourceImplementsFeaturesForBounds = source.dataSourceImplementsFeaturesForBounds;
|
||||||
_dataSource = source.dataSource;
|
_dataSource = source.dataSource;
|
||||||
mbgl::style::CustomGeometrySource *rawSource = static_cast<mbgl::style::CustomGeometrySource *>(source.rawSource);
|
mbgl::style::CustomGeometrySource *rawSource =
|
||||||
|
static_cast<mbgl::style::CustomGeometrySource *>(source.rawSource);
|
||||||
_rawSource = rawSource;
|
_rawSource = rawSource;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -114,24 +120,23 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSArray<MLNShape <MLNFeature> *> *data;
|
NSArray<MLNShape<MLNFeature> *> *data;
|
||||||
if(!self.dataSource) {
|
if (!self.dataSource) {
|
||||||
data = nil;
|
data = nil;
|
||||||
} else if(self.dataSourceImplementsFeaturesForTile) {
|
} else if (self.dataSourceImplementsFeaturesForTile) {
|
||||||
data = [self.dataSource featuresInTileAtX:self.x
|
data = [self.dataSource featuresInTileAtX:self.x y:self.y zoomLevel:self.z];
|
||||||
y:self.y
|
|
||||||
zoomLevel:self.z];
|
|
||||||
} else {
|
} else {
|
||||||
mbgl::CanonicalTileID tileID = mbgl::CanonicalTileID(self.z, self.x, self.y);
|
mbgl::CanonicalTileID tileID = mbgl::CanonicalTileID(self.z, self.x, self.y);
|
||||||
mbgl::LatLngBounds tileBounds = mbgl::LatLngBounds(tileID);
|
mbgl::LatLngBounds tileBounds = mbgl::LatLngBounds(tileID);
|
||||||
data = [self.dataSource featuresInCoordinateBounds:MLNCoordinateBoundsFromLatLngBounds(tileBounds)
|
data =
|
||||||
|
[self.dataSource featuresInCoordinateBounds:MLNCoordinateBoundsFromLatLngBounds(tileBounds)
|
||||||
zoomLevel:self.z];
|
zoomLevel:self.z];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(![self isCancelled]) {
|
if (![self isCancelled]) {
|
||||||
mbgl::FeatureCollection featureCollection;
|
mbgl::FeatureCollection featureCollection;
|
||||||
featureCollection.reserve(data.count);
|
featureCollection.reserve(data.count);
|
||||||
for (MLNShape <MLNFeature> * feature in data) {
|
for (MLNShape<MLNFeature> *feature in data) {
|
||||||
if ([feature isMemberOfClass:[MLNShapeCollection class]]) {
|
if ([feature isMemberOfClass:[MLNShapeCollection class]]) {
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
@@ -146,7 +151,7 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
|
|||||||
const auto geojson = mbgl::GeoJSON{featureCollection};
|
const auto geojson = mbgl::GeoJSON{featureCollection};
|
||||||
|
|
||||||
// Note: potential race condition with `cancel`
|
// Note: potential race condition with `cancel`
|
||||||
if(![self isCancelled]) {
|
if (![self isCancelled]) {
|
||||||
if (auto *rawSource = self.rawSource) {
|
if (auto *rawSource = self.rawSource) {
|
||||||
rawSource->setTileData(mbgl::CanonicalTileID(self.z, self.x, self.y), geojson);
|
rawSource->setTileData(mbgl::CanonicalTileID(self.z, self.x, self.y), geojson);
|
||||||
}
|
}
|
||||||
@@ -163,19 +168,21 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
|
|||||||
|
|
||||||
@implementation MLNComputedShapeSource
|
@implementation MLNComputedShapeSource
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier options:(NSDictionary<MLNShapeSourceOption, id> *)options {
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
|
options:(NSDictionary<MLNShapeSourceOption, id> *)options {
|
||||||
NSOperationQueue *requestQueue = [[NSOperationQueue alloc] init];
|
NSOperationQueue *requestQueue = [[NSOperationQueue alloc] init];
|
||||||
requestQueue.name = [NSString stringWithFormat:@"mgl.MLNComputedShapeSource.%@", identifier];
|
requestQueue.name = [NSString stringWithFormat:@"mgl.MLNComputedShapeSource.%@", identifier];
|
||||||
requestQueue.qualityOfService = NSQualityOfServiceUtility;
|
requestQueue.qualityOfService = NSQualityOfServiceUtility;
|
||||||
requestQueue.maxConcurrentOperationCount = 4;
|
requestQueue.maxConcurrentOperationCount = 4;
|
||||||
|
|
||||||
auto sourceOptions = MBGLCustomGeometrySourceOptionsFromDictionary(options);
|
auto sourceOptions = MBGLCustomGeometrySourceOptionsFromDictionary(options);
|
||||||
sourceOptions.fetchTileFunction = ^void(const mbgl::CanonicalTileID& tileID) {
|
sourceOptions.fetchTileFunction = ^void(const mbgl::CanonicalTileID &tileID) {
|
||||||
NSOperation *operation = [[MLNComputedShapeSourceFetchOperation alloc] initForSource:self tile:tileID];
|
NSOperation *operation = [[MLNComputedShapeSourceFetchOperation alloc] initForSource:self
|
||||||
|
tile:tileID];
|
||||||
[requestQueue addOperation:operation];
|
[requestQueue addOperation:operation];
|
||||||
};
|
};
|
||||||
|
|
||||||
sourceOptions.cancelTileFunction = ^void(const mbgl::CanonicalTileID& tileID) {
|
sourceOptions.cancelTileFunction = ^void(const mbgl::CanonicalTileID &tileID) {
|
||||||
for (MLNComputedShapeSourceFetchOperation *operation in requestQueue.operations) {
|
for (MLNComputedShapeSourceFetchOperation *operation in requestQueue.operations) {
|
||||||
if (operation.x == tileID.x && operation.y == tileID.y && operation.z == tileID.z) {
|
if (operation.x == tileID.x && operation.y == tileID.y && operation.z == tileID.z) {
|
||||||
[operation cancel];
|
[operation cancel];
|
||||||
@@ -183,7 +190,8 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto source = std::make_unique<mbgl::style::CustomGeometrySource>(identifier.UTF8String, sourceOptions);
|
auto source =
|
||||||
|
std::make_unique<mbgl::style::CustomGeometrySource>(identifier.UTF8String, sourceOptions);
|
||||||
|
|
||||||
if (self = [super initWithPendingSource:std::move(source)]) {
|
if (self = [super initWithPendingSource:std::move(source)]) {
|
||||||
_requestQueue = requestQueue;
|
_requestQueue = requestQueue;
|
||||||
@@ -191,7 +199,9 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id<MLNComputedShapeSourceDataSource>)dataSource options:(NSDictionary<MLNShapeSourceOption, id> *)options {
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
|
dataSource:(id<MLNComputedShapeSourceDataSource>)dataSource
|
||||||
|
options:(NSDictionary<MLNShapeSourceOption, id> *)options {
|
||||||
if (self = [self initWithIdentifier:identifier options:options]) {
|
if (self = [self initWithIdentifier:identifier options:options]) {
|
||||||
[self setDataSource:dataSource];
|
[self setDataSource:dataSource];
|
||||||
}
|
}
|
||||||
@@ -202,12 +212,16 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
|
|||||||
[self.requestQueue cancelAllOperations];
|
[self.requestQueue cancelAllOperations];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFeatures:(NSArray<MLNShape <MLNFeature> *>*)features inTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)zoomLevel {
|
- (void)setFeatures:(NSArray<MLNShape<MLNFeature> *> *)features
|
||||||
|
inTileAtX:(NSUInteger)x
|
||||||
|
y:(NSUInteger)y
|
||||||
|
zoomLevel:(NSUInteger)zoomLevel {
|
||||||
MLNAssertStyleSourceIsValid();
|
MLNAssertStyleSourceIsValid();
|
||||||
mbgl::CanonicalTileID tileID = mbgl::CanonicalTileID((uint8_t)zoomLevel, (uint32_t)x, (uint32_t)y);
|
mbgl::CanonicalTileID tileID =
|
||||||
|
mbgl::CanonicalTileID((uint8_t)zoomLevel, (uint32_t)x, (uint32_t)y);
|
||||||
mbgl::FeatureCollection featureCollection;
|
mbgl::FeatureCollection featureCollection;
|
||||||
featureCollection.reserve(features.count);
|
featureCollection.reserve(features.count);
|
||||||
for (MLNShape <MLNFeature> * feature in features) {
|
for (MLNShape<MLNFeature> *feature in features) {
|
||||||
mbgl::GeoJSONFeature geoJsonObject = [feature geoJSONObject].get<mbgl::GeoJSONFeature>();
|
mbgl::GeoJSONFeature geoJsonObject = [feature geoJSONObject].get<mbgl::GeoJSONFeature>();
|
||||||
featureCollection.push_back(geoJsonObject);
|
featureCollection.push_back(geoJsonObject);
|
||||||
if ([feature isMemberOfClass:[MLNShapeCollection class]]) {
|
if ([feature isMemberOfClass:[MLNShapeCollection class]]) {
|
||||||
@@ -226,13 +240,17 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
|
|||||||
- (void)setDataSource:(id<MLNComputedShapeSourceDataSource>)dataSource {
|
- (void)setDataSource:(id<MLNComputedShapeSourceDataSource>)dataSource {
|
||||||
[self.requestQueue cancelAllOperations];
|
[self.requestQueue cancelAllOperations];
|
||||||
// Check which method the datasource implements, to avoid having to check for each tile
|
// Check which method the datasource implements, to avoid having to check for each tile
|
||||||
self.dataSourceImplementsFeaturesForTile = [dataSource respondsToSelector:@selector(featuresInTileAtX:y:zoomLevel:)];
|
self.dataSourceImplementsFeaturesForTile =
|
||||||
self.dataSourceImplementsFeaturesForBounds = [dataSource respondsToSelector:@selector(featuresInCoordinateBounds:zoomLevel:)];
|
[dataSource respondsToSelector:@selector(featuresInTileAtX:y:zoomLevel:)];
|
||||||
|
self.dataSourceImplementsFeaturesForBounds =
|
||||||
|
[dataSource respondsToSelector:@selector(featuresInCoordinateBounds:zoomLevel:)];
|
||||||
|
|
||||||
if (!self.dataSourceImplementsFeaturesForBounds && !self.dataSourceImplementsFeaturesForTile) {
|
if (!self.dataSourceImplementsFeaturesForBounds && !self.dataSourceImplementsFeaturesForTile) {
|
||||||
[NSException raise:MLNInvalidDatasourceException
|
[NSException
|
||||||
|
raise:MLNInvalidDatasourceException
|
||||||
format:@"Datasource does not implement any MLNComputedShapeSourceDataSource methods"];
|
format:@"Datasource does not implement any MLNComputedShapeSourceDataSource methods"];
|
||||||
} else if (self.dataSourceImplementsFeaturesForBounds && self.dataSourceImplementsFeaturesForTile) {
|
} else if (self.dataSourceImplementsFeaturesForBounds &&
|
||||||
|
self.dataSourceImplementsFeaturesForTile) {
|
||||||
[NSException raise:MLNInvalidDatasourceException
|
[NSException raise:MLNInvalidDatasourceException
|
||||||
format:@"Datasource implements multiple MLNComputedShapeSourceDataSource methods"];
|
format:@"Datasource implements multiple MLNComputedShapeSourceDataSource methods"];
|
||||||
}
|
}
|
||||||
@@ -240,14 +258,16 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
|
|||||||
_dataSource = dataSource;
|
_dataSource = dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) invalidateBounds:(MLNCoordinateBounds)bounds {
|
- (void)invalidateBounds:(MLNCoordinateBounds)bounds {
|
||||||
MLNAssertStyleSourceIsValid();
|
MLNAssertStyleSourceIsValid();
|
||||||
((mbgl::style::CustomGeometrySource *)self.rawSource)->invalidateRegion(MLNLatLngBoundsFromCoordinateBounds(bounds));
|
((mbgl::style::CustomGeometrySource *)self.rawSource)
|
||||||
|
->invalidateRegion(MLNLatLngBoundsFromCoordinateBounds(bounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) invalidateTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)z {
|
- (void)invalidateTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)z {
|
||||||
MLNAssertStyleSourceIsValid();
|
MLNAssertStyleSourceIsValid();
|
||||||
((mbgl::style::CustomGeometrySource *)self.rawSource)->invalidateTile(mbgl::CanonicalTileID(z, (unsigned int)x, (unsigned int)y));
|
((mbgl::style::CustomGeometrySource *)self.rawSource)
|
||||||
|
->invalidateTile(mbgl::CanonicalTileID(z, (unsigned int)x, (unsigned int)y));
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ namespace conversion {
|
|||||||
|
|
||||||
// A wrapper class for `id`, so as not to confuse ARC.
|
// A wrapper class for `id`, so as not to confuse ARC.
|
||||||
class Holder {
|
class Holder {
|
||||||
public:
|
public:
|
||||||
Holder(const id v) : value(v) {}
|
Holder(const id v) : value(v) {}
|
||||||
const id value;
|
const id value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
class ConversionTraits<Holder> {
|
class ConversionTraits<Holder> {
|
||||||
public:
|
public:
|
||||||
static bool isUndefined(const Holder& holder) {
|
static bool isUndefined(const Holder& holder) {
|
||||||
const id value = holder.value;
|
const id value = holder.value;
|
||||||
return !value || value == [NSNull null];
|
return !value || value == [NSNull null];
|
||||||
@@ -146,7 +146,7 @@ class ConversionTraits<Holder> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool _isBool(const id value) {
|
static bool _isBool(const id value) {
|
||||||
if (![value isKindOfClass:[NSNumber class]]) return false;
|
if (![value isKindOfClass:[NSNumber class]]) return false;
|
||||||
// char: 32-bit boolean
|
// char: 32-bit boolean
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
#import "MLNStyleLayer.h"
|
#import "MLNStyleLayer.h"
|
||||||
|
|
||||||
#import "MLNCustomDrawableStyleLayer_Private.h"
|
#import "MLNCustomDrawableStyleLayer_Private.h"
|
||||||
#import "MLNStyle_Private.h"
|
|
||||||
#import "MLNStyleLayer_Private.h"
|
|
||||||
#import "MLNGeometry_Private.h"
|
#import "MLNGeometry_Private.h"
|
||||||
|
#import "MLNStyleLayer_Private.h"
|
||||||
|
#import "MLNStyle_Private.h"
|
||||||
|
|
||||||
#include <mbgl/layermanager/custom_drawable_layer_factory.hpp>
|
#include <mbgl/layermanager/custom_drawable_layer_factory.hpp>
|
||||||
#include <mbgl/style/layers/custom_drawable_layer.hpp>
|
#include <mbgl/style/layers/custom_drawable_layer.hpp>
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <mbgl/style/layer.hpp>
|
#include <mbgl/style/layer.hpp>
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
@implementation MLNCustomDrawableStyleLayer
|
@implementation MLNCustomDrawableStyleLayer
|
||||||
|
|
||||||
- (instancetype)initWithRawLayer:(mbgl::style::Layer *)rawLayer {
|
- (instancetype)initWithRawLayer:(mbgl::style::Layer*)rawLayer {
|
||||||
return [super initWithRawLayer:rawLayer];
|
return [super initWithRawLayer:rawLayer];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
#import "MLNCustomStyleLayer.h"
|
#import "MLNCustomStyleLayer.h"
|
||||||
#import "MLNCustomStyleLayer_Private.h"
|
#import "MLNCustomStyleLayer_Private.h"
|
||||||
|
|
||||||
#import "MLNMapView_Private.h"
|
|
||||||
#import "MLNStyle_Private.h"
|
|
||||||
#import "MLNStyleLayer_Private.h"
|
|
||||||
#import "MLNGeometry_Private.h"
|
#import "MLNGeometry_Private.h"
|
||||||
|
#import "MLNMapView_Private.h"
|
||||||
|
#import "MLNStyleLayer_Private.h"
|
||||||
|
#import "MLNStyle_Private.h"
|
||||||
|
|
||||||
#if MLN_RENDER_BACKEND_METAL
|
#if MLN_RENDER_BACKEND_METAL
|
||||||
#import <MetalKit/MetalKit.h>
|
#import <MetalKit/MetalKit.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <mbgl/style/layers/custom_layer.hpp>
|
|
||||||
#include <mbgl/math/wrap.hpp>
|
#include <mbgl/math/wrap.hpp>
|
||||||
|
#include <mbgl/style/layers/custom_layer.hpp>
|
||||||
|
|
||||||
#if MLN_RENDER_BACKEND_METAL
|
#if MLN_RENDER_BACKEND_METAL
|
||||||
#include <mbgl/style/layers/mtl/custom_layer_render_parameters.hpp>
|
#include <mbgl/style/layers/mtl/custom_layer_render_parameters.hpp>
|
||||||
@@ -29,9 +29,7 @@ class MLNCustomLayerHost;
|
|||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||||
auto layer = std::make_unique<mbgl::style::CustomLayer>(
|
auto layer = std::make_unique<mbgl::style::CustomLayer>(
|
||||||
identifier.UTF8String,
|
identifier.UTF8String, std::make_unique<MLNCustomLayerHost>(self));
|
||||||
std::make_unique<MLNCustomLayerHost>(self)
|
|
||||||
);
|
|
||||||
return self = [super initWithPendingLayer:std::move(layer)];
|
return self = [super initWithPendingLayer:std::move(layer)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,20 +89,23 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void initialize() {
|
void initialize() {
|
||||||
if (layerRef == nil) return;
|
if (layerRef == nil)
|
||||||
else if (layer == nil) layer = layerRef;
|
return;
|
||||||
|
else if (layer == nil)
|
||||||
|
layer = layerRef;
|
||||||
|
|
||||||
if (layer.mapView) {
|
if (layer.mapView) {
|
||||||
[layer didMoveToMapView:layer.mapView];
|
[layer didMoveToMapView:layer.mapView];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void render(const mbgl::style::CustomLayerRenderParameters& parameters) {
|
void render(const mbgl::style::CustomLayerRenderParameters ¶meters) {
|
||||||
if (!layer) return;
|
if (!layer) return;
|
||||||
|
|
||||||
#if MLN_RENDER_BACKEND_METAL
|
#if MLN_RENDER_BACKEND_METAL
|
||||||
MTL::RenderCommandEncoder* ptr =
|
MTL::RenderCommandEncoder *ptr =
|
||||||
static_cast<const mbgl::style::mtl::CustomLayerRenderParameters&>(parameters).encoder.get();
|
static_cast<const mbgl::style::mtl::CustomLayerRenderParameters &>(parameters)
|
||||||
|
.encoder.get();
|
||||||
id<MTLRenderCommandEncoder> encoder = (__bridge id<MTLRenderCommandEncoder>)ptr;
|
id<MTLRenderCommandEncoder> encoder = (__bridge id<MTLRenderCommandEncoder>)ptr;
|
||||||
layer.renderEncoder = encoder;
|
layer.renderEncoder = encoder;
|
||||||
#endif
|
#endif
|
||||||
@@ -116,16 +117,14 @@ public:
|
|||||||
.direction = mbgl::util::wrap(parameters.bearing, 0., 360.),
|
.direction = mbgl::util::wrap(parameters.bearing, 0., 360.),
|
||||||
.pitch = static_cast<CGFloat>(parameters.pitch),
|
.pitch = static_cast<CGFloat>(parameters.pitch),
|
||||||
.fieldOfView = static_cast<CGFloat>(parameters.fieldOfView),
|
.fieldOfView = static_cast<CGFloat>(parameters.fieldOfView),
|
||||||
.projectionMatrix = MLNMatrix4Make(parameters.projectionMatrix)
|
.projectionMatrix = MLNMatrix4Make(parameters.projectionMatrix)};
|
||||||
};
|
|
||||||
|
|
||||||
if (layer.mapView) {
|
if (layer.mapView) {
|
||||||
[layer drawInMapView:layer.mapView withContext:drawingContext];
|
[layer drawInMapView:layer.mapView withContext:drawingContext];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void contextLost() {
|
void contextLost() {}
|
||||||
}
|
|
||||||
|
|
||||||
void deinitialize() {
|
void deinitialize() {
|
||||||
if (layer == nil) return;
|
if (layer == nil) return;
|
||||||
@@ -138,13 +137,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
__weak MLNCustomStyleLayer * layerRef;
|
__weak MLNCustomStyleLayer *layerRef;
|
||||||
MLNCustomStyleLayer * layer = nil;
|
MLNCustomStyleLayer *layer = nil;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mbgl {
|
namespace mbgl {
|
||||||
|
|
||||||
MLNStyleLayer* CustomStyleLayerPeerFactory::createPeer(style::Layer* rawLayer) {
|
MLNStyleLayer *CustomStyleLayerPeerFactory::createPeer(style::Layer *rawLayer) {
|
||||||
return [[MLNCustomStyleLayer alloc] initWithRawLayer:rawLayer];
|
return [[MLNCustomStyleLayer alloc] initWithRawLayer:rawLayer];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
#import "MLNFoundation_Private.h"
|
|
||||||
#import "MLNFeature_Private.h"
|
|
||||||
#import "MLNCluster.h"
|
#import "MLNCluster.h"
|
||||||
|
#import "MLNFeature_Private.h"
|
||||||
|
#import "MLNFoundation_Private.h"
|
||||||
|
|
||||||
#import "MLNPointAnnotation.h"
|
#import "MLNPointAnnotation.h"
|
||||||
#import "MLNPolyline.h"
|
|
||||||
#import "MLNPolygon.h"
|
#import "MLNPolygon.h"
|
||||||
|
#import "MLNPolyline.h"
|
||||||
#import "MLNValueEvaluator.h"
|
#import "MLNValueEvaluator.h"
|
||||||
|
|
||||||
#import "MLNShape_Private.h"
|
|
||||||
#import "MLNPointCollection_Private.h"
|
#import "MLNPointCollection_Private.h"
|
||||||
#import "MLNPolyline_Private.h"
|
|
||||||
#import "MLNPolygon_Private.h"
|
#import "MLNPolygon_Private.h"
|
||||||
|
#import "MLNPolyline_Private.h"
|
||||||
|
#import "MLNShape_Private.h"
|
||||||
|
|
||||||
#import "NSDictionary+MLNAdditions.h"
|
|
||||||
#import "NSArray+MLNAdditions.h"
|
|
||||||
#import "NSExpression+MLNPrivateAdditions.h"
|
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
#import "NSArray+MLNAdditions.h"
|
||||||
|
#import "NSDictionary+MLNAdditions.h"
|
||||||
|
#import "NSExpression+MLNPrivateAdditions.h"
|
||||||
|
|
||||||
#import <mbgl/util/geometry.hpp>
|
|
||||||
#import <mbgl/style/conversion/geojson.hpp>
|
|
||||||
#import <mapbox/feature.hpp>
|
#import <mapbox/feature.hpp>
|
||||||
|
#import <mbgl/style/conversion/geojson.hpp>
|
||||||
|
#import <mbgl/util/geometry.hpp>
|
||||||
|
|
||||||
// Cluster constants
|
// Cluster constants
|
||||||
static NSString * const MLNClusterIdentifierKey = @"cluster_id";
|
static NSString *const MLNClusterIdentifierKey = @"cluster_id";
|
||||||
static NSString * const MLNClusterCountKey = @"point_count";
|
static NSString *const MLNClusterCountKey = @"point_count";
|
||||||
const NSUInteger MLNClusterIdentifierInvalid = NSUIntegerMax;
|
const NSUInteger MLNClusterIdentifierInvalid = NSUIntegerMax;
|
||||||
|
|
||||||
@interface MLNEmptyFeature ()
|
@interface MLNEmptyFeature ()
|
||||||
@@ -45,7 +45,7 @@ MLN_DEFINE_FEATURE_ATTRIBUTES_GETTER();
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)geoJSONDictionary {
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
return NSDictionaryFeatureForGeometry(@{@"type": @"Empty"}, self.attributes, self.identifier);
|
return NSDictionaryFeatureForGeometry(@{@"type" : @"Empty"}, self.attributes, self.identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::GeoJSON)geoJSONObject {
|
- (mbgl::GeoJSON)geoJSONObject {
|
||||||
@@ -56,11 +56,12 @@ MLN_DEFINE_FEATURE_ATTRIBUTES_GETTER();
|
|||||||
return mbgl::EmptyGeometry();
|
return mbgl::EmptyGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
|
||||||
return [NSString stringWithFormat:@"<%@: %p; identifier = %@, attributes = %@>",
|
return [NSString stringWithFormat:@"<%@: %p; identifier = %@, attributes = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self,
|
NSStringFromClass([self class]), (void *)self,
|
||||||
self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
|
self.identifier
|
||||||
|
? [NSString stringWithFormat:@"\"%@\"", self.identifier]
|
||||||
|
: self.identifier,
|
||||||
self.attributes.count ? self.attributes : @"none"];
|
self.attributes.count ? self.attributes : @"none"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,18 +86,20 @@ MLN_DEFINE_FEATURE_ATTRIBUTES_GETTER();
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)geoJSONDictionary {
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes, self.identifier);
|
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes,
|
||||||
|
self.identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::GeoJSON)geoJSONObject {
|
- (mbgl::GeoJSON)geoJSONObject {
|
||||||
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
|
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
return [NSString
|
||||||
return [NSString stringWithFormat:@"<%@: %p; identifier = %@, coordinate = %f, %f, attributes = %@>",
|
stringWithFormat:@"<%@: %p; identifier = %@, coordinate = %f, %f, attributes = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self,
|
NSStringFromClass([self class]), (void *)self,
|
||||||
self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
|
self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier]
|
||||||
|
: self.identifier,
|
||||||
self.coordinate.latitude, self.coordinate.longitude,
|
self.coordinate.latitude, self.coordinate.longitude,
|
||||||
self.attributes.count ? self.attributes : @"none"];
|
self.attributes.count ? self.attributes : @"none"];
|
||||||
}
|
}
|
||||||
@@ -106,7 +109,8 @@ MLN_DEFINE_FEATURE_ATTRIBUTES_GETTER();
|
|||||||
@implementation MLNPointFeatureCluster
|
@implementation MLNPointFeatureCluster
|
||||||
|
|
||||||
- (NSUInteger)clusterIdentifier {
|
- (NSUInteger)clusterIdentifier {
|
||||||
NSNumber *clusterNumber = MLN_OBJC_DYNAMIC_CAST([self attributeForKey:MLNClusterIdentifierKey], NSNumber);
|
NSNumber *clusterNumber =
|
||||||
|
MLN_OBJC_DYNAMIC_CAST([self attributeForKey:MLNClusterIdentifierKey], NSNumber);
|
||||||
MLNAssert(clusterNumber, @"Clusters should have a cluster_id");
|
MLNAssert(clusterNumber, @"Clusters should have a cluster_id");
|
||||||
|
|
||||||
if (!clusterNumber) {
|
if (!clusterNumber) {
|
||||||
@@ -127,7 +131,6 @@ MLN_DEFINE_FEATURE_ATTRIBUTES_GETTER();
|
|||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@interface MLNPolylineFeature ()
|
@interface MLNPolylineFeature ()
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -147,18 +150,20 @@ MLN_DEFINE_FEATURE_ATTRIBUTES_GETTER();
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)geoJSONDictionary {
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes, self.identifier);
|
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes,
|
||||||
|
self.identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::GeoJSON)geoJSONObject {
|
- (mbgl::GeoJSON)geoJSONObject {
|
||||||
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
|
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
return [NSString
|
||||||
return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
|
stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self,
|
NSStringFromClass([self class]), (void *)self,
|
||||||
self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
|
self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier]
|
||||||
|
: self.identifier,
|
||||||
(unsigned long)[self pointCount],
|
(unsigned long)[self pointCount],
|
||||||
MLNStringFromCoordinateBounds(self.overlayBounds),
|
MLNStringFromCoordinateBounds(self.overlayBounds),
|
||||||
self.attributes.count ? self.attributes : @"none"];
|
self.attributes.count ? self.attributes : @"none"];
|
||||||
@@ -185,18 +190,20 @@ MLN_DEFINE_FEATURE_ATTRIBUTES_GETTER();
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)geoJSONDictionary {
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes, self.identifier);
|
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes,
|
||||||
|
self.identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::GeoJSON)geoJSONObject {
|
- (mbgl::GeoJSON)geoJSONObject {
|
||||||
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
|
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
return [NSString
|
||||||
return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
|
stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self,
|
NSStringFromClass([self class]), (void *)self,
|
||||||
self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
|
self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier]
|
||||||
|
: self.identifier,
|
||||||
(unsigned long)[self pointCount],
|
(unsigned long)[self pointCount],
|
||||||
MLNStringFromCoordinateBounds(self.overlayBounds),
|
MLNStringFromCoordinateBounds(self.overlayBounds),
|
||||||
self.attributes.count ? self.attributes : @"none"];
|
self.attributes.count ? self.attributes : @"none"];
|
||||||
@@ -223,7 +230,8 @@ MLN_DEFINE_FEATURE_ATTRIBUTES_GETTER();
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)geoJSONDictionary {
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes, self.identifier);
|
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes,
|
||||||
|
self.identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::GeoJSON)geoJSONObject {
|
- (mbgl::GeoJSON)geoJSONObject {
|
||||||
@@ -251,18 +259,20 @@ MLN_DEFINE_FEATURE_ATTRIBUTES_GETTER();
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)geoJSONDictionary {
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes, self.identifier);
|
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes,
|
||||||
|
self.identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::GeoJSON)geoJSONObject {
|
- (mbgl::GeoJSON)geoJSONObject {
|
||||||
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
|
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
return [NSString
|
||||||
return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
|
stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self,
|
NSStringFromClass([self class]), (void *)self,
|
||||||
self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
|
self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier]
|
||||||
|
: self.identifier,
|
||||||
(unsigned long)self.polylines.count,
|
(unsigned long)self.polylines.count,
|
||||||
MLNStringFromCoordinateBounds(self.overlayBounds),
|
MLNStringFromCoordinateBounds(self.overlayBounds),
|
||||||
self.attributes.count ? self.attributes : @"none"];
|
self.attributes.count ? self.attributes : @"none"];
|
||||||
@@ -289,18 +299,20 @@ MLN_DEFINE_FEATURE_ATTRIBUTES_GETTER();
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)geoJSONDictionary {
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes, self.identifier);
|
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes,
|
||||||
|
self.identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::GeoJSON)geoJSONObject {
|
- (mbgl::GeoJSON)geoJSONObject {
|
||||||
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
|
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
return [NSString
|
||||||
return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
|
stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self,
|
NSStringFromClass([self class]), (void *)self,
|
||||||
self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
|
self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier]
|
||||||
|
: self.identifier,
|
||||||
(unsigned long)self.polygons.count,
|
(unsigned long)self.polygons.count,
|
||||||
MLNStringFromCoordinateBounds(self.overlayBounds),
|
MLNStringFromCoordinateBounds(self.overlayBounds),
|
||||||
self.attributes.count ? self.attributes : @"none"];
|
self.attributes.count ? self.attributes : @"none"];
|
||||||
@@ -332,15 +344,17 @@ MLN_DEFINE_FEATURE_ATTRIBUTES_GETTER();
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)geoJSONDictionary {
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes, self.identifier);
|
return NSDictionaryFeatureForGeometry([super geoJSONDictionary], self.attributes,
|
||||||
|
self.identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::GeoJSON)geoJSONObject {
|
- (mbgl::GeoJSON)geoJSONObject {
|
||||||
mbgl::FeatureCollection featureCollection;
|
mbgl::FeatureCollection featureCollection;
|
||||||
featureCollection.reserve(self.shapes.count);
|
featureCollection.reserve(self.shapes.count);
|
||||||
for (MLNShape <MLNFeature> *feature in self.shapes) {
|
for (MLNShape<MLNFeature> *feature in self.shapes) {
|
||||||
auto geoJSONObject = feature.geoJSONObject;
|
auto geoJSONObject = feature.geoJSONObject;
|
||||||
MLNAssert(geoJSONObject.is<mbgl::GeoJSONFeature>(), @"Feature collection must only contain features.");
|
MLNAssert(geoJSONObject.is<mbgl::GeoJSONFeature>(),
|
||||||
|
@"Feature collection must only contain features.");
|
||||||
featureCollection.push_back(geoJSONObject.get<mbgl::GeoJSONFeature>());
|
featureCollection.push_back(geoJSONObject.get<mbgl::GeoJSONFeature>());
|
||||||
}
|
}
|
||||||
return featureCollection;
|
return featureCollection;
|
||||||
@@ -359,16 +373,14 @@ private:
|
|||||||
const bool is_in_feature;
|
const bool is_in_feature;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GeometryEvaluator(const mbgl::PropertyMap *properties = nullptr, const bool isInFeature = false):
|
GeometryEvaluator(const mbgl::PropertyMap *properties = nullptr, const bool isInFeature = false)
|
||||||
shared_properties(properties),
|
: shared_properties(properties), is_in_feature(isInFeature) {}
|
||||||
is_in_feature(isInFeature)
|
|
||||||
{}
|
|
||||||
|
|
||||||
MLNShape * operator()(const mbgl::EmptyGeometry &) const {
|
MLNShape *operator()(const mbgl::EmptyGeometry &) const {
|
||||||
return is_in_feature ? [[MLNEmptyFeature alloc] init] : [[MLNShape alloc] init];
|
return is_in_feature ? [[MLNEmptyFeature alloc] init] : [[MLNShape alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNShape * operator()(const mbgl::Point<T> &geometry) const {
|
MLNShape *operator()(const mbgl::Point<T> &geometry) const {
|
||||||
Class shapeClass = is_in_feature ? [MLNPointFeature class] : [MLNPointAnnotation class];
|
Class shapeClass = is_in_feature ? [MLNPointFeature class] : [MLNPointAnnotation class];
|
||||||
|
|
||||||
// If we're dealing with a cluster, we should change the class type.
|
// If we're dealing with a cluster, we should change the class type.
|
||||||
@@ -391,27 +403,29 @@ public:
|
|||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNShape * operator()(const mbgl::LineString<T> &geometry) const {
|
MLNShape *operator()(const mbgl::LineString<T> &geometry) const {
|
||||||
std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(geometry);
|
std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(geometry);
|
||||||
Class shapeClass = is_in_feature ? [MLNPolylineFeature class] : [MLNPolyline class];
|
Class shapeClass = is_in_feature ? [MLNPolylineFeature class] : [MLNPolyline class];
|
||||||
return [shapeClass polylineWithCoordinates:&coordinates[0] count:coordinates.size()];
|
return [shapeClass polylineWithCoordinates:&coordinates[0] count:coordinates.size()];
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNShape * operator()(const mbgl::Polygon<T> &geometry) const {
|
MLNShape *operator()(const mbgl::Polygon<T> &geometry) const {
|
||||||
return toShape<MLNPolygon, MLNPolygonFeature>(geometry, is_in_feature);
|
return toShape<MLNPolygon, MLNPolygonFeature>(geometry, is_in_feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNShape * operator()(const mbgl::MultiPoint<T> &geometry) const {
|
MLNShape *operator()(const mbgl::MultiPoint<T> &geometry) const {
|
||||||
std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(geometry);
|
std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(geometry);
|
||||||
Class shapeClass = is_in_feature ? [MLNPointCollectionFeature class] : [MLNPointCollection class];
|
Class shapeClass =
|
||||||
|
is_in_feature ? [MLNPointCollectionFeature class] : [MLNPointCollection class];
|
||||||
return [[shapeClass alloc] initWithCoordinates:&coordinates[0] count:coordinates.size()];
|
return [[shapeClass alloc] initWithCoordinates:&coordinates[0] count:coordinates.size()];
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNShape * operator()(const mbgl::MultiLineString<T> &geometry) const {
|
MLNShape *operator()(const mbgl::MultiLineString<T> &geometry) const {
|
||||||
NSMutableArray *polylines = [NSMutableArray arrayWithCapacity:geometry.size()];
|
NSMutableArray *polylines = [NSMutableArray arrayWithCapacity:geometry.size()];
|
||||||
for (auto &lineString : geometry) {
|
for (auto &lineString : geometry) {
|
||||||
std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(lineString);
|
std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(lineString);
|
||||||
MLNPolyline *polyline = [MLNPolyline polylineWithCoordinates:&coordinates[0] count:coordinates.size()];
|
MLNPolyline *polyline = [MLNPolyline polylineWithCoordinates:&coordinates[0]
|
||||||
|
count:coordinates.size()];
|
||||||
[polylines addObject:polyline];
|
[polylines addObject:polyline];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,7 +433,7 @@ public:
|
|||||||
return [shapeClass multiPolylineWithPolylines:polylines];
|
return [shapeClass multiPolylineWithPolylines:polylines];
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNShape * operator()(const mbgl::MultiPolygon<T> &geometry) const {
|
MLNShape *operator()(const mbgl::MultiPolygon<T> &geometry) const {
|
||||||
NSMutableArray *polygons = [NSMutableArray arrayWithCapacity:geometry.size()];
|
NSMutableArray *polygons = [NSMutableArray arrayWithCapacity:geometry.size()];
|
||||||
for (auto &polygon : geometry) {
|
for (auto &polygon : geometry) {
|
||||||
[polygons addObject:toShape(polygon, false)];
|
[polygons addObject:toShape(polygon, false)];
|
||||||
@@ -429,14 +443,16 @@ public:
|
|||||||
return [shapeClass multiPolygonWithPolygons:polygons];
|
return [shapeClass multiPolygonWithPolygons:polygons];
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNShape * operator()(const mapbox::geometry::geometry_collection<T> &collection) const {
|
MLNShape *operator()(const mapbox::geometry::geometry_collection<T> &collection) const {
|
||||||
NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:collection.size()];
|
NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:collection.size()];
|
||||||
for (auto &geometry : collection) {
|
for (auto &geometry : collection) {
|
||||||
// This is very much like the transformation that happens in MLNFeaturesFromMBGLFeatures(), but these are raw geometries with no associated feature IDs or attributes.
|
// This is very much like the transformation that happens in MLNFeaturesFromMBGLFeatures(),
|
||||||
|
// but these are raw geometries with no associated feature IDs or attributes.
|
||||||
MLNShape *shape = mapbox::geometry::geometry<T>::visit(geometry, *this);
|
MLNShape *shape = mapbox::geometry::geometry<T>::visit(geometry, *this);
|
||||||
[shapes addObject:shape];
|
[shapes addObject:shape];
|
||||||
}
|
}
|
||||||
Class shapeClass = is_in_feature ? [MLNShapeCollectionFeature class] : [MLNShapeCollection class];
|
Class shapeClass =
|
||||||
|
is_in_feature ? [MLNShapeCollectionFeature class] : [MLNShapeCollection class];
|
||||||
return [shapeClass shapeCollectionWithShapes:shapes];
|
return [shapeClass shapeCollectionWithShapes:shapes];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,14 +461,16 @@ private:
|
|||||||
return CLLocationCoordinate2DMake(point.y, point.x);
|
return CLLocationCoordinate2DMake(point.y, point.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<CLLocationCoordinate2D> toLocationCoordinates2D(const std::vector<mbgl::Point<T>> &points) {
|
static std::vector<CLLocationCoordinate2D> toLocationCoordinates2D(
|
||||||
|
const std::vector<mbgl::Point<T>> &points) {
|
||||||
std::vector<CLLocationCoordinate2D> coordinates;
|
std::vector<CLLocationCoordinate2D> coordinates;
|
||||||
coordinates.reserve(points.size());
|
coordinates.reserve(points.size());
|
||||||
std::transform(points.begin(), points.end(), std::back_inserter(coordinates), toLocationCoordinate2D);
|
std::transform(points.begin(), points.end(), std::back_inserter(coordinates),
|
||||||
|
toLocationCoordinate2D);
|
||||||
return coordinates;
|
return coordinates;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename U = MLNPolygon, typename V = MLNPolygonFeature>
|
template <typename U = MLNPolygon, typename V = MLNPolygonFeature>
|
||||||
static U *toShape(const mbgl::Polygon<T> &geometry, const bool isInFeature) {
|
static U *toShape(const mbgl::Polygon<T> &geometry, const bool isInFeature) {
|
||||||
auto &linearRing = geometry.front();
|
auto &linearRing = geometry.front();
|
||||||
std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(linearRing);
|
std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(linearRing);
|
||||||
@@ -462,31 +480,34 @@ private:
|
|||||||
for (auto iter = geometry.begin() + 1; iter != geometry.end(); iter++) {
|
for (auto iter = geometry.begin() + 1; iter != geometry.end(); iter++) {
|
||||||
auto &innerRing = *iter;
|
auto &innerRing = *iter;
|
||||||
std::vector<CLLocationCoordinate2D> innerCoordinates = toLocationCoordinates2D(innerRing);
|
std::vector<CLLocationCoordinate2D> innerCoordinates = toLocationCoordinates2D(innerRing);
|
||||||
MLNPolygon *innerPolygon = [MLNPolygon polygonWithCoordinates:&innerCoordinates[0] count:innerCoordinates.size()];
|
MLNPolygon *innerPolygon = [MLNPolygon polygonWithCoordinates:&innerCoordinates[0]
|
||||||
|
count:innerCoordinates.size()];
|
||||||
[innerPolygons addObject:innerPolygon];
|
[innerPolygons addObject:innerPolygon];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Class shapeClass = isInFeature ? [V class] : [U class];
|
Class shapeClass = isInFeature ? [V class] : [U class];
|
||||||
return [shapeClass polygonWithCoordinates:&coordinates[0] count:coordinates.size() interiorPolygons:innerPolygons];
|
return [shapeClass polygonWithCoordinates:&coordinates[0]
|
||||||
|
count:coordinates.size()
|
||||||
|
interiorPolygons:innerPolygons];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class GeoJSONEvaluator {
|
class GeoJSONEvaluator {
|
||||||
public:
|
public:
|
||||||
MLNShape * operator()(const mbgl::Geometry<T> &geometry) const {
|
MLNShape *operator()(const mbgl::Geometry<T> &geometry) const {
|
||||||
GeometryEvaluator<T> evaluator;
|
GeometryEvaluator<T> evaluator;
|
||||||
MLNShape *shape = mapbox::geometry::geometry<T>::visit(geometry, evaluator);
|
MLNShape *shape = mapbox::geometry::geometry<T>::visit(geometry, evaluator);
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNShape <MLNFeature> * operator()(const mbgl::GeoJSONFeature &feature) const {
|
MLNShape<MLNFeature> *operator()(const mbgl::GeoJSONFeature &feature) const {
|
||||||
MLNShape <MLNFeature> *shape = (MLNShape <MLNFeature> *)MLNFeatureFromMBGLFeature(feature);
|
MLNShape<MLNFeature> *shape = (MLNShape<MLNFeature> *)MLNFeatureFromMBGLFeature(feature);
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNShape <MLNFeature> * operator()(const mbgl::FeatureCollection &collection) const {
|
MLNShape<MLNFeature> *operator()(const mbgl::FeatureCollection &collection) const {
|
||||||
NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:collection.size()];
|
NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:collection.size()];
|
||||||
for (const auto &feature : collection) {
|
for (const auto &feature : collection) {
|
||||||
[shapes addObject:MLNFeatureFromMBGLFeature(feature)];
|
[shapes addObject:MLNFeatureFromMBGLFeature(feature)];
|
||||||
@@ -495,7 +516,8 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
NSArray<MLNShape <MLNFeature> *> *MLNFeaturesFromMBGLFeatures(const std::vector<mbgl::Feature> &features) {
|
NSArray<MLNShape<MLNFeature> *> *MLNFeaturesFromMBGLFeatures(
|
||||||
|
const std::vector<mbgl::Feature> &features) {
|
||||||
NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:features.size()];
|
NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:features.size()];
|
||||||
for (const auto &feature : features) {
|
for (const auto &feature : features) {
|
||||||
[shapes addObject:MLNFeatureFromMBGLFeature(static_cast<mbgl::GeoJSONFeature>(feature))];
|
[shapes addObject:MLNFeatureFromMBGLFeature(static_cast<mbgl::GeoJSONFeature>(feature))];
|
||||||
@@ -503,7 +525,8 @@ NSArray<MLNShape <MLNFeature> *> *MLNFeaturesFromMBGLFeatures(const std::vector<
|
|||||||
return shapes;
|
return shapes;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSArray<MLNShape <MLNFeature> *> *MLNFeaturesFromMBGLFeatures(const std::vector<mbgl::GeoJSONFeature> &features) {
|
NSArray<MLNShape<MLNFeature> *> *MLNFeaturesFromMBGLFeatures(
|
||||||
|
const std::vector<mbgl::GeoJSONFeature> &features) {
|
||||||
NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:features.size()];
|
NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:features.size()];
|
||||||
for (const auto &feature : features) {
|
for (const auto &feature : features) {
|
||||||
[shapes addObject:MLNFeatureFromMBGLFeature(feature)];
|
[shapes addObject:MLNFeatureFromMBGLFeature(feature)];
|
||||||
@@ -511,15 +534,17 @@ NSArray<MLNShape <MLNFeature> *> *MLNFeaturesFromMBGLFeatures(const std::vector<
|
|||||||
return shapes;
|
return shapes;
|
||||||
}
|
}
|
||||||
|
|
||||||
id <MLNFeature> MLNFeatureFromMBGLFeature(const mbgl::GeoJSONFeature &feature) {
|
id<MLNFeature> MLNFeatureFromMBGLFeature(const mbgl::GeoJSONFeature &feature) {
|
||||||
NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:feature.properties.size()];
|
NSMutableDictionary *attributes =
|
||||||
|
[NSMutableDictionary dictionaryWithCapacity:feature.properties.size()];
|
||||||
for (auto &pair : feature.properties) {
|
for (auto &pair : feature.properties) {
|
||||||
auto &value = pair.second;
|
auto &value = pair.second;
|
||||||
ValueEvaluator evaluator;
|
ValueEvaluator evaluator;
|
||||||
attributes[@(pair.first.c_str())] = mbgl::Value::visit(value, evaluator);
|
attributes[@(pair.first.c_str())] = mbgl::Value::visit(value, evaluator);
|
||||||
}
|
}
|
||||||
GeometryEvaluator<double> evaluator(&feature.properties, true);
|
GeometryEvaluator<double> evaluator(&feature.properties, true);
|
||||||
MLNShape <MLNFeature> *shape = (MLNShape <MLNFeature> *)mapbox::geometry::geometry<double>::visit(feature.geometry, evaluator);
|
MLNShape<MLNFeature> *shape = (MLNShape<MLNFeature> *)mapbox::geometry::geometry<double>::visit(
|
||||||
|
feature.geometry, evaluator);
|
||||||
if (!feature.id.is<mapbox::feature::null_value_t>()) {
|
if (!feature.id.is<mapbox::feature::null_value_t>()) {
|
||||||
shape.identifier = mbgl::FeatureIdentifier::visit(feature.id, ValueEvaluator());
|
shape.identifier = mbgl::FeatureIdentifier::visit(feature.id, ValueEvaluator());
|
||||||
}
|
}
|
||||||
@@ -528,14 +553,14 @@ id <MLNFeature> MLNFeatureFromMBGLFeature(const mbgl::GeoJSONFeature &feature) {
|
|||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNShape* MLNShapeFromGeoJSON(const mapbox::geojson::geojson &geojson) {
|
MLNShape *MLNShapeFromGeoJSON(const mapbox::geojson::geojson &geojson) {
|
||||||
GeoJSONEvaluator<double> evaluator;
|
GeoJSONEvaluator<double> evaluator;
|
||||||
MLNShape *shape = mapbox::geojson::geojson::visit(geojson, evaluator);
|
MLNShape *shape = mapbox::geojson::geojson::visit(geojson, evaluator);
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
mbgl::GeoJSONFeature mbglFeature(mbgl::GeoJSONFeature feature, id identifier, NSDictionary *attributes)
|
mbgl::GeoJSONFeature mbglFeature(mbgl::GeoJSONFeature feature, id identifier,
|
||||||
{
|
NSDictionary *attributes) {
|
||||||
if (identifier) {
|
if (identifier) {
|
||||||
NSExpression *identifierExpression = [NSExpression expressionForConstantValue:identifier];
|
NSExpression *identifierExpression = [NSExpression expressionForConstantValue:identifier];
|
||||||
feature.id = [identifierExpression mgl_featureIdentifier];
|
feature.id = [identifierExpression mgl_featureIdentifier];
|
||||||
@@ -544,10 +569,11 @@ mbgl::GeoJSONFeature mbglFeature(mbgl::GeoJSONFeature feature, id identifier, NS
|
|||||||
return feature;
|
return feature;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSDictionary<NSString *, id> *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier) {
|
NSDictionary<NSString *, id> *NSDictionaryFeatureForGeometry(NSDictionary *geometry,
|
||||||
NSMutableDictionary *feature = [@{@"type": @"Feature",
|
NSDictionary *attributes,
|
||||||
@"properties": attributes,
|
id identifier) {
|
||||||
@"geometry": geometry} mutableCopy];
|
NSMutableDictionary *feature =
|
||||||
|
[@{@"type" : @"Feature", @"properties" : attributes, @"geometry" : geometry} mutableCopy];
|
||||||
feature[@"id"] = identifier;
|
feature[@"id"] = identifier;
|
||||||
return [feature copy];
|
return [feature copy];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,13 @@
|
|||||||
|
|
||||||
- (NSString *)description {
|
- (NSString *)description {
|
||||||
if (self.rawLayer) {
|
if (self.rawLayer) {
|
||||||
return [NSString stringWithFormat:
|
return [NSString
|
||||||
@"<%@: %p; identifier = %@; sourceIdentifier = %@; visible = %@>",
|
stringWithFormat:@"<%@: %p; identifier = %@; sourceIdentifier = %@; visible = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self, self.identifier,
|
NSStringFromClass([self class]), (void *)self, self.identifier,
|
||||||
self.sourceIdentifier, self.visible ? @"YES" : @"NO"];
|
self.sourceIdentifier, self.visible ? @"YES" : @"NO"];
|
||||||
}
|
} else {
|
||||||
else {
|
return [NSString
|
||||||
return [NSString stringWithFormat:
|
stringWithFormat:@"<%@: %p; identifier = %@; sourceIdentifier = <unknown>; visible = NO>",
|
||||||
@"<%@: %p; identifier = %@; sourceIdentifier = <unknown>; visible = NO>",
|
|
||||||
NSStringFromClass([self class]), (void *)self, self.identifier];
|
NSStringFromClass([self class]), (void *)self, self.identifier];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#import "MLNFoundation_Private.h"
|
#import "MLNFoundation_Private.h"
|
||||||
|
|
||||||
/// Initializes the run loop shim that lives on the main thread.
|
/// Initializes the run loop shim that lives on the main thread.
|
||||||
void MLNInitializeRunLoop() {
|
void MLNInitializeRunLoop() { static mbgl::util::RunLoop runLoop; }
|
||||||
static mbgl::util::RunLoop runLoop;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -45,58 +45,75 @@ mbgl::LatLng MLNLatLngFromLocationCoordinate2D(CLLocationCoordinate2D coordinate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CLLocationDistance MLNAltitudeForZoomLevel(double zoomLevel, CGFloat pitch, CLLocationDegrees latitude, CGSize size) {
|
CLLocationDistance MLNAltitudeForZoomLevel(double zoomLevel, CGFloat pitch,
|
||||||
CLLocationDistance metersPerPixel = mbgl::Projection::getMetersPerPixelAtLatitude(latitude, zoomLevel);
|
CLLocationDegrees latitude, CGSize size) {
|
||||||
|
CLLocationDistance metersPerPixel =
|
||||||
|
mbgl::Projection::getMetersPerPixelAtLatitude(latitude, zoomLevel);
|
||||||
CLLocationDistance metersTall = metersPerPixel * size.height;
|
CLLocationDistance metersTall = metersPerPixel * size.height;
|
||||||
CLLocationDistance altitude = metersTall / 2 / std::tan(MLNRadiansFromDegrees(MLNAngularFieldOfView) / 2.);
|
CLLocationDistance altitude =
|
||||||
|
metersTall / 2 / std::tan(MLNRadiansFromDegrees(MLNAngularFieldOfView) / 2.);
|
||||||
return altitude * std::sin(M_PI_2 - MLNRadiansFromDegrees(pitch)) / std::sin(M_PI_2);
|
return altitude * std::sin(M_PI_2 - MLNRadiansFromDegrees(pitch)) / std::sin(M_PI_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
double MLNZoomLevelForAltitude(CLLocationDistance altitude, CGFloat pitch, CLLocationDegrees latitude, CGSize size) {
|
double MLNZoomLevelForAltitude(CLLocationDistance altitude, CGFloat pitch,
|
||||||
CLLocationDistance eyeAltitude = altitude / std::sin(M_PI_2 - MLNRadiansFromDegrees(pitch)) * std::sin(M_PI_2);
|
CLLocationDegrees latitude, CGSize size) {
|
||||||
CLLocationDistance metersTall = eyeAltitude * 2 * std::tan(MLNRadiansFromDegrees(MLNAngularFieldOfView) / 2.);
|
CLLocationDistance eyeAltitude =
|
||||||
|
altitude / std::sin(M_PI_2 - MLNRadiansFromDegrees(pitch)) * std::sin(M_PI_2);
|
||||||
|
CLLocationDistance metersTall =
|
||||||
|
eyeAltitude * 2 * std::tan(MLNRadiansFromDegrees(MLNAngularFieldOfView) / 2.);
|
||||||
CLLocationDistance metersPerPixel = metersTall / size.height;
|
CLLocationDistance metersPerPixel = metersTall / size.height;
|
||||||
CGFloat mapPixelWidthAtZoom = std::cos(MLNRadiansFromDegrees(latitude)) * mbgl::util::M2PI * mbgl::util::EARTH_RADIUS_M / metersPerPixel;
|
CGFloat mapPixelWidthAtZoom = std::cos(MLNRadiansFromDegrees(latitude)) * mbgl::util::M2PI *
|
||||||
|
mbgl::util::EARTH_RADIUS_M / metersPerPixel;
|
||||||
return ::log2(mapPixelWidthAtZoom / mbgl::util::tileSize_D);
|
return ::log2(mapPixelWidthAtZoom / mbgl::util::tileSize_D);
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNRadianDistance MLNDistanceBetweenRadianCoordinates(MLNRadianCoordinate2D from, MLNRadianCoordinate2D to) {
|
MLNRadianDistance MLNDistanceBetweenRadianCoordinates(MLNRadianCoordinate2D from,
|
||||||
double a = pow(sin((to.latitude - from.latitude) / 2), 2)
|
MLNRadianCoordinate2D to) {
|
||||||
+ pow(sin((to.longitude - from.longitude) / 2), 2) * cos(from.latitude) * cos(to.latitude);
|
double a =
|
||||||
|
pow(sin((to.latitude - from.latitude) / 2), 2) +
|
||||||
|
pow(sin((to.longitude - from.longitude) / 2), 2) * cos(from.latitude) * cos(to.latitude);
|
||||||
|
|
||||||
return 2 * atan2(sqrt(a), sqrt(1 - a));
|
return 2 * atan2(sqrt(a), sqrt(1 - a));
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNRadianDirection MLNRadianCoordinatesDirection(MLNRadianCoordinate2D from, MLNRadianCoordinate2D to) {
|
MLNRadianDirection MLNRadianCoordinatesDirection(MLNRadianCoordinate2D from,
|
||||||
|
MLNRadianCoordinate2D to) {
|
||||||
double a = sin(to.longitude - from.longitude) * cos(to.latitude);
|
double a = sin(to.longitude - from.longitude) * cos(to.latitude);
|
||||||
double b = cos(from.latitude) * sin(to.latitude)
|
double b = cos(from.latitude) * sin(to.latitude) -
|
||||||
- sin(from.latitude) * cos(to.latitude) * cos(to.longitude - from.longitude);
|
sin(from.latitude) * cos(to.latitude) * cos(to.longitude - from.longitude);
|
||||||
return atan2(a, b);
|
return atan2(a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNRadianCoordinate2D MLNRadianCoordinateAtDistanceFacingDirection(MLNRadianCoordinate2D coordinate,
|
MLNRadianCoordinate2D MLNRadianCoordinateAtDistanceFacingDirection(MLNRadianCoordinate2D coordinate,
|
||||||
MLNRadianDistance distance,
|
MLNRadianDistance distance,
|
||||||
MLNRadianDirection direction) {
|
MLNRadianDirection direction) {
|
||||||
double otherLatitude = asin(sin(coordinate.latitude) * cos(distance)
|
double otherLatitude = asin(sin(coordinate.latitude) * cos(distance) +
|
||||||
+ cos(coordinate.latitude) * sin(distance) * cos(direction));
|
cos(coordinate.latitude) * sin(distance) * cos(direction));
|
||||||
double otherLongitude = coordinate.longitude + atan2(sin(direction) * sin(distance) * cos(coordinate.latitude),
|
double otherLongitude =
|
||||||
|
coordinate.longitude + atan2(sin(direction) * sin(distance) * cos(coordinate.latitude),
|
||||||
cos(distance) - sin(coordinate.latitude) * sin(otherLatitude));
|
cos(distance) - sin(coordinate.latitude) * sin(otherLatitude));
|
||||||
return MLNRadianCoordinate2DMake(otherLatitude, otherLongitude);
|
return MLNRadianCoordinate2DMake(otherLatitude, otherLongitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
CLLocationDirection MLNDirectionBetweenCoordinates(CLLocationCoordinate2D firstCoordinate, CLLocationCoordinate2D secondCoordinate) {
|
CLLocationDirection MLNDirectionBetweenCoordinates(CLLocationCoordinate2D firstCoordinate,
|
||||||
if (MLNEqualFloatWithAccuracy(firstCoordinate.latitude, secondCoordinate.latitude, 1e-8) && MLNEqualFloatWithAccuracy(firstCoordinate.longitude, secondCoordinate.longitude, 1e-8)) {
|
CLLocationCoordinate2D secondCoordinate) {
|
||||||
|
if (MLNEqualFloatWithAccuracy(firstCoordinate.latitude, secondCoordinate.latitude, 1e-8) &&
|
||||||
|
MLNEqualFloatWithAccuracy(firstCoordinate.longitude, secondCoordinate.longitude, 1e-8)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ported from
|
||||||
|
// https://github.com/mapbox/turf-swift/blob/857e2e8060678ef4a7a9169d4971b0788fdffc37/Turf/Turf.swift#L23-L31
|
||||||
|
MLNRadianCoordinate2D firstRadianCoordinate =
|
||||||
|
MLNRadianCoordinateFromLocationCoordinate(firstCoordinate);
|
||||||
|
MLNRadianCoordinate2D secondRadianCoordinate =
|
||||||
|
MLNRadianCoordinateFromLocationCoordinate(secondCoordinate);
|
||||||
|
|
||||||
// Ported from https://github.com/mapbox/turf-swift/blob/857e2e8060678ef4a7a9169d4971b0788fdffc37/Turf/Turf.swift#L23-L31
|
CGFloat a = sin(secondRadianCoordinate.longitude - firstRadianCoordinate.longitude) *
|
||||||
MLNRadianCoordinate2D firstRadianCoordinate = MLNRadianCoordinateFromLocationCoordinate(firstCoordinate);
|
cos(secondRadianCoordinate.latitude);
|
||||||
MLNRadianCoordinate2D secondRadianCoordinate = MLNRadianCoordinateFromLocationCoordinate(secondCoordinate);
|
CGFloat b = (cos(firstRadianCoordinate.latitude) * sin(secondRadianCoordinate.latitude) -
|
||||||
|
sin(firstRadianCoordinate.latitude) * cos(secondRadianCoordinate.latitude) *
|
||||||
CGFloat a = sin(secondRadianCoordinate.longitude - firstRadianCoordinate.longitude) * cos(secondRadianCoordinate.latitude);
|
cos(secondRadianCoordinate.longitude - firstRadianCoordinate.longitude));
|
||||||
CGFloat b = (cos(firstRadianCoordinate.latitude) * sin(secondRadianCoordinate.latitude)
|
|
||||||
- sin(firstRadianCoordinate.latitude) * cos(secondRadianCoordinate.latitude) * cos(secondRadianCoordinate.longitude - firstRadianCoordinate.longitude));
|
|
||||||
MLNRadianDirection radianDirection = atan2(a, b);
|
MLNRadianDirection radianDirection = atan2(a, b);
|
||||||
return radianDirection * 180 / M_PI;
|
return radianDirection * 180 / M_PI;
|
||||||
}
|
}
|
||||||
@@ -107,25 +124,36 @@ CGPoint MLNPointRounded(CGPoint point) {
|
|||||||
#elif TARGET_OS_MAC
|
#elif TARGET_OS_MAC
|
||||||
CGFloat scaleFactor = [NSScreen mainScreen].backingScaleFactor;
|
CGFloat scaleFactor = [NSScreen mainScreen].backingScaleFactor;
|
||||||
#endif
|
#endif
|
||||||
return CGPointMake(round(point.x * scaleFactor) / scaleFactor, round(point.y * scaleFactor) / scaleFactor);
|
return CGPointMake(round(point.x * scaleFactor) / scaleFactor,
|
||||||
|
round(point.y * scaleFactor) / scaleFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNMapPoint MLNMapPointForCoordinate(CLLocationCoordinate2D coordinate, double zoomLevel) {
|
MLNMapPoint MLNMapPointForCoordinate(CLLocationCoordinate2D coordinate, double zoomLevel) {
|
||||||
mbgl::Point<double> projectedCoordinate = mbgl::Projection::project(MLNLatLngFromLocationCoordinate2D(coordinate), std::pow(2.0, zoomLevel));
|
mbgl::Point<double> projectedCoordinate = mbgl::Projection::project(
|
||||||
|
MLNLatLngFromLocationCoordinate2D(coordinate), std::pow(2.0, zoomLevel));
|
||||||
return MLNMapPointMake(projectedCoordinate.x, projectedCoordinate.y, zoomLevel);
|
return MLNMapPointMake(projectedCoordinate.x, projectedCoordinate.y, zoomLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNMatrix4 MLNMatrix4Make(std::array<double, 16> array) {
|
MLNMatrix4 MLNMatrix4Make(std::array<double, 16> array) {
|
||||||
MLNMatrix4 mat4 = {
|
MLNMatrix4 mat4 = {.m00 = array[0],
|
||||||
.m00 = array[0], .m01 = array[1], .m02 = array[2], .m03 = array[3],
|
.m01 = array[1],
|
||||||
.m10 = array[4], .m11 = array[5], .m12 = array[6], .m13 = array[7],
|
.m02 = array[2],
|
||||||
.m20 = array[8], .m21 = array[9], .m22 = array[10], .m23 = array[11],
|
.m03 = array[3],
|
||||||
.m30 = array[12], .m31 = array[13], .m32 = array[14], .m33 = array[15]
|
.m10 = array[4],
|
||||||
};
|
.m11 = array[5],
|
||||||
|
.m12 = array[6],
|
||||||
|
.m13 = array[7],
|
||||||
|
.m20 = array[8],
|
||||||
|
.m21 = array[9],
|
||||||
|
.m22 = array[10],
|
||||||
|
.m23 = array[11],
|
||||||
|
.m30 = array[12],
|
||||||
|
.m31 = array[13],
|
||||||
|
.m32 = array[14],
|
||||||
|
.m33 = array[15]};
|
||||||
return mat4;
|
return mat4;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL MLNEqualFloatWithAccuracy(CGFloat left, CGFloat right, CGFloat accuracy)
|
BOOL MLNEqualFloatWithAccuracy(CGFloat left, CGFloat right, CGFloat accuracy) {
|
||||||
{
|
|
||||||
return MAX(left, right) - MIN(left, right) <= accuracy;
|
return MAX(left, right) - MIN(left, right) <= accuracy;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,17 @@
|
|||||||
#import "MLNTileSource_Private.h"
|
#import "MLNTileSource_Private.h"
|
||||||
#import "NSURL+MLNAdditions.h"
|
#import "NSURL+MLNAdditions.h"
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
#import "UIImage+MLNAdditions.h"
|
#import "UIImage+MLNAdditions.h"
|
||||||
#else
|
#else
|
||||||
#import "NSImage+MLNAdditions.h"
|
#import "NSImage+MLNAdditions.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <mbgl/style/sources/image_source.hpp>
|
#include <mbgl/style/sources/image_source.hpp>
|
||||||
#include <mbgl/util/premultiply.hpp>
|
#include <mbgl/util/premultiply.hpp>
|
||||||
|
|
||||||
@interface MLNImageSource ()
|
@interface MLNImageSource ()
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier coordinateQuad:(MLNCoordinateQuad)coordinateQuad NS_DESIGNATED_INITIALIZER;
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
|
coordinateQuad:(MLNCoordinateQuad)coordinateQuad NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
@property (nonatomic, readonly) mbgl::style::ImageSource *rawSource;
|
@property (nonatomic, readonly) mbgl::style::ImageSource *rawSource;
|
||||||
|
|
||||||
@@ -23,23 +24,25 @@
|
|||||||
|
|
||||||
@implementation MLNImageSource
|
@implementation MLNImageSource
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier coordinateQuad:(MLNCoordinateQuad)coordinateQuad {
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
|
coordinateQuad:(MLNCoordinateQuad)coordinateQuad {
|
||||||
const auto coordsArray = MLNLatLngArrayFromCoordinateQuad(coordinateQuad);
|
const auto coordsArray = MLNLatLngArrayFromCoordinateQuad(coordinateQuad);
|
||||||
auto source = std::make_unique<mbgl::style::ImageSource>(identifier.UTF8String, coordsArray);
|
auto source = std::make_unique<mbgl::style::ImageSource>(identifier.UTF8String, coordsArray);
|
||||||
return self = [super initWithPendingSource:std::move(source)];
|
return self = [super initWithPendingSource:std::move(source)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier coordinateQuad:(MLNCoordinateQuad)coordinateQuad URL:(NSURL *)url {
|
coordinateQuad:(MLNCoordinateQuad)coordinateQuad
|
||||||
self = [self initWithIdentifier:identifier coordinateQuad: coordinateQuad];
|
URL:(NSURL *)url {
|
||||||
|
self = [self initWithIdentifier:identifier coordinateQuad:coordinateQuad];
|
||||||
self.URL = url;
|
self.URL = url;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier coordinateQuad:(MLNCoordinateQuad)coordinateQuad image:(MLNImage *)image {
|
coordinateQuad:(MLNCoordinateQuad)coordinateQuad
|
||||||
self = [self initWithIdentifier:identifier coordinateQuad: coordinateQuad];
|
image:(MLNImage *)image {
|
||||||
|
self = [self initWithIdentifier:identifier coordinateQuad:coordinateQuad];
|
||||||
self.image = image;
|
self.image = image;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -66,7 +69,7 @@
|
|||||||
if (image != nullptr) {
|
if (image != nullptr) {
|
||||||
self.rawSource->setImage(image.mgl_premultipliedImage);
|
self.rawSource->setImage(image.mgl_premultipliedImage);
|
||||||
} else {
|
} else {
|
||||||
self.rawSource->setImage(mbgl::PremultipliedImage({0,0}));
|
self.rawSource->setImage(mbgl::PremultipliedImage({0, 0}));
|
||||||
}
|
}
|
||||||
_image = image;
|
_image = image;
|
||||||
}
|
}
|
||||||
@@ -76,21 +79,21 @@
|
|||||||
return MLNCoordinateQuadFromLatLngArray(self.rawSource->getCoordinates());
|
return MLNCoordinateQuadFromLatLngArray(self.rawSource->getCoordinates());
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setCoordinates: (MLNCoordinateQuad)coordinateQuad {
|
- (void)setCoordinates:(MLNCoordinateQuad)coordinateQuad {
|
||||||
MLNAssertStyleSourceIsValid();
|
MLNAssertStyleSourceIsValid();
|
||||||
self.rawSource->setCoordinates(MLNLatLngArrayFromCoordinateQuad(coordinateQuad));
|
self.rawSource->setCoordinates(MLNLatLngArrayFromCoordinateQuad(coordinateQuad));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description {
|
- (NSString *)description {
|
||||||
if (self.rawSource) {
|
if (self.rawSource) {
|
||||||
return [NSString stringWithFormat:@"<%@: %p; identifier = %@; coordinates = %@; URL = %@; image = %@>",
|
return [NSString
|
||||||
|
stringWithFormat:@"<%@: %p; identifier = %@; coordinates = %@; URL = %@; image = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self, self.identifier,
|
NSStringFromClass([self class]), (void *)self, self.identifier,
|
||||||
MLNStringFromCoordinateQuad(self.coordinates),
|
MLNStringFromCoordinateQuad(self.coordinates), self.URL, self.image];
|
||||||
self.URL,
|
} else {
|
||||||
self.image];
|
return [NSString
|
||||||
}
|
stringWithFormat:
|
||||||
else {
|
@"<%@: %p; identifier = %@; coordinates = <unknown>; URL = <unknown>; image = %@>",
|
||||||
return [NSString stringWithFormat:@"<%@: %p; identifier = %@; coordinates = <unknown>; URL = <unknown>; image = %@>",
|
|
||||||
NSStringFromClass([self class]), (void *)self, self.identifier, self.image];
|
NSStringFromClass([self class]), (void *)self, self.identifier, self.image];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,7 +104,8 @@
|
|||||||
|
|
||||||
- (NSString *)attributionHTMLString {
|
- (NSString *)attributionHTMLString {
|
||||||
if (!self.rawSource) {
|
if (!self.rawSource) {
|
||||||
MLNAssert(0, @"Source with identifier `%@` was invalidated after a style change", self.identifier);
|
MLNAssert(0, @"Source with identifier `%@` was invalidated after a style change",
|
||||||
|
self.identifier);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include <mbgl/util/logging.hpp>
|
|
||||||
#include <mbgl/util/enum.hpp>
|
#include <mbgl/util/enum.hpp>
|
||||||
|
#include <mbgl/util/logging.hpp>
|
||||||
|
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
|
||||||
@@ -10,12 +10,14 @@
|
|||||||
|
|
||||||
namespace mbgl {
|
namespace mbgl {
|
||||||
|
|
||||||
class MLNCoreLoggingObserver : public Log :: Observer {
|
class MLNCoreLoggingObserver : public Log ::Observer {
|
||||||
public:
|
public:
|
||||||
//Return true not print messages at core level, and filter at platform level.
|
// Return true not print messages at core level, and filter at platform level.
|
||||||
bool onRecord(EventSeverity severity, Event event, int64_t code, const std::string& msg) override{
|
bool onRecord(EventSeverity severity, Event event, int64_t code,
|
||||||
|
const std::string &msg) override {
|
||||||
NSString *message = [NSString stringWithFormat:@"[event]:%s [code]:%lld [message]:%@", Enum<Event>::toString(event), code, [NSString stringWithCString:msg.c_str() encoding:NSUTF8StringEncoding]];
|
NSString *message = [NSString
|
||||||
|
stringWithFormat:@"[event]:%s [code]:%lld [message]:%@", Enum<Event>::toString(event), code,
|
||||||
|
[NSString stringWithCString:msg.c_str() encoding:NSUTF8StringEncoding]];
|
||||||
switch (severity) {
|
switch (severity) {
|
||||||
case EventSeverity::Debug:
|
case EventSeverity::Debug:
|
||||||
MLNLogDebug(message);
|
MLNLogDebug(message);
|
||||||
@@ -35,10 +37,9 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace mbgl
|
||||||
|
|
||||||
@implementation MLNLoggingConfiguration
|
@implementation MLNLoggingConfiguration {
|
||||||
{
|
|
||||||
std::unique_ptr<mbgl::MLNCoreLoggingObserver> _coreLoggingObserver;
|
std::unique_ptr<mbgl::MLNCoreLoggingObserver> _coreLoggingObserver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,15 +53,14 @@ public:
|
|||||||
return sharedConfiguration;
|
return sharedConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init{
|
- (id)init {
|
||||||
if(self = [super init]){
|
if (self = [super init]) {
|
||||||
mbgl::Log::setObserver(std::make_unique<mbgl::MLNCoreLoggingObserver>());
|
mbgl::Log::setObserver(std::make_unique<mbgl::MLNCoreLoggingObserver>());
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setHandler:(void (^)(MLNLoggingLevel, NSString *, NSUInteger, NSString *))handler {
|
- (void)setHandler:(void (^)(MLNLoggingLevel, NSString *, NSUInteger, NSString *))handler {
|
||||||
|
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
_handler = [self defaultBlockHandler];
|
_handler = [self defaultBlockHandler];
|
||||||
} else {
|
} else {
|
||||||
@@ -68,19 +68,21 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)logCallingFunction:(const char *)callingFunction functionLine:(NSUInteger)functionLine messageType:(MLNLoggingLevel)type format:(id)messageFormat, ... {
|
- (void)logCallingFunction:(const char *)callingFunction
|
||||||
|
functionLine:(NSUInteger)functionLine
|
||||||
|
messageType:(MLNLoggingLevel)type
|
||||||
|
format:(id)messageFormat, ... {
|
||||||
va_list formatList;
|
va_list formatList;
|
||||||
va_start(formatList, messageFormat);
|
va_start(formatList, messageFormat);
|
||||||
NSString *formattedMessage = [[NSString alloc] initWithFormat:messageFormat arguments:formatList];
|
NSString *formattedMessage = [[NSString alloc] initWithFormat:messageFormat arguments:formatList];
|
||||||
va_end(formatList);
|
va_end(formatList);
|
||||||
|
|
||||||
_handler(type, @(callingFunction), functionLine, formattedMessage);
|
_handler(type, @(callingFunction), functionLine, formattedMessage);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MLNLoggingBlockHandler)defaultBlockHandler {
|
- (MLNLoggingBlockHandler)defaultBlockHandler {
|
||||||
MLNLoggingBlockHandler maplibreHandler = ^(MLNLoggingLevel level, NSString *fileName, NSUInteger line, NSString *message) {
|
MLNLoggingBlockHandler maplibreHandler =
|
||||||
|
^(MLNLoggingLevel level, NSString *fileName, NSUInteger line, NSString *message) {
|
||||||
if (@available(iOS 10.0, macOS 10.12.0, *)) {
|
if (@available(iOS 10.0, macOS 10.12.0, *)) {
|
||||||
static dispatch_once_t once;
|
static dispatch_once_t once;
|
||||||
static os_log_t info_log;
|
static os_log_t info_log;
|
||||||
@@ -89,15 +91,13 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
static os_log_t error_log;
|
static os_log_t error_log;
|
||||||
static os_log_t fault_log;
|
static os_log_t fault_log;
|
||||||
static os_log_type_t log_types[] = { OS_LOG_TYPE_DEFAULT,
|
static os_log_type_t log_types[] = {OS_LOG_TYPE_DEFAULT, OS_LOG_TYPE_INFO,
|
||||||
OS_LOG_TYPE_INFO,
|
|
||||||
#if MLN_LOGGING_ENABLE_DEBUG
|
#if MLN_LOGGING_ENABLE_DEBUG
|
||||||
OS_LOG_TYPE_DEBUG,
|
OS_LOG_TYPE_DEBUG,
|
||||||
#endif
|
#endif
|
||||||
OS_LOG_TYPE_ERROR,
|
OS_LOG_TYPE_ERROR, OS_LOG_TYPE_FAULT};
|
||||||
OS_LOG_TYPE_FAULT };
|
constexpr const char *const subsystem = "org.maplibre.Native";
|
||||||
constexpr const char* const subsystem = "org.maplibre.Native";
|
dispatch_once(&once, ^{
|
||||||
dispatch_once(&once, ^ {
|
|
||||||
info_log = os_log_create(subsystem, "INFO");
|
info_log = os_log_create(subsystem, "INFO");
|
||||||
#if MLN_LOGGING_ENABLE_DEBUG
|
#if MLN_LOGGING_ENABLE_DEBUG
|
||||||
debug_log = os_log_create(subsystem, "DEBUG");
|
debug_log = os_log_create(subsystem, "DEBUG");
|
||||||
@@ -129,7 +129,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
os_log_type_t logType = log_types[level];
|
os_log_type_t logType = log_types[level];
|
||||||
os_log_with_type(maplibre_log, logType, "%@ - %lu: %@", fileName, (unsigned long)line, message);
|
os_log_with_type(maplibre_log, logType, "%@ - %lu: %@", fileName, (unsigned long)line,
|
||||||
|
message);
|
||||||
} else {
|
} else {
|
||||||
NSString *category;
|
NSString *category;
|
||||||
switch (level) {
|
switch (level) {
|
||||||
|
|||||||
@@ -8,23 +8,21 @@
|
|||||||
|
|
||||||
@implementation MLNMapCamera
|
@implementation MLNMapCamera
|
||||||
|
|
||||||
+ (BOOL)supportsSecureCoding
|
+ (BOOL)supportsSecureCoding {
|
||||||
{
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (instancetype)camera
|
+ (instancetype)camera {
|
||||||
{
|
|
||||||
return [[self alloc] init];
|
return [[self alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
|
+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
|
||||||
fromEyeCoordinate:(CLLocationCoordinate2D)eyeCoordinate
|
fromEyeCoordinate:(CLLocationCoordinate2D)eyeCoordinate
|
||||||
eyeAltitude:(CLLocationDistance)eyeAltitude
|
eyeAltitude:(CLLocationDistance)eyeAltitude {
|
||||||
{
|
|
||||||
CLLocationDirection heading = -1;
|
CLLocationDirection heading = -1;
|
||||||
CGFloat pitch = -1;
|
CGFloat pitch = -1;
|
||||||
if (CLLocationCoordinate2DIsValid(centerCoordinate) && CLLocationCoordinate2DIsValid(eyeCoordinate)) {
|
if (CLLocationCoordinate2DIsValid(centerCoordinate) &&
|
||||||
|
CLLocationCoordinate2DIsValid(eyeCoordinate)) {
|
||||||
heading = MLNDirectionBetweenCoordinates(eyeCoordinate, centerCoordinate);
|
heading = MLNDirectionBetweenCoordinates(eyeCoordinate, centerCoordinate);
|
||||||
|
|
||||||
CLLocation *centerLocation = [[CLLocation alloc] initWithLatitude:centerCoordinate.latitude
|
CLLocation *centerLocation = [[CLLocation alloc] initWithLatitude:centerCoordinate.latitude
|
||||||
@@ -45,8 +43,7 @@
|
|||||||
+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
|
+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
|
||||||
acrossDistance:(CLLocationDistance)distance
|
acrossDistance:(CLLocationDistance)distance
|
||||||
pitch:(CGFloat)pitch
|
pitch:(CGFloat)pitch
|
||||||
heading:(CLLocationDirection)heading
|
heading:(CLLocationDirection)heading {
|
||||||
{
|
|
||||||
// Angle at the viewpoint formed by the straight lines running perpendicular
|
// Angle at the viewpoint formed by the straight lines running perpendicular
|
||||||
// to the ground and toward the center coordinate.
|
// to the ground and toward the center coordinate.
|
||||||
CLLocationDirection eyeAngle = 90 - pitch;
|
CLLocationDirection eyeAngle = 90 - pitch;
|
||||||
@@ -61,8 +58,7 @@
|
|||||||
+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
|
+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
|
||||||
altitude:(CLLocationDistance)altitude
|
altitude:(CLLocationDistance)altitude
|
||||||
pitch:(CGFloat)pitch
|
pitch:(CGFloat)pitch
|
||||||
heading:(CLLocationDirection)heading
|
heading:(CLLocationDirection)heading {
|
||||||
{
|
|
||||||
return [[self alloc] initWithCenterCoordinate:centerCoordinate
|
return [[self alloc] initWithCenterCoordinate:centerCoordinate
|
||||||
altitude:altitude
|
altitude:altitude
|
||||||
pitch:pitch
|
pitch:pitch
|
||||||
@@ -72,9 +68,9 @@
|
|||||||
+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
|
+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
|
||||||
fromDistance:(CLLocationDistance)distance
|
fromDistance:(CLLocationDistance)distance
|
||||||
pitch:(CGFloat)pitch
|
pitch:(CGFloat)pitch
|
||||||
heading:(CLLocationDirection)heading
|
heading:(CLLocationDirection)heading {
|
||||||
{
|
// TODO: Remove this compatibility shim in favor of
|
||||||
// TODO: Remove this compatibility shim in favor of `-cameraLookingAtCenterCoordinate:acrossDistance:pitch:heading:.
|
// `-cameraLookingAtCenterCoordinate:acrossDistance:pitch:heading:.
|
||||||
// https://github.com/mapbox/mapbox-gl-native/issues/12943
|
// https://github.com/mapbox/mapbox-gl-native/issues/12943
|
||||||
return [MLNMapCamera cameraLookingAtCenterCoordinate:centerCoordinate
|
return [MLNMapCamera cameraLookingAtCenterCoordinate:centerCoordinate
|
||||||
altitude:distance
|
altitude:distance
|
||||||
@@ -85,11 +81,10 @@
|
|||||||
- (instancetype)initWithCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
|
- (instancetype)initWithCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
|
||||||
altitude:(CLLocationDistance)altitude
|
altitude:(CLLocationDistance)altitude
|
||||||
pitch:(CGFloat)pitch
|
pitch:(CGFloat)pitch
|
||||||
heading:(CLLocationDirection)heading
|
heading:(CLLocationDirection)heading {
|
||||||
{
|
MLNLogDebug(@"Initializing withCenterCoordinate: %@ altitude: %.0fm pitch: %f° heading: %f°",
|
||||||
MLNLogDebug(@"Initializing withCenterCoordinate: %@ altitude: %.0fm pitch: %f° heading: %f°", MLNStringFromCLLocationCoordinate2D(centerCoordinate), altitude, pitch, heading);
|
MLNStringFromCLLocationCoordinate2D(centerCoordinate), altitude, pitch, heading);
|
||||||
if (self = [super init])
|
if (self = [super init]) {
|
||||||
{
|
|
||||||
_centerCoordinate = centerCoordinate;
|
_centerCoordinate = centerCoordinate;
|
||||||
_altitude = altitude;
|
_altitude = altitude;
|
||||||
_pitch = pitch;
|
_pitch = pitch;
|
||||||
@@ -99,11 +94,9 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (nullable instancetype)initWithCoder:(NSCoder *)coder
|
- (nullable instancetype)initWithCoder:(NSCoder *)coder {
|
||||||
{
|
|
||||||
MLNLogInfo(@"Initialiazing with coder.");
|
MLNLogInfo(@"Initialiazing with coder.");
|
||||||
if (self = [super init])
|
if (self = [super init]) {
|
||||||
{
|
|
||||||
_centerCoordinate = CLLocationCoordinate2DMake([coder decodeDoubleForKey:@"centerLatitude"],
|
_centerCoordinate = CLLocationCoordinate2DMake([coder decodeDoubleForKey:@"centerLatitude"],
|
||||||
[coder decodeDoubleForKey:@"centerLongitude"]);
|
[coder decodeDoubleForKey:@"centerLongitude"]);
|
||||||
_altitude = [coder decodeDoubleForKey:@"altitude"];
|
_altitude = [coder decodeDoubleForKey:@"altitude"];
|
||||||
@@ -114,8 +107,7 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)encodeWithCoder:(NSCoder *)coder
|
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||||
{
|
|
||||||
[coder encodeDouble:_centerCoordinate.latitude forKey:@"centerLatitude"];
|
[coder encodeDouble:_centerCoordinate.latitude forKey:@"centerLatitude"];
|
||||||
[coder encodeDouble:_centerCoordinate.longitude forKey:@"centerLongitude"];
|
[coder encodeDouble:_centerCoordinate.longitude forKey:@"centerLongitude"];
|
||||||
[coder encodeDouble:_altitude forKey:@"altitude"];
|
[coder encodeDouble:_altitude forKey:@"altitude"];
|
||||||
@@ -124,8 +116,7 @@
|
|||||||
[coder encodeDouble:_roll forKey:@"roll"];
|
[coder encodeDouble:_roll forKey:@"roll"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)copyWithZone:(nullable NSZone *)zone
|
- (id)copyWithZone:(nullable NSZone *)zone {
|
||||||
{
|
|
||||||
MLNMapCamera *copy = [[[self class] allocWithZone:zone] initWithCenterCoordinate:_centerCoordinate
|
MLNMapCamera *copy = [[[self class] allocWithZone:zone] initWithCenterCoordinate:_centerCoordinate
|
||||||
altitude:_altitude
|
altitude:_altitude
|
||||||
pitch:_pitch
|
pitch:_pitch
|
||||||
@@ -149,50 +140,47 @@
|
|||||||
self.altitude = distance * sin(MLNRadiansFromDegrees(eyeAngle));
|
self.altitude = distance * sin(MLNRadiansFromDegrees(eyeAngle));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
return [NSString stringWithFormat:@"<%@: %p; centerCoordinate = %f, %f; altitude = %.0fm; "
|
||||||
return [NSString stringWithFormat:@"<%@: %p; centerCoordinate = %f, %f; altitude = %.0fm; heading = %.0f°; pitch = %.0f°; roll = %.0f°>",
|
@"heading = %.0f°; pitch = %.0f°; roll = %.0f°>",
|
||||||
NSStringFromClass([self class]), (void *)self, _centerCoordinate.latitude, _centerCoordinate.longitude, _altitude, _heading, _pitch, _roll];
|
NSStringFromClass([self class]), (void *)self,
|
||||||
|
_centerCoordinate.latitude, _centerCoordinate.longitude,
|
||||||
|
_altitude, _heading, _pitch, _roll];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)other
|
- (BOOL)isEqual:(id)other {
|
||||||
{
|
if (![other isKindOfClass:[self class]]) {
|
||||||
if ( ! [other isKindOfClass:[self class]])
|
|
||||||
{
|
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
if (other == self)
|
if (other == self) {
|
||||||
{
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNMapCamera *otherCamera = other;
|
MLNMapCamera *otherCamera = other;
|
||||||
return (_centerCoordinate.latitude == otherCamera.centerCoordinate.latitude
|
return (_centerCoordinate.latitude == otherCamera.centerCoordinate.latitude &&
|
||||||
&& _centerCoordinate.longitude == otherCamera.centerCoordinate.longitude
|
_centerCoordinate.longitude == otherCamera.centerCoordinate.longitude &&
|
||||||
&& _altitude == otherCamera.altitude
|
_altitude == otherCamera.altitude && _pitch == otherCamera.pitch &&
|
||||||
&& _pitch == otherCamera.pitch && _heading == otherCamera.heading
|
_heading == otherCamera.heading && _roll == otherCamera.roll);
|
||||||
&& _roll == otherCamera.roll);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEqualToMapCamera:(MLNMapCamera *)otherCamera
|
- (BOOL)isEqualToMapCamera:(MLNMapCamera *)otherCamera {
|
||||||
{
|
if (otherCamera == self) {
|
||||||
if (otherCamera == self)
|
|
||||||
{
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (MLNEqualFloatWithAccuracy(_centerCoordinate.latitude, otherCamera.centerCoordinate.latitude, 1e-8)
|
return (MLNEqualFloatWithAccuracy(_centerCoordinate.latitude,
|
||||||
&& MLNEqualFloatWithAccuracy(_centerCoordinate.longitude, otherCamera.centerCoordinate.longitude, 1e-8)
|
otherCamera.centerCoordinate.latitude, 1e-8) &&
|
||||||
&& MLNEqualFloatWithAccuracy(_altitude, otherCamera.altitude, 1e-6)
|
MLNEqualFloatWithAccuracy(_centerCoordinate.longitude,
|
||||||
&& MLNEqualFloatWithAccuracy(_pitch, otherCamera.pitch, 1e-2)
|
otherCamera.centerCoordinate.longitude, 1e-8) &&
|
||||||
&& MLNEqualFloatWithAccuracy(_heading, otherCamera.heading, 1e-2)
|
MLNEqualFloatWithAccuracy(_altitude, otherCamera.altitude, 1e-6) &&
|
||||||
&& MLNEqualFloatWithAccuracy(_roll, otherCamera.roll, 1e-2));
|
MLNEqualFloatWithAccuracy(_pitch, otherCamera.pitch, 1e-2) &&
|
||||||
|
MLNEqualFloatWithAccuracy(_heading, otherCamera.heading, 1e-2) &&
|
||||||
|
MLNEqualFloatWithAccuracy(_roll, otherCamera.roll, 1e-2));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)hash
|
- (NSUInteger)hash {
|
||||||
{
|
return (@(_centerCoordinate.latitude).hash + @(_centerCoordinate.longitude).hash +
|
||||||
return (@(_centerCoordinate.latitude).hash + @(_centerCoordinate.longitude).hash
|
@(_altitude).hash + @(_pitch).hash + @(_heading).hash + @(_roll).hash);
|
||||||
+ @(_altitude).hash + @(_pitch).hash + @(_heading).hash + @(_roll).hash);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -6,11 +6,9 @@
|
|||||||
|
|
||||||
@implementation MLNMapOptions
|
@implementation MLNMapOptions
|
||||||
|
|
||||||
-(instancetype _Nonnull)init
|
- (instancetype _Nonnull)init {
|
||||||
{
|
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self)
|
if (self) {
|
||||||
{
|
|
||||||
_styleURL = nil;
|
_styleURL = nil;
|
||||||
_styleJSON = nil;
|
_styleJSON = nil;
|
||||||
_pluginLayers = nil;
|
_pluginLayers = nil;
|
||||||
|
|||||||
@@ -2,80 +2,100 @@
|
|||||||
|
|
||||||
#import <mbgl/actor/actor.hpp>
|
#import <mbgl/actor/actor.hpp>
|
||||||
#import <mbgl/actor/scheduler.hpp>
|
#import <mbgl/actor/scheduler.hpp>
|
||||||
#import <mbgl/util/geo.hpp>
|
#import <mbgl/map/camera.hpp>
|
||||||
#import <mbgl/map/map_options.hpp>
|
#import <mbgl/map/map_options.hpp>
|
||||||
#import <mbgl/map/map_snapshotter.hpp>
|
#import <mbgl/map/map_snapshotter.hpp>
|
||||||
#import <mbgl/map/camera.hpp>
|
|
||||||
#import <mbgl/storage/resource_options.hpp>
|
#import <mbgl/storage/resource_options.hpp>
|
||||||
#import <mbgl/util/client_options.hpp>
|
#import <mbgl/util/client_options.hpp>
|
||||||
|
#import <mbgl/util/geo.hpp>
|
||||||
#import <mbgl/util/string.hpp>
|
#import <mbgl/util/string.hpp>
|
||||||
|
|
||||||
#import "MLNOfflineStorage_Private.h"
|
#import "MLNAnnotationImage_Private.h"
|
||||||
#import "MLNGeometry_Private.h"
|
|
||||||
#import "MLNStyle_Private.h"
|
|
||||||
#import "MLNAttributionInfo_Private.h"
|
#import "MLNAttributionInfo_Private.h"
|
||||||
|
#import "MLNGeometry_Private.h"
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
#import "MLNRendererConfiguration.h"
|
|
||||||
#import "MLNMapSnapshotter_Private.h"
|
#import "MLNMapSnapshotter_Private.h"
|
||||||
|
#import "MLNMultiPoint_Private.h"
|
||||||
|
#import "MLNOfflineStorage_Private.h"
|
||||||
|
#import "MLNPointCollection.h"
|
||||||
|
#import "MLNPolygon.h"
|
||||||
|
#import "MLNPolyline.h"
|
||||||
|
#import "MLNRendererConfiguration.h"
|
||||||
#import "MLNSettings_Private.h"
|
#import "MLNSettings_Private.h"
|
||||||
#import "MLNShape.h"
|
#import "MLNShape.h"
|
||||||
#import "MLNPolyline.h"
|
|
||||||
#import "MLNPolygon.h"
|
|
||||||
#import "MLNMultiPoint_Private.h"
|
|
||||||
#import "MLNShapeCollection.h"
|
#import "MLNShapeCollection.h"
|
||||||
#import "MLNPointCollection.h"
|
#import "MLNStyle_Private.h"
|
||||||
#import "MLNAnnotationImage_Private.h"
|
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
#import "UIImage+MLNAdditions.h"
|
|
||||||
#import "UIColor+MLNAdditions.h"
|
#import "UIColor+MLNAdditions.h"
|
||||||
|
#import "UIImage+MLNAdditions.h"
|
||||||
#else
|
#else
|
||||||
#import "NSImage+MLNAdditions.h"
|
|
||||||
#import "NSColor+MLNAdditions.h"
|
|
||||||
#import <CoreGraphics/CoreGraphics.h>
|
#import <CoreGraphics/CoreGraphics.h>
|
||||||
#import <CoreImage/CIContext.h>
|
#import <CoreImage/CIContext.h>
|
||||||
#import <CoreImage/CIFilter.h>
|
#import <CoreImage/CIFilter.h>
|
||||||
#import <QuartzCore/QuartzCore.h>
|
#import <QuartzCore/QuartzCore.h>
|
||||||
|
#import "NSColor+MLNAdditions.h"
|
||||||
|
#import "NSImage+MLNAdditions.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#import "NSBundle+MLNAdditions.h"
|
#import "NSBundle+MLNAdditions.h"
|
||||||
|
|
||||||
const CGPoint MLNLogoImagePosition = CGPointMake(8, 8);
|
const CGPoint MLNLogoImagePosition = CGPointMake(8, 8);
|
||||||
const CGFloat MLNSnapshotterMinimumPixelSize = 64;
|
const CGFloat MLNSnapshotterMinimumPixelSize = 64;
|
||||||
NSString * const MLNSnapshotterAnnotationSpritePrefix = @"org.maplibre.sprites.";
|
NSString *const MLNSnapshotterAnnotationSpritePrefix = @"org.maplibre.sprites.";
|
||||||
|
|
||||||
MLNImage *MLNAttributedSnapshot(mbgl::MapSnapshotter::Attributions attributions, MLNImage *mglImage, mbgl::MapSnapshotter::PointForFn pointForFn, mbgl::MapSnapshotter::LatLngForFn latLngForFn, MLNMapSnapshotOptions *options, MLNMapSnapshotOverlayHandler overlayHandler);
|
MLNImage *MLNAttributedSnapshot(mbgl::MapSnapshotter::Attributions attributions, MLNImage *mglImage,
|
||||||
MLNMapSnapshot *MLNSnapshotWithDecoratedImage(MLNImage *mglImage, MLNMapSnapshotOptions *options, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn, mbgl::MapSnapshotter::LatLngForFn latLngForFn, MLNMapSnapshotOverlayHandler overlayHandler, NSError * _Nullable *outError);
|
mbgl::MapSnapshotter::PointForFn pointForFn,
|
||||||
NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnapshotter::Attributions attributions);
|
mbgl::MapSnapshotter::LatLngForFn latLngForFn,
|
||||||
|
MLNMapSnapshotOptions *options,
|
||||||
|
MLNMapSnapshotOverlayHandler overlayHandler);
|
||||||
|
MLNMapSnapshot *MLNSnapshotWithDecoratedImage(MLNImage *mglImage, MLNMapSnapshotOptions *options,
|
||||||
|
mbgl::MapSnapshotter::Attributions attributions,
|
||||||
|
mbgl::MapSnapshotter::PointForFn pointForFn,
|
||||||
|
mbgl::MapSnapshotter::LatLngForFn latLngForFn,
|
||||||
|
MLNMapSnapshotOverlayHandler overlayHandler,
|
||||||
|
NSError *_Nullable *outError);
|
||||||
|
NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(
|
||||||
|
mbgl::MapSnapshotter::Attributions attributions);
|
||||||
|
|
||||||
class MLNMapSnapshotterDelegateHost: public mbgl::MapSnapshotterObserver {
|
class MLNMapSnapshotterDelegateHost : public mbgl::MapSnapshotterObserver {
|
||||||
public:
|
public:
|
||||||
MLNMapSnapshotterDelegateHost(MLNMapSnapshotter *snapshotter_) : snapshotter(snapshotter_) {}
|
MLNMapSnapshotterDelegateHost(MLNMapSnapshotter *snapshotter_) : snapshotter(snapshotter_) {}
|
||||||
|
|
||||||
void onDidFailLoadingStyle(const std::string& errorMessage) {
|
void onDidFailLoadingStyle(const std::string &errorMessage) {
|
||||||
MLNMapSnapshotter *strongSnapshotter = snapshotter;
|
MLNMapSnapshotter *strongSnapshotter = snapshotter;
|
||||||
if ([strongSnapshotter.delegate respondsToSelector:@selector(mapSnapshotterDidFail:withError:)]) {
|
if ([strongSnapshotter.delegate respondsToSelector:@selector(mapSnapshotterDidFail:
|
||||||
|
withError:)]) {
|
||||||
NSString *description = @(errorMessage.c_str());
|
NSString *description = @(errorMessage.c_str());
|
||||||
NSDictionary *userInfo = @{
|
NSDictionary *userInfo = @{
|
||||||
NSLocalizedDescriptionKey: NSLocalizedStringWithDefaultValue(@"SNAPSHOT_LOAD_STYLE_FAILED_DESC", nil, nil, @"The snapshot failed because the style can’t be loaded.", @"User-friendly error description"),
|
NSLocalizedDescriptionKey : NSLocalizedStringWithDefaultValue(
|
||||||
NSLocalizedFailureReasonErrorKey: description,
|
@"SNAPSHOT_LOAD_STYLE_FAILED_DESC", nil, nil,
|
||||||
|
@"The snapshot failed because the style can’t be loaded.",
|
||||||
|
@"User-friendly error description"),
|
||||||
|
NSLocalizedFailureReasonErrorKey : description,
|
||||||
};
|
};
|
||||||
NSError *error = [NSError errorWithDomain:MLNErrorDomain code:MLNErrorCodeLoadStyleFailed userInfo:userInfo];
|
NSError *error = [NSError errorWithDomain:MLNErrorDomain
|
||||||
|
code:MLNErrorCodeLoadStyleFailed
|
||||||
|
userInfo:userInfo];
|
||||||
[strongSnapshotter.delegate mapSnapshotterDidFail:snapshotter withError:error];
|
[strongSnapshotter.delegate mapSnapshotterDidFail:snapshotter withError:error];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDidFinishLoadingStyle() {
|
void onDidFinishLoadingStyle() {
|
||||||
MLNMapSnapshotter *strongSnapshotter = snapshotter;
|
MLNMapSnapshotter *strongSnapshotter = snapshotter;
|
||||||
if ([strongSnapshotter.delegate respondsToSelector:@selector(mapSnapshotter:didFinishLoadingStyle:)]) {
|
if ([strongSnapshotter.delegate respondsToSelector:@selector(mapSnapshotter:
|
||||||
[strongSnapshotter.delegate mapSnapshotter:snapshotter didFinishLoadingStyle:snapshotter.style];
|
didFinishLoadingStyle:)]) {
|
||||||
|
[strongSnapshotter.delegate mapSnapshotter:snapshotter
|
||||||
|
didFinishLoadingStyle:snapshotter.style];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onStyleImageMissing(const std::string& imageName) {
|
void onStyleImageMissing(const std::string &imageName) {
|
||||||
MLNMapSnapshotter *strongSnapshotter = snapshotter;
|
MLNMapSnapshotter *strongSnapshotter = snapshotter;
|
||||||
if ([strongSnapshotter.delegate respondsToSelector:@selector(mapSnapshotter:didFailLoadingImageNamed:)]) {
|
if ([strongSnapshotter.delegate respondsToSelector:@selector(mapSnapshotter:
|
||||||
[strongSnapshotter.delegate mapSnapshotter:snapshotter didFailLoadingImageNamed:@(imageName.c_str())];
|
didFailLoadingImageNamed:)]) {
|
||||||
|
[strongSnapshotter.delegate mapSnapshotter:snapshotter
|
||||||
|
didFailLoadingImageNamed:@(imageName.c_str())];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,9 +103,12 @@ private:
|
|||||||
__weak MLNMapSnapshotter *snapshotter;
|
__weak MLNMapSnapshotter *snapshotter;
|
||||||
};
|
};
|
||||||
|
|
||||||
@interface MLNMapSnapshotOverlay() <MLNMapSnapshotProtocol>
|
@interface MLNMapSnapshotOverlay () <MLNMapSnapshotProtocol>
|
||||||
@property (nonatomic, assign) CGFloat scale;
|
@property (nonatomic, assign) CGFloat scale;
|
||||||
- (instancetype)initWithContext:(CGContextRef)context scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn;
|
- (instancetype)initWithContext:(CGContextRef)context
|
||||||
|
scale:(CGFloat)scale
|
||||||
|
pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn
|
||||||
|
latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -94,7 +117,10 @@ private:
|
|||||||
mbgl::MapSnapshotter::LatLngForFn _latLngForFn;
|
mbgl::MapSnapshotter::LatLngForFn _latLngForFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype) initWithContext:(CGContextRef)context scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn {
|
- (instancetype)initWithContext:(CGContextRef)context
|
||||||
|
scale:(CGFloat)scale
|
||||||
|
pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn
|
||||||
|
latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
_context = context;
|
_context = context;
|
||||||
@@ -108,30 +134,26 @@ private:
|
|||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
|
|
||||||
- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate
|
- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate {
|
||||||
{
|
|
||||||
mbgl::ScreenCoordinate sc = _pointForFn(MLNLatLngFromLocationCoordinate2D(coordinate));
|
mbgl::ScreenCoordinate sc = _pointForFn(MLNLatLngFromLocationCoordinate2D(coordinate));
|
||||||
return CGPointMake(sc.x, sc.y);
|
return CGPointMake(sc.x, sc.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CLLocationCoordinate2D)coordinateForPoint:(CGPoint)point
|
- (CLLocationCoordinate2D)coordinateForPoint:(CGPoint)point {
|
||||||
{
|
|
||||||
mbgl::LatLng latLng = _latLngForFn(mbgl::ScreenCoordinate(point.x, point.y));
|
mbgl::LatLng latLng = _latLngForFn(mbgl::ScreenCoordinate(point.x, point.y));
|
||||||
return MLNLocationCoordinate2DFromLatLng(latLng);
|
return MLNLocationCoordinate2DFromLatLng(latLng);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
- (NSPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate
|
- (NSPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate {
|
||||||
{
|
|
||||||
mbgl::ScreenCoordinate sc = _pointForFn(MLNLatLngFromLocationCoordinate2D(coordinate));
|
mbgl::ScreenCoordinate sc = _pointForFn(MLNLatLngFromLocationCoordinate2D(coordinate));
|
||||||
CGFloat height = ((CGFloat)CGBitmapContextGetHeight(self.context))/self.scale;
|
CGFloat height = ((CGFloat)CGBitmapContextGetHeight(self.context)) / self.scale;
|
||||||
return NSMakePoint(sc.x, height - sc.y);
|
return NSMakePoint(sc.x, height - sc.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CLLocationCoordinate2D)coordinateForPoint:(NSPoint)point
|
- (CLLocationCoordinate2D)coordinateForPoint:(NSPoint)point {
|
||||||
{
|
CGFloat height = ((CGFloat)CGBitmapContextGetHeight(self.context)) / self.scale;
|
||||||
CGFloat height = ((CGFloat)CGBitmapContextGetHeight(self.context))/self.scale;
|
|
||||||
auto screenCoord = mbgl::ScreenCoordinate(point.x, height - point.y);
|
auto screenCoord = mbgl::ScreenCoordinate(point.x, height - point.y);
|
||||||
mbgl::LatLng latLng = _latLngForFn(screenCoord);
|
mbgl::LatLng latLng = _latLngForFn(screenCoord);
|
||||||
return MLNLocationCoordinate2DFromLatLng(latLng);
|
return MLNLocationCoordinate2DFromLatLng(latLng);
|
||||||
@@ -142,14 +164,14 @@ private:
|
|||||||
|
|
||||||
@implementation MLNMapSnapshotOptions
|
@implementation MLNMapSnapshotOptions
|
||||||
|
|
||||||
- (instancetype _Nonnull)initWithStyleURL:(nullable NSURL *)styleURL camera:(MLNMapCamera *)camera size:(CGSize)size
|
- (instancetype _Nonnull)initWithStyleURL:(nullable NSURL *)styleURL
|
||||||
{
|
camera:(MLNMapCamera *)camera
|
||||||
MLNLogDebug(@"Initializing withStyleURL: %@ camera: %@ size: %@", styleURL, camera, MLNStringFromSize(size));
|
size:(CGSize)size {
|
||||||
|
MLNLogDebug(@"Initializing withStyleURL: %@ camera: %@ size: %@", styleURL, camera,
|
||||||
|
MLNStringFromSize(size));
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self)
|
if (self) {
|
||||||
{
|
if (!styleURL) {
|
||||||
if ( !styleURL)
|
|
||||||
{
|
|
||||||
styleURL = [MLNStyle defaultStyleURL];
|
styleURL = [MLNStyle defaultStyleURL];
|
||||||
}
|
}
|
||||||
_styleURL = styleURL;
|
_styleURL = styleURL;
|
||||||
@@ -167,7 +189,9 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
|
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
|
||||||
__typeof__(self) copy = [[[self class] alloc] initWithStyleURL:_styleURL camera:_camera size:_size];
|
__typeof__(self) copy = [[[self class] alloc] initWithStyleURL:_styleURL
|
||||||
|
camera:_camera
|
||||||
|
size:_size];
|
||||||
copy.zoomLevel = _zoomLevel;
|
copy.zoomLevel = _zoomLevel;
|
||||||
copy.coordinateBounds = _coordinateBounds;
|
copy.coordinateBounds = _coordinateBounds;
|
||||||
copy.scale = _scale;
|
copy.scale = _scale;
|
||||||
@@ -178,8 +202,11 @@ private:
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MLNMapSnapshot() <MLNMapSnapshotProtocol>
|
@interface MLNMapSnapshot () <MLNMapSnapshotProtocol>
|
||||||
- (instancetype)initWithImage:(nullable MLNImage *)image scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn;
|
- (instancetype)initWithImage:(nullable MLNImage *)image
|
||||||
|
scale:(CGFloat)scale
|
||||||
|
pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn
|
||||||
|
latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn;
|
||||||
|
|
||||||
@property (nonatomic) CGFloat scale;
|
@property (nonatomic) CGFloat scale;
|
||||||
@end
|
@end
|
||||||
@@ -189,8 +216,10 @@ private:
|
|||||||
mbgl::MapSnapshotter::LatLngForFn _latLngForFn;
|
mbgl::MapSnapshotter::LatLngForFn _latLngForFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithImage:(nullable MLNImage *)image scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn
|
- (instancetype)initWithImage:(nullable MLNImage *)image
|
||||||
{
|
scale:(CGFloat)scale
|
||||||
|
pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn
|
||||||
|
latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
_pointForFn = pointForFn;
|
_pointForFn = pointForFn;
|
||||||
@@ -203,28 +232,24 @@ private:
|
|||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
|
|
||||||
- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate
|
- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate {
|
||||||
{
|
|
||||||
mbgl::ScreenCoordinate sc = _pointForFn(MLNLatLngFromLocationCoordinate2D(coordinate));
|
mbgl::ScreenCoordinate sc = _pointForFn(MLNLatLngFromLocationCoordinate2D(coordinate));
|
||||||
return CGPointMake(sc.x, sc.y);
|
return CGPointMake(sc.x, sc.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CLLocationCoordinate2D)coordinateForPoint:(CGPoint)point
|
- (CLLocationCoordinate2D)coordinateForPoint:(CGPoint)point {
|
||||||
{
|
|
||||||
mbgl::LatLng latLng = _latLngForFn(mbgl::ScreenCoordinate(point.x, point.y));
|
mbgl::LatLng latLng = _latLngForFn(mbgl::ScreenCoordinate(point.x, point.y));
|
||||||
return MLNLocationCoordinate2DFromLatLng(latLng);
|
return MLNLocationCoordinate2DFromLatLng(latLng);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
- (NSPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate
|
- (NSPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate {
|
||||||
{
|
|
||||||
mbgl::ScreenCoordinate sc = _pointForFn(MLNLatLngFromLocationCoordinate2D(coordinate));
|
mbgl::ScreenCoordinate sc = _pointForFn(MLNLatLngFromLocationCoordinate2D(coordinate));
|
||||||
return NSMakePoint(sc.x, self.image.size.height - sc.y);
|
return NSMakePoint(sc.x, self.image.size.height - sc.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CLLocationCoordinate2D)coordinateForPoint:(NSPoint)point
|
- (CLLocationCoordinate2D)coordinateForPoint:(NSPoint)point {
|
||||||
{
|
|
||||||
auto screenCoord = mbgl::ScreenCoordinate(point.x, self.image.size.height - point.y);
|
auto screenCoord = mbgl::ScreenCoordinate(point.x, self.image.size.height - point.y);
|
||||||
mbgl::LatLng latLng = _latLngForFn(screenCoord);
|
mbgl::LatLng latLng = _latLngForFn(screenCoord);
|
||||||
return MLNLocationCoordinate2DFromLatLng(latLng);
|
return MLNLocationCoordinate2DFromLatLng(latLng);
|
||||||
@@ -234,7 +259,7 @@ private:
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MLNMapSnapshotter() <MLNMultiPointDelegate>
|
@interface MLNMapSnapshotter () <MLNMultiPointDelegate>
|
||||||
@property (nonatomic) BOOL loading;
|
@property (nonatomic) BOOL loading;
|
||||||
@property (nonatomic) BOOL terminated;
|
@property (nonatomic) BOOL terminated;
|
||||||
@end
|
@end
|
||||||
@@ -257,33 +282,36 @@ private:
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithOptions:(MLNMapSnapshotOptions *)options
|
- (instancetype)initWithOptions:(MLNMapSnapshotOptions *)options {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Initializing withOptions: %@", options);
|
MLNLogDebug(@"Initializing withOptions: %@", options);
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
self.options = options;
|
self.options = options;
|
||||||
_installedImages = [NSMutableSet set];
|
_installedImages = [NSMutableSet set];
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:nil];
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(applicationWillTerminate:)
|
||||||
|
name:UIApplicationWillTerminateNotification
|
||||||
|
object:nil];
|
||||||
#else
|
#else
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:NSApplicationWillTerminateNotification object:nil];
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(applicationWillTerminate:)
|
||||||
|
name:NSApplicationWillTerminateNotification
|
||||||
|
object:nil];
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)applicationWillTerminate:(NSNotification *)notification
|
- (void)applicationWillTerminate:(NSNotification *)notification {
|
||||||
{
|
|
||||||
[self cancel];
|
[self cancel];
|
||||||
self.terminated = YES;
|
self.terminated = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addAnnotation:(id<MLNAnnotation>)annotation
|
- (void)addAnnotation:(id<MLNAnnotation>)annotation {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Adding annotation: %@", annotation);
|
MLNLogDebug(@"Adding annotation: %@", annotation);
|
||||||
if ( ! annotation) return;
|
if (!annotation) return;
|
||||||
return [self addAnnotations:@[annotation]];
|
return [self addAnnotations:@[ annotation ]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addAnnotations:(NSArray<id<MLNAnnotation>> *)annotations {
|
- (void)addAnnotations:(NSArray<id<MLNAnnotation>> *)annotations {
|
||||||
@@ -296,26 +324,24 @@ private:
|
|||||||
[_annotationsToInstall addObjectsFromArray:annotations];
|
[_annotationsToInstall addObjectsFromArray:annotations];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)installAnnotations:(NSArray<id<MLNAnnotation>> *)annotations
|
- (void)installAnnotations:(NSArray<id<MLNAnnotation>> *)annotations {
|
||||||
{
|
|
||||||
MLNLogDebug(@"install: %lu annotations", annotations.count);
|
MLNLogDebug(@"install: %lu annotations", annotations.count);
|
||||||
if ( ! annotations) return;
|
if (!annotations) return;
|
||||||
|
|
||||||
for (id <MLNAnnotation> annotation in annotations)
|
for (id<MLNAnnotation> annotation in annotations) {
|
||||||
{
|
MLNAssert([annotation conformsToProtocol:@protocol(MLNAnnotation)],
|
||||||
MLNAssert([annotation conformsToProtocol:@protocol(MLNAnnotation)], @"annotation should conform to MLNAnnotation");
|
@"annotation should conform to MLNAnnotation");
|
||||||
|
|
||||||
if ([annotation isKindOfClass:[MLNMultiPoint class]])
|
if ([annotation isKindOfClass:[MLNMultiPoint class]]) {
|
||||||
{
|
|
||||||
MLNMultiPoint *multiPoint = (MLNMultiPoint *)annotation;
|
MLNMultiPoint *multiPoint = (MLNMultiPoint *)annotation;
|
||||||
if (!multiPoint.pointCount) {
|
if (!multiPoint.pointCount) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
_mbglMapSnapshotter->addAnnotation([multiPoint annotationObjectWithDelegate:self]);
|
_mbglMapSnapshotter->addAnnotation([multiPoint annotationObjectWithDelegate:self]);
|
||||||
} else if (! [annotation isKindOfClass:[MLNMultiPolyline class]]
|
} else if (![annotation isKindOfClass:[MLNMultiPolyline class]] &&
|
||||||
&& ![annotation isKindOfClass:[MLNMultiPolygon class]]
|
![annotation isKindOfClass:[MLNMultiPolygon class]] &&
|
||||||
&& ![annotation isKindOfClass:[MLNShapeCollection class]]
|
![annotation isKindOfClass:[MLNShapeCollection class]] &&
|
||||||
&& ![annotation isKindOfClass:[MLNPointCollection class]]) {
|
![annotation isKindOfClass:[MLNPointCollection class]]) {
|
||||||
NSString *symbolName;
|
NSString *symbolName;
|
||||||
NSValue *annotationValue = [NSValue valueWithNonretainedObject:annotation];
|
NSValue *annotationValue = [NSValue valueWithNonretainedObject:annotation];
|
||||||
MLNAnnotationImage *annotationImage;
|
MLNAnnotationImage *annotationImage;
|
||||||
@@ -329,18 +355,16 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
symbolName = annotationImage.styleIconIdentifier;
|
symbolName = annotationImage.styleIconIdentifier;
|
||||||
if ( ! symbolName)
|
if (!symbolName) {
|
||||||
{
|
symbolName = [MLNSnapshotterAnnotationSpritePrefix
|
||||||
symbolName = [MLNSnapshotterAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier];
|
stringByAppendingString:annotationImage.reuseIdentifier];
|
||||||
annotationImage.styleIconIdentifier = symbolName;
|
annotationImage.styleIconIdentifier = symbolName;
|
||||||
}
|
}
|
||||||
|
|
||||||
[self installAnnotationImage:annotationImage];
|
[self installAnnotationImage:annotationImage];
|
||||||
|
|
||||||
_mbglMapSnapshotter->addAnnotation(mbgl::SymbolAnnotation {
|
_mbglMapSnapshotter->addAnnotation(mbgl::SymbolAnnotation{
|
||||||
MLNPointFromLocationCoordinate2D(annotation.coordinate),
|
MLNPointFromLocationCoordinate2D(annotation.coordinate), symbolName.UTF8String});
|
||||||
symbolName.UTF8String
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -348,37 +372,45 @@ private:
|
|||||||
- (void)installAnnotationImage:(MLNAnnotationImage *)annotationImage {
|
- (void)installAnnotationImage:(MLNAnnotationImage *)annotationImage {
|
||||||
NSString *iconIdentifier = annotationImage.styleIconIdentifier;
|
NSString *iconIdentifier = annotationImage.styleIconIdentifier;
|
||||||
if (![_installedImages containsObject:iconIdentifier]) {
|
if (![_installedImages containsObject:iconIdentifier]) {
|
||||||
_mbglMapSnapshotter->addAnnotationImage([annotationImage.image mgl_styleImageWithIdentifier:iconIdentifier]);
|
_mbglMapSnapshotter->addAnnotationImage(
|
||||||
|
[annotationImage.image mgl_styleImageWithIdentifier:iconIdentifier]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)startWithCompletionHandler:(MLNMapSnapshotCompletionHandler)completion
|
- (void)startWithCompletionHandler:(MLNMapSnapshotCompletionHandler)completion {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Starting withCompletionHandler: %@", completion);
|
MLNLogDebug(@"Starting withCompletionHandler: %@", completion);
|
||||||
[self startWithQueue:dispatch_get_main_queue() completionHandler:completion];
|
[self startWithQueue:dispatch_get_main_queue() completionHandler:completion];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MLNMapSnapshotCompletionHandler)completionHandler {
|
- (void)startWithQueue:(dispatch_queue_t)queue
|
||||||
|
completionHandler:(MLNMapSnapshotCompletionHandler)completionHandler {
|
||||||
[self startWithQueue:queue overlayHandler:nil completionHandler:completionHandler];
|
[self startWithQueue:queue overlayHandler:nil completionHandler:completionHandler];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)startWithOverlayHandler:(MLNMapSnapshotOverlayHandler)overlayHandler completionHandler:(MLNMapSnapshotCompletionHandler)completion {
|
- (void)startWithOverlayHandler:(MLNMapSnapshotOverlayHandler)overlayHandler
|
||||||
[self startWithQueue:dispatch_get_main_queue() overlayHandler:overlayHandler completionHandler:completion];
|
completionHandler:(MLNMapSnapshotCompletionHandler)completion {
|
||||||
|
[self startWithQueue:dispatch_get_main_queue()
|
||||||
|
overlayHandler:overlayHandler
|
||||||
|
completionHandler:completion];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)startWithQueue:(dispatch_queue_t)queue overlayHandler:(MLNMapSnapshotOverlayHandler)overlayHandler completionHandler:(MLNMapSnapshotCompletionHandler)completion
|
- (void)startWithQueue:(dispatch_queue_t)queue
|
||||||
{
|
overlayHandler:(MLNMapSnapshotOverlayHandler)overlayHandler
|
||||||
|
completionHandler:(MLNMapSnapshotCompletionHandler)completion {
|
||||||
if (!completion) {
|
if (!completion) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that offline storage has been initialized on the main thread, as MLNMapView and MLNOfflineStorage do when used directly.
|
// Ensure that offline storage has been initialized on the main thread, as MLNMapView and
|
||||||
|
// MLNOfflineStorage do when used directly.
|
||||||
// https://github.com/mapbox/mapbox-gl-native-ios/issues/227
|
// https://github.com/mapbox/mapbox-gl-native-ios/issues/227
|
||||||
if ([NSThread.currentThread isMainThread]) {
|
if ([NSThread.currentThread isMainThread]) {
|
||||||
(void)[MLNOfflineStorage sharedOfflineStorage];
|
(void)[MLNOfflineStorage sharedOfflineStorage];
|
||||||
} else {
|
} else {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"-[MLNMapSnapshotter startWithQueue:completionHandler:] must be called from the main thread, not %@.", NSThread.currentThread];
|
format:@"-[MLNMapSnapshotter startWithQueue:completionHandler:] must be called "
|
||||||
|
@"from the main thread, not %@.",
|
||||||
|
NSThread.currentThread];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.loading) {
|
if (self.loading) {
|
||||||
@@ -397,15 +429,24 @@ private:
|
|||||||
[self configureWithOptions:options];
|
[self configureWithOptions:options];
|
||||||
MLNLogDebug(@"Starting with options: %@", self.options);
|
MLNLogDebug(@"Starting with options: %@", self.options);
|
||||||
|
|
||||||
// Temporarily capture the snapshotter until the completion handler finishes executing, to keep standalone local usage of the snapshotter from becoming a no-op.
|
// Temporarily capture the snapshotter until the completion handler finishes executing, to keep
|
||||||
// POSTCONDITION: Only refer to this variable in the final result queue.
|
// standalone local usage of the snapshotter from becoming a no-op. POSTCONDITION: Only refer to
|
||||||
// POSTCONDITION: It is important to nil out this variable at some point in the future to avoid a leak. In cases where the completion handler gets called, the variable should be nilled out explicitly. If -cancel is called, mbgl releases the snapshot block below, causing the only remaining references to the snapshotter to go out of scope.
|
// this variable in the final result queue. POSTCONDITION: It is important to nil out this
|
||||||
|
// variable at some point in the future to avoid a leak. In cases where the completion handler
|
||||||
|
// gets called, the variable should be nilled out explicitly. If -cancel is called, mbgl releases
|
||||||
|
// the snapshot block below, causing the only remaining references to the snapshotter to go out of
|
||||||
|
// scope.
|
||||||
__block MLNMapSnapshotter *strongSelf = self;
|
__block MLNMapSnapshotter *strongSelf = self;
|
||||||
_mbglMapSnapshotter->snapshot(^(std::exception_ptr mbglError, mbgl::PremultipliedImage image, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn, mbgl::MapSnapshotter::LatLngForFn latLngForFn) {
|
_mbglMapSnapshotter->snapshot(^(std::exception_ptr mbglError, mbgl::PremultipliedImage image,
|
||||||
|
mbgl::MapSnapshotter::Attributions attributions,
|
||||||
|
mbgl::MapSnapshotter::PointForFn pointForFn,
|
||||||
|
mbgl::MapSnapshotter::LatLngForFn latLngForFn) {
|
||||||
if (mbglError) {
|
if (mbglError) {
|
||||||
NSString *description = @(mbgl::util::toString(mbglError).c_str());
|
NSString *description = @(mbgl::util::toString(mbglError).c_str());
|
||||||
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description};
|
NSDictionary *userInfo = @{NSLocalizedDescriptionKey : description};
|
||||||
NSError *error = [NSError errorWithDomain:MLNErrorDomain code:MLNErrorCodeSnapshotFailed userInfo:userInfo];
|
NSError *error = [NSError errorWithDomain:MLNErrorDomain
|
||||||
|
code:MLNErrorCodeSnapshotFailed
|
||||||
|
userInfo:userInfo];
|
||||||
|
|
||||||
// Dispatch to result queue
|
// Dispatch to result queue
|
||||||
dispatch_async(queue, ^{
|
dispatch_async(queue, ^{
|
||||||
@@ -415,7 +456,8 @@ private:
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
MLNImage *mglImage = [[MLNImage alloc] initWithMLNPremultipliedImage:std::move(image) scale:options.scale];
|
MLNImage *mglImage = [[MLNImage alloc] initWithMLNPremultipliedImage:std::move(image)
|
||||||
|
scale:options.scale];
|
||||||
#else
|
#else
|
||||||
MLNImage *mglImage = [[MLNImage alloc] initWithMLNPremultipliedImage:std::move(image)];
|
MLNImage *mglImage = [[MLNImage alloc] initWithMLNPremultipliedImage:std::move(image)];
|
||||||
mglImage.size = NSMakeSize(mglImage.size.width / options.scale,
|
mglImage.size = NSMakeSize(mglImage.size.width / options.scale,
|
||||||
@@ -427,7 +469,8 @@ private:
|
|||||||
dispatch_async(workQueue, ^{
|
dispatch_async(workQueue, ^{
|
||||||
// Call a function that cannot accidentally capture self.
|
// Call a function that cannot accidentally capture self.
|
||||||
NSError *error;
|
NSError *error;
|
||||||
MLNMapSnapshot *snapshot = MLNSnapshotWithDecoratedImage(mglImage, options, attributions, pointForFn, latLngForFn, overlayHandler, &error);
|
MLNMapSnapshot *snapshot = MLNSnapshotWithDecoratedImage(
|
||||||
|
mglImage, options, attributions, pointForFn, latLngForFn, overlayHandler, &error);
|
||||||
|
|
||||||
// Dispatch result to result queue
|
// Dispatch result to result queue
|
||||||
dispatch_async(queue, ^{
|
dispatch_async(queue, ^{
|
||||||
@@ -440,35 +483,48 @@ private:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNImage *MLNAttributedSnapshot(mbgl::MapSnapshotter::Attributions attributions, MLNImage *mglImage, MLNMapSnapshotOptions *options, void (^overlayHandler)()) {
|
MLNImage *MLNAttributedSnapshot(mbgl::MapSnapshotter::Attributions attributions, MLNImage *mglImage,
|
||||||
|
MLNMapSnapshotOptions *options, void (^overlayHandler)()) {
|
||||||
NSArray<MLNAttributionInfo *> *attributionInfo = MLNAttributionInfosFromAttributions(attributions);
|
NSArray<MLNAttributionInfo *> *attributionInfo =
|
||||||
|
MLNAttributionInfosFromAttributions(attributions);
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
MLNAttributionInfoStyle attributionInfoStyle = MLNAttributionInfoStyleLong;
|
MLNAttributionInfoStyle attributionInfoStyle = MLNAttributionInfoStyleLong;
|
||||||
for (NSUInteger styleValue = MLNAttributionInfoStyleLong; styleValue >= MLNAttributionInfoStyleShort; styleValue--) {
|
for (NSUInteger styleValue = MLNAttributionInfoStyleLong;
|
||||||
|
styleValue >= MLNAttributionInfoStyleShort; styleValue--) {
|
||||||
attributionInfoStyle = (MLNAttributionInfoStyle)styleValue;
|
attributionInfoStyle = (MLNAttributionInfoStyle)styleValue;
|
||||||
CGSize attributionSize = [MLNMapSnapshotter attributionSizeWithLogoStyle:attributionInfoStyle sourceAttributionStyle:attributionInfoStyle attributionInfo:attributionInfo];
|
CGSize attributionSize = [MLNMapSnapshotter attributionSizeWithLogoStyle:attributionInfoStyle
|
||||||
|
sourceAttributionStyle:attributionInfoStyle
|
||||||
|
attributionInfo:attributionInfo];
|
||||||
if (attributionSize.width <= mglImage.size.width) {
|
if (attributionSize.width <= mglImage.size.width) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UIImage *logoImage = [MLNMapSnapshotter logoImageWithStyle:attributionInfoStyle];
|
UIImage *logoImage = [MLNMapSnapshotter logoImageWithStyle:attributionInfoStyle];
|
||||||
CGSize attributionBackgroundSize = [MLNMapSnapshotter attributionTextSizeWithStyle:attributionInfoStyle attributionInfo:attributionInfo];
|
CGSize attributionBackgroundSize =
|
||||||
|
[MLNMapSnapshotter attributionTextSizeWithStyle:attributionInfoStyle
|
||||||
|
attributionInfo:attributionInfo];
|
||||||
|
|
||||||
CGRect logoImageRect = CGRectMake(MLNLogoImagePosition.x, mglImage.size.height - (MLNLogoImagePosition.y + logoImage.size.height), logoImage.size.width, logoImage.size.height);
|
CGRect logoImageRect =
|
||||||
CGPoint attributionOrigin = CGPointMake(mglImage.size.width - 10 - attributionBackgroundSize.width,
|
CGRectMake(MLNLogoImagePosition.x,
|
||||||
logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2) + 1);
|
mglImage.size.height - (MLNLogoImagePosition.y + logoImage.size.height),
|
||||||
|
logoImage.size.width, logoImage.size.height);
|
||||||
|
CGPoint attributionOrigin =
|
||||||
|
CGPointMake(mglImage.size.width - 10 - attributionBackgroundSize.width,
|
||||||
|
logoImageRect.origin.y + (logoImageRect.size.height / 2) -
|
||||||
|
(attributionBackgroundSize.height / 2) + 1);
|
||||||
if (!logoImage) {
|
if (!logoImage) {
|
||||||
CGSize defaultLogoSize = [MLNMapSnapshotter maplibreLongStyleLogo].size;
|
CGSize defaultLogoSize = [MLNMapSnapshotter maplibreLongStyleLogo].size;
|
||||||
logoImageRect = CGRectMake(0, mglImage.size.height - (MLNLogoImagePosition.y + defaultLogoSize.height), 0, defaultLogoSize.height);
|
logoImageRect =
|
||||||
attributionOrigin = CGPointMake(10, logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2) + 1);
|
CGRectMake(0, mglImage.size.height - (MLNLogoImagePosition.y + defaultLogoSize.height), 0,
|
||||||
|
defaultLogoSize.height);
|
||||||
|
attributionOrigin = CGPointMake(10, logoImageRect.origin.y + (logoImageRect.size.height / 2) -
|
||||||
|
(attributionBackgroundSize.height / 2) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
CGRect attributionBackgroundFrame = CGRectMake(attributionOrigin.x,
|
CGRect attributionBackgroundFrame =
|
||||||
attributionOrigin.y,
|
CGRectMake(attributionOrigin.x, attributionOrigin.y, attributionBackgroundSize.width,
|
||||||
attributionBackgroundSize.width,
|
|
||||||
attributionBackgroundSize.height);
|
attributionBackgroundSize.height);
|
||||||
CGPoint attributionTextPosition = CGPointMake(attributionBackgroundFrame.origin.x + 10,
|
CGPoint attributionTextPosition = CGPointMake(attributionBackgroundFrame.origin.x + 10,
|
||||||
attributionBackgroundFrame.origin.y - 1);
|
attributionBackgroundFrame.origin.y - 1);
|
||||||
@@ -503,11 +559,14 @@ MLNImage *MLNAttributedSnapshot(mbgl::MapSnapshotter::Attributions attributions,
|
|||||||
|
|
||||||
CIImage *ciAttributionImage = [[CIImage alloc] initWithCGImage:attributionImage.CGImage];
|
CIImage *ciAttributionImage = [[CIImage alloc] initWithCGImage:attributionImage.CGImage];
|
||||||
|
|
||||||
UIImage *blurredAttributionBackground = [MLNMapSnapshotter blurredAttributionBackground:ciAttributionImage];
|
UIImage *blurredAttributionBackground =
|
||||||
|
[MLNMapSnapshotter blurredAttributionBackground:ciAttributionImage];
|
||||||
|
|
||||||
[blurredAttributionBackground drawInRect:attributionBackgroundFrame];
|
[blurredAttributionBackground drawInRect:attributionBackgroundFrame];
|
||||||
|
|
||||||
[MLNMapSnapshotter drawAttributionTextWithStyle:attributionInfoStyle origin:attributionTextPosition attributionInfo:attributionInfo];
|
[MLNMapSnapshotter drawAttributionTextWithStyle:attributionInfoStyle
|
||||||
|
origin:attributionTextPosition
|
||||||
|
attributionInfo:attributionInfo];
|
||||||
}
|
}
|
||||||
|
|
||||||
UIImage *compositedImage = UIGraphicsGetImageFromCurrentImageContext();
|
UIImage *compositedImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
@@ -517,37 +576,43 @@ MLNImage *MLNAttributedSnapshot(mbgl::MapSnapshotter::Attributions attributions,
|
|||||||
return compositedImage;
|
return compositedImage;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
NSRect targetFrame = { .origin = NSZeroPoint, .size = options.size };
|
NSRect targetFrame = {.origin = NSZeroPoint, .size = options.size};
|
||||||
|
|
||||||
MLNAttributionInfoStyle attributionInfoStyle = MLNAttributionInfoStyleLong;
|
MLNAttributionInfoStyle attributionInfoStyle = MLNAttributionInfoStyleLong;
|
||||||
for (NSUInteger styleValue = MLNAttributionInfoStyleLong; styleValue >= MLNAttributionInfoStyleShort; styleValue--) {
|
for (NSUInteger styleValue = MLNAttributionInfoStyleLong;
|
||||||
|
styleValue >= MLNAttributionInfoStyleShort; styleValue--) {
|
||||||
attributionInfoStyle = (MLNAttributionInfoStyle)styleValue;
|
attributionInfoStyle = (MLNAttributionInfoStyle)styleValue;
|
||||||
CGSize attributionSize = [MLNMapSnapshotter attributionSizeWithLogoStyle:attributionInfoStyle sourceAttributionStyle:attributionInfoStyle attributionInfo:attributionInfo];
|
CGSize attributionSize = [MLNMapSnapshotter attributionSizeWithLogoStyle:attributionInfoStyle
|
||||||
|
sourceAttributionStyle:attributionInfoStyle
|
||||||
|
attributionInfo:attributionInfo];
|
||||||
if (attributionSize.width <= mglImage.size.width) {
|
if (attributionSize.width <= mglImage.size.width) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSImage *logoImage = [MLNMapSnapshotter logoImageWithStyle:attributionInfoStyle];
|
NSImage *logoImage = [MLNMapSnapshotter logoImageWithStyle:attributionInfoStyle];
|
||||||
CGSize attributionBackgroundSize = [MLNMapSnapshotter attributionTextSizeWithStyle:attributionInfoStyle attributionInfo:attributionInfo];
|
CGSize attributionBackgroundSize =
|
||||||
|
[MLNMapSnapshotter attributionTextSizeWithStyle:attributionInfoStyle
|
||||||
|
attributionInfo:attributionInfo];
|
||||||
NSImage *sourceImage = mglImage;
|
NSImage *sourceImage = mglImage;
|
||||||
|
|
||||||
CGRect logoImageRect = CGRectMake(MLNLogoImagePosition.x, MLNLogoImagePosition.y, logoImage.size.width, logoImage.size.height);
|
CGRect logoImageRect = CGRectMake(MLNLogoImagePosition.x, MLNLogoImagePosition.y,
|
||||||
CGPoint attributionOrigin = CGPointMake(targetFrame.size.width - 10 - attributionBackgroundSize.width,
|
logoImage.size.width, logoImage.size.height);
|
||||||
MLNLogoImagePosition.y + 1);
|
CGPoint attributionOrigin = CGPointMake(
|
||||||
|
targetFrame.size.width - 10 - attributionBackgroundSize.width, MLNLogoImagePosition.y + 1);
|
||||||
if (!logoImage) {
|
if (!logoImage) {
|
||||||
CGSize defaultLogoSize = [MLNMapSnapshotter maplibreLongStyleLogo].size;
|
CGSize defaultLogoSize = [MLNMapSnapshotter maplibreLongStyleLogo].size;
|
||||||
logoImageRect = CGRectMake(0, MLNLogoImagePosition.y, 0, defaultLogoSize.height);
|
logoImageRect = CGRectMake(0, MLNLogoImagePosition.y, 0, defaultLogoSize.height);
|
||||||
attributionOrigin = CGPointMake(10, attributionOrigin.y);
|
attributionOrigin = CGPointMake(10, attributionOrigin.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
CGRect attributionBackgroundFrame = CGRectMake(attributionOrigin.x,
|
CGRect attributionBackgroundFrame =
|
||||||
attributionOrigin.y,
|
CGRectMake(attributionOrigin.x, attributionOrigin.y, attributionBackgroundSize.width,
|
||||||
attributionBackgroundSize.width,
|
|
||||||
attributionBackgroundSize.height);
|
attributionBackgroundSize.height);
|
||||||
CGPoint attributionTextPosition = CGPointMake(attributionBackgroundFrame.origin.x + 10,
|
CGPoint attributionTextPosition =
|
||||||
logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2));
|
CGPointMake(attributionBackgroundFrame.origin.x + 10,
|
||||||
|
logoImageRect.origin.y + (logoImageRect.size.height / 2) -
|
||||||
|
(attributionBackgroundSize.height / 2));
|
||||||
|
|
||||||
NSImage *compositedImage = nil;
|
NSImage *compositedImage = nil;
|
||||||
NSImageRep *sourceImageRep = [sourceImage bestRepresentationForRect:targetFrame
|
NSImageRep *sourceImageRep = [sourceImage bestRepresentationForRect:targetFrame
|
||||||
@@ -557,7 +622,7 @@ MLNImage *MLNAttributedSnapshot(mbgl::MapSnapshotter::Attributions attributions,
|
|||||||
|
|
||||||
[compositedImage lockFocus];
|
[compositedImage lockFocus];
|
||||||
|
|
||||||
[sourceImageRep drawInRect: targetFrame];
|
[sourceImageRep drawInRect:targetFrame];
|
||||||
|
|
||||||
overlayHandler();
|
overlayHandler();
|
||||||
|
|
||||||
@@ -573,15 +638,20 @@ MLNImage *MLNAttributedSnapshot(mbgl::MapSnapshotter::Attributions attributions,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.showsAttribution) {
|
if (options.showsAttribution) {
|
||||||
NSBitmapImageRep *attributionBackground = [[NSBitmapImageRep alloc] initWithFocusedViewRect:attributionBackgroundFrame];
|
NSBitmapImageRep *attributionBackground =
|
||||||
|
[[NSBitmapImageRep alloc] initWithFocusedViewRect:attributionBackgroundFrame];
|
||||||
|
|
||||||
CIImage *attributionBackgroundImage = [[CIImage alloc] initWithCGImage:[attributionBackground CGImage]];
|
CIImage *attributionBackgroundImage =
|
||||||
|
[[CIImage alloc] initWithCGImage:[attributionBackground CGImage]];
|
||||||
|
|
||||||
NSImage *blurredAttributionBackground = [MLNMapSnapshotter blurredAttributionBackground:attributionBackgroundImage];
|
NSImage *blurredAttributionBackground =
|
||||||
|
[MLNMapSnapshotter blurredAttributionBackground:attributionBackgroundImage];
|
||||||
|
|
||||||
[blurredAttributionBackground drawInRect:attributionBackgroundFrame];
|
[blurredAttributionBackground drawInRect:attributionBackgroundFrame];
|
||||||
|
|
||||||
[MLNMapSnapshotter drawAttributionTextWithStyle:attributionInfoStyle origin:attributionTextPosition attributionInfo:attributionInfo];
|
[MLNMapSnapshotter drawAttributionTextWithStyle:attributionInfoStyle
|
||||||
|
origin:attributionTextPosition
|
||||||
|
attributionInfo:attributionInfo];
|
||||||
}
|
}
|
||||||
|
|
||||||
[compositedImage unlockFocus];
|
[compositedImage unlockFocus];
|
||||||
@@ -590,7 +660,12 @@ MLNImage *MLNAttributedSnapshot(mbgl::MapSnapshotter::Attributions attributions,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNMapSnapshot *MLNSnapshotWithDecoratedImage(MLNImage *mglImage, MLNMapSnapshotOptions *options, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn, mbgl::MapSnapshotter::LatLngForFn latLngForFn, MLNMapSnapshotOverlayHandler overlayHandler, NSError * _Nullable *outError) {
|
MLNMapSnapshot *MLNSnapshotWithDecoratedImage(MLNImage *mglImage, MLNMapSnapshotOptions *options,
|
||||||
|
mbgl::MapSnapshotter::Attributions attributions,
|
||||||
|
mbgl::MapSnapshotter::PointForFn pointForFn,
|
||||||
|
mbgl::MapSnapshotter::LatLngForFn latLngForFn,
|
||||||
|
MLNMapSnapshotOverlayHandler overlayHandler,
|
||||||
|
NSError *_Nullable *outError) {
|
||||||
MLNImage *compositedImage = MLNAttributedSnapshot(attributions, mglImage, options, ^{
|
MLNImage *compositedImage = MLNAttributedSnapshot(attributions, mglImage, options, ^{
|
||||||
if (!overlayHandler) {
|
if (!overlayHandler) {
|
||||||
return;
|
return;
|
||||||
@@ -600,7 +675,8 @@ MLNMapSnapshot *MLNSnapshotWithDecoratedImage(MLNImage *mglImage, MLNMapSnapshot
|
|||||||
if (!context) {
|
if (!context) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MLNMapSnapshotOverlay *snapshotOverlay = [[MLNMapSnapshotOverlay alloc] initWithContext:context
|
MLNMapSnapshotOverlay *snapshotOverlay =
|
||||||
|
[[MLNMapSnapshotOverlay alloc] initWithContext:context
|
||||||
scale:options.scale
|
scale:options.scale
|
||||||
pointForFn:pointForFn
|
pointForFn:pointForFn
|
||||||
latLngForFn:latLngForFn];
|
latLngForFn:latLngForFn];
|
||||||
@@ -629,7 +705,8 @@ MLNMapSnapshot *MLNSnapshotWithDecoratedImage(MLNImage *mglImage, MLNMapSnapshot
|
|||||||
latLngForFn:latLngForFn];
|
latLngForFn:latLngForFn];
|
||||||
} else {
|
} else {
|
||||||
if (outError) {
|
if (outError) {
|
||||||
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"Failed to generate composited snapshot."};
|
NSDictionary *userInfo =
|
||||||
|
@{NSLocalizedDescriptionKey : @"Failed to generate composited snapshot."};
|
||||||
*outError = [NSError errorWithDomain:MLNErrorDomain
|
*outError = [NSError errorWithDomain:MLNErrorDomain
|
||||||
code:MLNErrorCodeSnapshotFailed
|
code:MLNErrorCodeSnapshotFailed
|
||||||
userInfo:userInfo];
|
userInfo:userInfo];
|
||||||
@@ -638,7 +715,8 @@ MLNMapSnapshot *MLNSnapshotWithDecoratedImage(MLNImage *mglImage, MLNMapSnapshot
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnapshotter::Attributions attributions) {
|
NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(
|
||||||
|
mbgl::MapSnapshotter::Attributions attributions) {
|
||||||
NSMutableArray *infos = [NSMutableArray array];
|
NSMutableArray *infos = [NSMutableArray array];
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
@@ -658,8 +736,9 @@ NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnap
|
|||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)drawAttributionTextWithStyle:(MLNAttributionInfoStyle)attributionInfoStyle origin:(CGPoint)origin attributionInfo:(NSArray<MLNAttributionInfo *>*)attributionInfo
|
+ (void)drawAttributionTextWithStyle:(MLNAttributionInfoStyle)attributionInfoStyle
|
||||||
{
|
origin:(CGPoint)origin
|
||||||
|
attributionInfo:(NSArray<MLNAttributionInfo *> *)attributionInfo {
|
||||||
for (MLNAttributionInfo *info in attributionInfo) {
|
for (MLNAttributionInfo *info in attributionInfo) {
|
||||||
if (info.isFeedbackLink) {
|
if (info.isFeedbackLink) {
|
||||||
continue;
|
continue;
|
||||||
@@ -671,12 +750,12 @@ NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (MLNImage *)blurredAttributionBackground:(CIImage *)backgroundImage
|
+ (MLNImage *)blurredAttributionBackground:(CIImage *)backgroundImage {
|
||||||
{
|
|
||||||
CGAffineTransform transform = CGAffineTransformIdentity;
|
CGAffineTransform transform = CGAffineTransformIdentity;
|
||||||
CIFilter *clamp = [CIFilter filterWithName:@"CIAffineClamp"];
|
CIFilter *clamp = [CIFilter filterWithName:@"CIAffineClamp"];
|
||||||
[clamp setValue:backgroundImage forKey:kCIInputImageKey];
|
[clamp setValue:backgroundImage forKey:kCIInputImageKey];
|
||||||
[clamp setValue:[NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)] forKey:@"inputTransform"];
|
[clamp setValue:[NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)]
|
||||||
|
forKey:@"inputTransform"];
|
||||||
|
|
||||||
CIFilter *attributionBlurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
|
CIFilter *attributionBlurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
|
||||||
[attributionBlurFilter setValue:[clamp outputImage] forKey:kCIInputImageKey];
|
[attributionBlurFilter setValue:[clamp outputImage] forKey:kCIInputImageKey];
|
||||||
@@ -702,8 +781,7 @@ NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnap
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (MLNImage *)logoImageWithStyle:(MLNAttributionInfoStyle)style
|
+ (MLNImage *)logoImageWithStyle:(MLNAttributionInfoStyle)style {
|
||||||
{
|
|
||||||
MLNImage *logoImage;
|
MLNImage *logoImage;
|
||||||
switch (style) {
|
switch (style) {
|
||||||
case MLNAttributionInfoStyleLong:
|
case MLNAttributionInfoStyleLong:
|
||||||
@@ -711,9 +789,13 @@ NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnap
|
|||||||
break;
|
break;
|
||||||
case MLNAttributionInfoStyleMedium:
|
case MLNAttributionInfoStyleMedium:
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
logoImage = [UIImage imageNamed:@"maplibre-logo-icon" inBundle:[NSBundle mgl_frameworkBundle] compatibleWithTraitCollection:nil];
|
logoImage = [UIImage imageNamed:@"maplibre-logo-icon"
|
||||||
|
inBundle:[NSBundle mgl_frameworkBundle]
|
||||||
|
compatibleWithTraitCollection:nil];
|
||||||
#else
|
#else
|
||||||
logoImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mgl_frameworkBundle] pathForResource:@"mapbox_helmet" ofType:@"pdf"]];
|
logoImage = [[NSImage alloc]
|
||||||
|
initWithContentsOfFile:[[NSBundle mgl_frameworkBundle] pathForResource:@"mapbox_helmet"
|
||||||
|
ofType:@"pdf"]];
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case MLNAttributionInfoStyleShort:
|
case MLNAttributionInfoStyleShort:
|
||||||
@@ -723,22 +805,28 @@ NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnap
|
|||||||
return logoImage;
|
return logoImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (MLNImage *)maplibreLongStyleLogo
|
+ (MLNImage *)maplibreLongStyleLogo {
|
||||||
{
|
|
||||||
MLNImage *logoImage;
|
MLNImage *logoImage;
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
logoImage =[UIImage imageNamed:@"maplibre-logo-stroke-gray" inBundle:[NSBundle mgl_frameworkBundle] compatibleWithTraitCollection:nil];
|
logoImage = [UIImage imageNamed:@"maplibre-logo-stroke-gray"
|
||||||
|
inBundle:[NSBundle mgl_frameworkBundle]
|
||||||
|
compatibleWithTraitCollection:nil];
|
||||||
#else
|
#else
|
||||||
logoImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mgl_frameworkBundle] pathForResource:@"mapbox" ofType:@"pdf"]];
|
logoImage = [[NSImage alloc]
|
||||||
|
initWithContentsOfFile:[[NSBundle mgl_frameworkBundle] pathForResource:@"mapbox"
|
||||||
|
ofType:@"pdf"]];
|
||||||
#endif
|
#endif
|
||||||
return logoImage;
|
return logoImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CGSize)attributionSizeWithLogoStyle:(MLNAttributionInfoStyle)logoStyle sourceAttributionStyle:(MLNAttributionInfoStyle)attributionStyle attributionInfo:(NSArray<MLNAttributionInfo *>*)attributionInfo
|
+ (CGSize)attributionSizeWithLogoStyle:(MLNAttributionInfoStyle)logoStyle
|
||||||
{
|
sourceAttributionStyle:(MLNAttributionInfoStyle)attributionStyle
|
||||||
|
attributionInfo:(NSArray<MLNAttributionInfo *> *)attributionInfo {
|
||||||
MLNImage *logoImage = [self logoImageWithStyle:logoStyle];
|
MLNImage *logoImage = [self logoImageWithStyle:logoStyle];
|
||||||
|
|
||||||
CGSize attributionBackgroundSize = [MLNMapSnapshotter attributionTextSizeWithStyle:attributionStyle attributionInfo:attributionInfo];
|
CGSize attributionBackgroundSize =
|
||||||
|
[MLNMapSnapshotter attributionTextSizeWithStyle:attributionStyle
|
||||||
|
attributionInfo:attributionInfo];
|
||||||
|
|
||||||
CGSize attributionSize = CGSizeZero;
|
CGSize attributionSize = CGSizeZero;
|
||||||
|
|
||||||
@@ -751,8 +839,8 @@ NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnap
|
|||||||
return attributionSize;
|
return attributionSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CGSize)attributionTextSizeWithStyle:(MLNAttributionInfoStyle)attributionStyle attributionInfo:(NSArray<MLNAttributionInfo *>*)attributionInfo
|
+ (CGSize)attributionTextSizeWithStyle:(MLNAttributionInfoStyle)attributionStyle
|
||||||
{
|
attributionInfo:(NSArray<MLNAttributionInfo *> *)attributionInfo {
|
||||||
CGSize attributionBackgroundSize = CGSizeMake(10, 0);
|
CGSize attributionBackgroundSize = CGSizeMake(10, 0);
|
||||||
for (MLNAttributionInfo *info in attributionInfo) {
|
for (MLNAttributionInfo *info in attributionInfo) {
|
||||||
if (info.isFeedbackLink) {
|
if (info.isFeedbackLink) {
|
||||||
@@ -760,14 +848,14 @@ NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnap
|
|||||||
}
|
}
|
||||||
CGSize attributionSize = [info titleWithStyle:attributionStyle].size;
|
CGSize attributionSize = [info titleWithStyle:attributionStyle].size;
|
||||||
attributionBackgroundSize.width += attributionSize.width + 10;
|
attributionBackgroundSize.width += attributionSize.width + 10;
|
||||||
attributionBackgroundSize.height = MAX(attributionSize.height, attributionBackgroundSize.height);
|
attributionBackgroundSize.height =
|
||||||
|
MAX(attributionSize.height, attributionBackgroundSize.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
return attributionBackgroundSize;
|
return attributionBackgroundSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)cancel
|
- (void)cancel {
|
||||||
{
|
|
||||||
MLNLogInfo(@"Cancelling snapshotter.");
|
MLNLogInfo(@"Cancelling snapshotter.");
|
||||||
|
|
||||||
if (_mbglMapSnapshotter) {
|
if (_mbglMapSnapshotter) {
|
||||||
@@ -784,8 +872,7 @@ NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnap
|
|||||||
// For non retina screens the ratio is 1:1 MLNSnapshotterMinimumPixelSize
|
// For non retina screens the ratio is 1:1 MLNSnapshotterMinimumPixelSize
|
||||||
mbgl::Size size = {
|
mbgl::Size size = {
|
||||||
static_cast<uint32_t>(MAX(options.size.width, MLNSnapshotterMinimumPixelSize)),
|
static_cast<uint32_t>(MAX(options.size.width, MLNSnapshotterMinimumPixelSize)),
|
||||||
static_cast<uint32_t>(MAX(options.size.height, MLNSnapshotterMinimumPixelSize))
|
static_cast<uint32_t>(MAX(options.size.height, MLNSnapshotterMinimumPixelSize))};
|
||||||
};
|
|
||||||
|
|
||||||
float pixelRatio = MAX(options.scale, 1);
|
float pixelRatio = MAX(options.scale, 1);
|
||||||
|
|
||||||
@@ -794,8 +881,7 @@ NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnap
|
|||||||
|
|
||||||
auto tileServerOptions = [[MLNSettings sharedSettings] tileServerOptionsInternal];
|
auto tileServerOptions = [[MLNSettings sharedSettings] tileServerOptionsInternal];
|
||||||
mbgl::ResourceOptions resourceOptions;
|
mbgl::ResourceOptions resourceOptions;
|
||||||
resourceOptions
|
resourceOptions.withTileServerOptions(*tileServerOptions)
|
||||||
.withTileServerOptions(*tileServerOptions)
|
|
||||||
.withCachePath(MLNOfflineStorage.sharedOfflineStorage.databasePath.UTF8String)
|
.withCachePath(MLNOfflineStorage.sharedOfflineStorage.databasePath.UTF8String)
|
||||||
.withAssetPath(NSBundle.mainBundle.resourceURL.path.UTF8String);
|
.withAssetPath(NSBundle.mainBundle.resourceURL.path.UTF8String);
|
||||||
mbgl::ClientOptions clientOptions;
|
mbgl::ClientOptions clientOptions;
|
||||||
@@ -806,7 +892,8 @@ NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnap
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the snapshotter
|
// Create the snapshotter
|
||||||
auto localFontFamilyName = config.localFontFamilyName ? std::string(config.localFontFamilyName.UTF8String) : nullptr;
|
auto localFontFamilyName =
|
||||||
|
config.localFontFamilyName ? std::string(config.localFontFamilyName.UTF8String) : nullptr;
|
||||||
_delegateHost = std::make_unique<MLNMapSnapshotterDelegateHost>(self);
|
_delegateHost = std::make_unique<MLNMapSnapshotterDelegateHost>(self);
|
||||||
_mbglMapSnapshotter = std::make_unique<mbgl::MapSnapshotter>(
|
_mbglMapSnapshotter = std::make_unique<mbgl::MapSnapshotter>(
|
||||||
size, pixelRatio, resourceOptions, clientOptions, *_delegateHost, localFontFamilyName);
|
size, pixelRatio, resourceOptions, clientOptions, *_delegateHost, localFontFamilyName);
|
||||||
@@ -852,16 +939,14 @@ NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnap
|
|||||||
return [[MLNStyle alloc] initWithRawStyle:&_mbglMapSnapshotter->getStyle() stylable:self];
|
return [[MLNStyle alloc] initWithRawStyle:&_mbglMapSnapshotter->getStyle() stylable:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (double)alphaForShapeAnnotation:(MLNShape *)annotation
|
- (double)alphaForShapeAnnotation:(MLNShape *)annotation {
|
||||||
{
|
|
||||||
if ([self.delegate respondsToSelector:@selector(mapSnapshotter:alphaForShapeAnnotation:)]) {
|
if ([self.delegate respondsToSelector:@selector(mapSnapshotter:alphaForShapeAnnotation:)]) {
|
||||||
return [self.delegate mapSnapshotter:self alphaForShapeAnnotation:annotation];
|
return [self.delegate mapSnapshotter:self alphaForShapeAnnotation:annotation];
|
||||||
}
|
}
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::Color)strokeColorForShapeAnnotation:(MLNShape *)annotation
|
- (mbgl::Color)strokeColorForShapeAnnotation:(MLNShape *)annotation {
|
||||||
{
|
|
||||||
if ([self.delegate respondsToSelector:@selector(mapSnapshotter:strokeColorForShapeAnnotation:)]) {
|
if ([self.delegate respondsToSelector:@selector(mapSnapshotter:strokeColorForShapeAnnotation:)]) {
|
||||||
MLNColor *color = [self.delegate mapSnapshotter:self strokeColorForShapeAnnotation:annotation];
|
MLNColor *color = [self.delegate mapSnapshotter:self strokeColorForShapeAnnotation:annotation];
|
||||||
return color.mgl_color;
|
return color.mgl_color;
|
||||||
@@ -870,18 +955,17 @@ NSArray<MLNAttributionInfo *> *MLNAttributionInfosFromAttributions(mbgl::MapSnap
|
|||||||
return mbgl::Color::white();
|
return mbgl::Color::white();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::Color)fillColorForPolygonAnnotation:(MLNPolygon *)annotation
|
- (mbgl::Color)fillColorForPolygonAnnotation:(MLNPolygon *)annotation {
|
||||||
{
|
if ([self.delegate respondsToSelector:@selector(mapSnapshotter:fillColorForPolygonAnnotation:)]) {
|
||||||
if ([self.delegate respondsToSelector:@selector(mapSnapshotter:fillColorForPolygonAnnotation:)]){
|
|
||||||
MLNColor *color = [self.delegate mapSnapshotter:self fillColorForPolygonAnnotation:annotation];
|
MLNColor *color = [self.delegate mapSnapshotter:self fillColorForPolygonAnnotation:annotation];
|
||||||
return color.mgl_color;
|
return color.mgl_color;
|
||||||
}
|
}
|
||||||
return mbgl::Color::white();
|
return mbgl::Color::white();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGFloat)lineWidthForPolylineAnnotation:(MLNPolyline *)annotation
|
- (CGFloat)lineWidthForPolylineAnnotation:(MLNPolyline *)annotation {
|
||||||
{
|
if ([self.delegate respondsToSelector:@selector(mapSnapshotter:
|
||||||
if ([self.delegate respondsToSelector:@selector(mapSnapshotter:lineWidthForPolylineAnnotation:)]) {
|
lineWidthForPolylineAnnotation:)]) {
|
||||||
return [self.delegate mapSnapshotter:self lineWidthForPolylineAnnotation:annotation];
|
return [self.delegate mapSnapshotter:self lineWidthForPolylineAnnotation:annotation];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,25 @@
|
|||||||
#import "MLNMultiPoint_Private.h"
|
|
||||||
#import "MLNGeometry_Private.h"
|
#import "MLNGeometry_Private.h"
|
||||||
#import "MLNShape_Private.h"
|
|
||||||
#import "NSCoder+MLNAdditions.h"
|
|
||||||
#import "MLNTypes.h"
|
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
#import "MLNMultiPoint_Private.h"
|
||||||
|
#import "MLNShape_Private.h"
|
||||||
|
#import "MLNTypes.h"
|
||||||
|
#import "NSCoder+MLNAdditions.h"
|
||||||
|
|
||||||
@implementation MLNMultiPoint
|
@implementation MLNMultiPoint {
|
||||||
{
|
|
||||||
std::optional<mbgl::LatLngBounds> _bounds;
|
std::optional<mbgl::LatLngBounds> _bounds;
|
||||||
std::vector<CLLocationCoordinate2D> _coordinates;
|
std::vector<CLLocationCoordinate2D> _coordinates;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count
|
- (instancetype)initWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count {
|
||||||
{
|
MLNLogDebug(@"Initializing with %lu coordinates.", (unsigned long)count);
|
||||||
MLNLogDebug(@"Initializing with %lu coordinates.", (unsigned long) count);
|
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self)
|
if (self) {
|
||||||
{
|
|
||||||
if (!count) {
|
if (!count) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"A multipoint must have at least one vertex."];
|
format:@"A multipoint must have at least one vertex."];
|
||||||
}
|
}
|
||||||
_coordinates = { coords, coords + count };
|
_coordinates = {coords, coords + count};
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -32,8 +29,7 @@
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithCoder:(NSCoder *)decoder
|
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||||
{
|
|
||||||
MLNLogInfo(@"Initializing with coder.");
|
MLNLogInfo(@"Initializing with coder.");
|
||||||
if (self = [super initWithCoder:decoder]) {
|
if (self = [super initWithCoder:decoder]) {
|
||||||
_coordinates = [decoder mgl_decodeLocationCoordinates2DForKey:@"coordinates"];
|
_coordinates = [decoder mgl_decodeLocationCoordinates2DForKey:@"coordinates"];
|
||||||
@@ -41,62 +37,54 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)encodeWithCoder:(NSCoder *)coder
|
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||||
{
|
|
||||||
[super encodeWithCoder:coder];
|
[super encodeWithCoder:coder];
|
||||||
[coder mgl_encodeLocationCoordinates2D:_coordinates forKey:@"coordinates"];
|
[coder mgl_encodeLocationCoordinates2D:_coordinates forKey:@"coordinates"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)other
|
- (BOOL)isEqual:(id)other {
|
||||||
{
|
|
||||||
if (self == other) return YES;
|
if (self == other) return YES;
|
||||||
if (![other isKindOfClass:[MLNMultiPoint class]]) return NO;
|
if (![other isKindOfClass:[MLNMultiPoint class]]) return NO;
|
||||||
|
|
||||||
MLNMultiPoint *otherMultipoint = other;
|
MLNMultiPoint *otherMultipoint = other;
|
||||||
return ([super isEqual:otherMultipoint]
|
return ([super isEqual:otherMultipoint] && _coordinates == otherMultipoint->_coordinates);
|
||||||
&& _coordinates == otherMultipoint->_coordinates);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)hash
|
- (NSUInteger)hash {
|
||||||
{
|
|
||||||
NSUInteger hash = [super hash];
|
NSUInteger hash = [super hash];
|
||||||
for (auto coord : _coordinates) {
|
for (auto coord : _coordinates) {
|
||||||
hash += @(coord.latitude+coord.longitude).hash;
|
hash += @(coord.latitude + coord.longitude).hash;
|
||||||
}
|
}
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CLLocationCoordinate2D)coordinate
|
- (CLLocationCoordinate2D)coordinate {
|
||||||
{
|
|
||||||
MLNAssert([self pointCount] > 0, @"A multipoint must have coordinates");
|
MLNAssert([self pointCount] > 0, @"A multipoint must have coordinates");
|
||||||
return _coordinates.at(0);
|
return _coordinates.at(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)pointCount
|
- (NSUInteger)pointCount {
|
||||||
{
|
|
||||||
return _coordinates.size();
|
return _coordinates.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSSet<NSString *> *)keyPathsForValuesAffectingPointCount
|
+ (NSSet<NSString *> *)keyPathsForValuesAffectingPointCount {
|
||||||
{
|
|
||||||
return [NSSet setWithObjects:@"coordinates", nil];
|
return [NSSet setWithObjects:@"coordinates", nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CLLocationCoordinate2D *)coordinates
|
- (CLLocationCoordinate2D *)coordinates {
|
||||||
{
|
|
||||||
return _coordinates.data();
|
return _coordinates.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range
|
- (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range {
|
||||||
{
|
if (range.location + range.length > [self pointCount]) {
|
||||||
if (range.location + range.length > [self pointCount])
|
[NSException
|
||||||
{
|
raise:NSRangeException
|
||||||
[NSException raise:NSRangeException
|
|
||||||
format:@"Invalid coordinate range %@ extends beyond current coordinate count of %ld",
|
format:@"Invalid coordinate range %@ extends beyond current coordinate count of %ld",
|
||||||
NSStringFromRange(range), (unsigned long)[self pointCount]];
|
NSStringFromRange(range), (unsigned long)[self pointCount]];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::copy(_coordinates.begin() + range.location, _coordinates.begin() + NSMaxRange(range), coords);
|
std::copy(_coordinates.begin() + range.location, _coordinates.begin() + NSMaxRange(range),
|
||||||
|
coords);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count {
|
- (void)setCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count {
|
||||||
@@ -107,21 +95,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[self willChangeValueForKey:@"coordinates"];
|
[self willChangeValueForKey:@"coordinates"];
|
||||||
_coordinates = { coords, coords + count };
|
_coordinates = {coords, coords + count};
|
||||||
_bounds = {};
|
_bounds = {};
|
||||||
[self didChangeValueForKey:@"coordinates"];
|
[self didChangeValueForKey:@"coordinates"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)insertCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count atIndex:(NSUInteger)index {
|
- (void)insertCoordinates:(const CLLocationCoordinate2D *)coords
|
||||||
MLNLogDebug(@"Inserting %lu coordinates at index %lu.", (unsigned long)count, (unsigned long)index);
|
count:(NSUInteger)count
|
||||||
|
atIndex:(NSUInteger)index {
|
||||||
|
MLNLogDebug(@"Inserting %lu coordinates at index %lu.", (unsigned long)count,
|
||||||
|
(unsigned long)index);
|
||||||
if (!count) {
|
if (!count) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index > _coordinates.size()) {
|
if (index > _coordinates.size()) {
|
||||||
[NSException raise:NSRangeException
|
[NSException raise:NSRangeException
|
||||||
format:@"Invalid index %lu for existing coordinate count %ld",
|
format:@"Invalid index %lu for existing coordinate count %ld", (unsigned long)index,
|
||||||
(unsigned long)index, (unsigned long)[self pointCount]];
|
(unsigned long)[self pointCount]];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self willChangeValueForKey:@"coordinates"];
|
[self willChangeValueForKey:@"coordinates"];
|
||||||
@@ -130,20 +121,22 @@
|
|||||||
[self didChangeValueForKey:@"coordinates"];
|
[self didChangeValueForKey:@"coordinates"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)appendCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count
|
- (void)appendCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Appending %lu coordinates.", (unsigned long)count);
|
MLNLogDebug(@"Appending %lu coordinates.", (unsigned long)count);
|
||||||
[self insertCoordinates:coords count:count atIndex:_coordinates.size()];
|
[self insertCoordinates:coords count:count atIndex:_coordinates.size()];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)replaceCoordinatesInRange:(NSRange)range withCoordinates:(const CLLocationCoordinate2D *)coords
|
- (void)replaceCoordinatesInRange:(NSRange)range
|
||||||
{
|
withCoordinates:(const CLLocationCoordinate2D *)coords {
|
||||||
MLNLogDebug(@"Replacing coordinates in range: %@", NSStringFromRange(range));
|
MLNLogDebug(@"Replacing coordinates in range: %@", NSStringFromRange(range));
|
||||||
[self replaceCoordinatesInRange:range withCoordinates:coords count:range.length];
|
[self replaceCoordinatesInRange:range withCoordinates:coords count:range.length];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)replaceCoordinatesInRange:(NSRange)range withCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count {
|
- (void)replaceCoordinatesInRange:(NSRange)range
|
||||||
MLNLogDebug(@"Replacing %lu coordinates in range %@.", (unsigned long)count, NSStringFromRange(range));
|
withCoordinates:(const CLLocationCoordinate2D *)coords
|
||||||
|
count:(NSUInteger)count {
|
||||||
|
MLNLogDebug(@"Replacing %lu coordinates in range %@.", (unsigned long)count,
|
||||||
|
NSStringFromRange(range));
|
||||||
if (!count && !range.length) {
|
if (!count && !range.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -157,9 +150,11 @@
|
|||||||
[self willChangeValueForKey:@"coordinates"];
|
[self willChangeValueForKey:@"coordinates"];
|
||||||
std::copy(coords, coords + MIN(count, range.length), _coordinates.begin() + range.location);
|
std::copy(coords, coords + MIN(count, range.length), _coordinates.begin() + range.location);
|
||||||
if (count >= range.length) {
|
if (count >= range.length) {
|
||||||
_coordinates.insert(_coordinates.begin() + NSMaxRange(range), coords, coords + count - range.length);
|
_coordinates.insert(_coordinates.begin() + NSMaxRange(range), coords,
|
||||||
|
coords + count - range.length);
|
||||||
} else {
|
} else {
|
||||||
_coordinates.erase(_coordinates.begin() + range.location + count, _coordinates.begin() + NSMaxRange(range));
|
_coordinates.erase(_coordinates.begin() + range.location + count,
|
||||||
|
_coordinates.begin() + NSMaxRange(range));
|
||||||
}
|
}
|
||||||
_bounds = {};
|
_bounds = {};
|
||||||
[self didChangeValueForKey:@"coordinates"];
|
[self didChangeValueForKey:@"coordinates"];
|
||||||
@@ -171,8 +166,7 @@
|
|||||||
[self replaceCoordinatesInRange:range withCoordinates:&coords count:0];
|
[self replaceCoordinatesInRange:range withCoordinates:&coords count:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MLNCoordinateBounds)overlayBounds
|
- (MLNCoordinateBounds)overlayBounds {
|
||||||
{
|
|
||||||
if (!_bounds) {
|
if (!_bounds) {
|
||||||
mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
|
mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
|
||||||
for (auto coordinate : _coordinates) {
|
for (auto coordinate : _coordinates) {
|
||||||
@@ -187,21 +181,20 @@
|
|||||||
return MLNCoordinateBoundsFromLatLngBounds(*_bounds);
|
return MLNCoordinateBoundsFromLatLngBounds(*_bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)intersectsOverlayBounds:(MLNCoordinateBounds)overlayBounds
|
- (BOOL)intersectsOverlayBounds:(MLNCoordinateBounds)overlayBounds {
|
||||||
{
|
|
||||||
return MLNCoordinateBoundsIntersectsCoordinateBounds(self.overlayBounds, overlayBounds);
|
return MLNCoordinateBoundsIntersectsCoordinateBounds(self.overlayBounds, overlayBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::Annotation)annotationObjectWithDelegate:(__unused id <MLNMultiPointDelegate>)delegate
|
- (mbgl::Annotation)annotationObjectWithDelegate:(__unused id<MLNMultiPointDelegate>)delegate {
|
||||||
{
|
MLNAssert(NO, @"Cannot add an annotation from an instance of %@",
|
||||||
MLNAssert(NO, @"Cannot add an annotation from an instance of %@", NSStringFromClass([self class]));
|
NSStringFromClass([self class]));
|
||||||
return mbgl::SymbolAnnotation(mbgl::Point<double>());
|
return mbgl::SymbolAnnotation(mbgl::Point<double>());
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
|
||||||
return [NSString stringWithFormat:@"<%@: %p; count = %lu; bounds = %@>",
|
return [NSString stringWithFormat:@"<%@: %p; count = %lu; bounds = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self, (unsigned long)[self pointCount],
|
NSStringFromClass([self class]), (void *)self,
|
||||||
|
(unsigned long)[self pointCount],
|
||||||
MLNStringFromCoordinateBounds(self.overlayBounds)];
|
MLNStringFromCoordinateBounds(self.overlayBounds)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
#import "MLNNetworkConfiguration_Private.h"
|
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
#import "MLNNetworkConfiguration_Private.h"
|
||||||
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
||||||
#import "MLNSettings_Private.h"
|
#import "MLNSettings_Private.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#import "MLNReachability.h"
|
#import "MLNReachability.h"
|
||||||
|
|
||||||
static NSString * const MLNStartTime = @"start_time";
|
static NSString *const MLNStartTime = @"start_time";
|
||||||
static NSString * const MLNResourceType = @"resource_type";
|
static NSString *const MLNResourceType = @"resource_type";
|
||||||
NSString * const kMLNDownloadPerformanceEvent = @"mobile.performance_trace";
|
NSString *const kMLNDownloadPerformanceEvent = @"mobile.performance_trace";
|
||||||
|
|
||||||
@interface MLNNetworkConfiguration () <MLNNativeNetworkDelegate>
|
@interface MLNNetworkConfiguration () <MLNNativeNetworkDelegate>
|
||||||
|
|
||||||
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSDictionary*> *events;
|
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSDictionary *> *events;
|
||||||
@property (nonatomic, weak) id<MLNNetworkConfigurationMetricsDelegate> metricsDelegate;
|
@property (nonatomic, weak) id<MLNNetworkConfigurationMetricsDelegate> metricsDelegate;
|
||||||
@property (nonatomic) dispatch_queue_t eventsQueue;
|
@property (nonatomic) dispatch_queue_t eventsQueue;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MLNNetworkConfiguration
|
@implementation MLNNetworkConfiguration {
|
||||||
{
|
|
||||||
NSURLSessionConfiguration *_sessionConfig;
|
NSURLSessionConfiguration *_sessionConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +26,8 @@ NSString * const kMLNDownloadPerformanceEvent = @"mobile.performance_trace";
|
|||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
self.sessionConfiguration = nil;
|
self.sessionConfiguration = nil;
|
||||||
_events = [NSMutableDictionary dictionary];
|
_events = [NSMutableDictionary dictionary];
|
||||||
_eventsQueue = dispatch_queue_create("org.maplibre.network-configuration", DISPATCH_QUEUE_CONCURRENT);
|
_eventsQueue =
|
||||||
|
dispatch_queue_create("org.maplibre.network-configuration", DISPATCH_QUEUE_CONCURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -54,7 +54,8 @@ NSString * const kMLNDownloadPerformanceEvent = @"mobile.performance_trace";
|
|||||||
}
|
}
|
||||||
|
|
||||||
+ (NSURLSessionConfiguration *)defaultSessionConfiguration {
|
+ (NSURLSessionConfiguration *)defaultSessionConfiguration {
|
||||||
NSURLSessionConfiguration* sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
NSURLSessionConfiguration *sessionConfiguration =
|
||||||
|
[NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
|
|
||||||
sessionConfiguration.timeoutIntervalForResource = 30;
|
sessionConfiguration.timeoutIntervalForResource = 30;
|
||||||
sessionConfiguration.HTTPMaximumConnectionsPerHost = 8;
|
sessionConfiguration.HTTPMaximumConnectionsPerHost = 8;
|
||||||
@@ -88,48 +89,42 @@ NSString * const kMLNDownloadPerformanceEvent = @"mobile.performance_trace";
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSMutableURLRequest *)willSendRequest:(NSMutableURLRequest *)request {
|
- (NSMutableURLRequest *)willSendRequest:(NSMutableURLRequest *)request {
|
||||||
|
|
||||||
if ([self.delegate respondsToSelector:@selector(willSendRequest:)]) {
|
if ([self.delegate respondsToSelector:@selector(willSendRequest:)]) {
|
||||||
return [self.delegate willSendRequest:request];
|
return [self.delegate willSendRequest:request];
|
||||||
}
|
}
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MLNInternalNetworkResponse *)didReceiveResponse:(MLNInternalNetworkResponse *)response {
|
- (MLNInternalNetworkResponse *)didReceiveResponse:(MLNInternalNetworkResponse *)response {
|
||||||
|
|
||||||
if ([self.delegate respondsToSelector:@selector(didReceiveResponse:)]) {
|
if ([self.delegate respondsToSelector:@selector(didReceiveResponse:)]) {
|
||||||
|
|
||||||
MLNNetworkResponse *tempResponse = [MLNNetworkResponse responseWithData:response.data
|
MLNNetworkResponse *tempResponse = [MLNNetworkResponse responseWithData:response.data
|
||||||
urlResponse:response.response
|
urlResponse:response.response
|
||||||
error:response.error];
|
error:response.error];
|
||||||
if (tempResponse) {
|
if (tempResponse) {
|
||||||
MLNInternalNetworkResponse *internalResponse = [MLNInternalNetworkResponse responseWithData:tempResponse.data
|
MLNInternalNetworkResponse *internalResponse =
|
||||||
|
[MLNInternalNetworkResponse responseWithData:tempResponse.data
|
||||||
urlResponse:tempResponse.response
|
urlResponse:tempResponse.response
|
||||||
error:tempResponse.error];
|
error:tempResponse.error];
|
||||||
return internalResponse;
|
return internalResponse;
|
||||||
} else {
|
} else {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSURLSessionConfiguration *)sessionConfiguration {
|
- (NSURLSessionConfiguration *)sessionConfiguration {
|
||||||
NSURLSessionConfiguration *sessionConfig = nil;
|
NSURLSessionConfiguration *sessionConfig = nil;
|
||||||
@synchronized (self) {
|
@synchronized(self) {
|
||||||
sessionConfig = _sessionConfig;
|
sessionConfig = _sessionConfig;
|
||||||
}
|
}
|
||||||
return sessionConfig;
|
return sessionConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration {
|
- (void)setSessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration {
|
||||||
@synchronized (self) {
|
@synchronized(self) {
|
||||||
if (sessionConfiguration == nil) {
|
if (sessionConfiguration == nil) {
|
||||||
_sessionConfig = [MLNNetworkConfiguration defaultSessionConfiguration];
|
_sessionConfig = [MLNNetworkConfiguration defaultSessionConfiguration];
|
||||||
} else {
|
} else {
|
||||||
@@ -141,7 +136,7 @@ NSString * const kMLNDownloadPerformanceEvent = @"mobile.performance_trace";
|
|||||||
- (void)startDownloadEvent:(NSString *)urlString type:(NSString *)resourceType {
|
- (void)startDownloadEvent:(NSString *)urlString type:(NSString *)resourceType {
|
||||||
if (urlString && resourceType && ![self eventDictionaryForKey:urlString]) {
|
if (urlString && resourceType && ![self eventDictionaryForKey:urlString]) {
|
||||||
NSDate *startDate = [NSDate date];
|
NSDate *startDate = [NSDate date];
|
||||||
[self setEventDictionary:@{ MLNStartTime: startDate, MLNResourceType: resourceType }
|
[self setEventDictionary:@{MLNStartTime : startDate, MLNResourceType : resourceType}
|
||||||
forKey:urlString];
|
forKey:urlString];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,8 +159,7 @@ NSString * const kMLNDownloadPerformanceEvent = @"mobile.performance_trace";
|
|||||||
|
|
||||||
// MARK: - Event management
|
// MARK: - Event management
|
||||||
|
|
||||||
- (void)sendEventForURLResponse:(NSURLResponse *)response withAction:(NSString *)action
|
- (void)sendEventForURLResponse:(NSURLResponse *)response withAction:(NSString *)action {
|
||||||
{
|
|
||||||
if ([response isKindOfClass:[NSURLResponse class]]) {
|
if ([response isKindOfClass:[NSURLResponse class]]) {
|
||||||
NSString *urlString = response.URL.relativePath;
|
NSString *urlString = response.URL.relativePath;
|
||||||
if (urlString && [self eventDictionaryForKey:urlString]) {
|
if (urlString && [self eventDictionaryForKey:urlString]) {
|
||||||
@@ -179,31 +173,31 @@ NSString * const kMLNDownloadPerformanceEvent = @"mobile.performance_trace";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)eventAttributesForURL:(NSURLResponse *)response withAction:(NSString *)action
|
- (NSDictionary *)eventAttributesForURL:(NSURLResponse *)response withAction:(NSString *)action {
|
||||||
{
|
|
||||||
NSString *urlString = response.URL.relativePath;
|
NSString *urlString = response.URL.relativePath;
|
||||||
NSDictionary *parameters = [self eventDictionaryForKey:urlString];
|
NSDictionary *parameters = [self eventDictionaryForKey:urlString];
|
||||||
NSDate *startDate = [parameters objectForKey:MLNStartTime];
|
NSDate *startDate = [parameters objectForKey:MLNStartTime];
|
||||||
NSDate *endDate = [NSDate date];
|
NSDate *endDate = [NSDate date];
|
||||||
NSTimeInterval elapsedTime = [endDate timeIntervalSinceDate:startDate];
|
NSTimeInterval elapsedTime = [endDate timeIntervalSinceDate:startDate];
|
||||||
NSDateFormatter* iso8601Formatter = [[NSDateFormatter alloc] init];
|
NSDateFormatter *iso8601Formatter = [[NSDateFormatter alloc] init];
|
||||||
iso8601Formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
|
iso8601Formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
|
||||||
NSString *createdDate = [iso8601Formatter stringFromDate:[NSDate date]];
|
NSString *createdDate = [iso8601Formatter stringFromDate:[NSDate date]];
|
||||||
|
|
||||||
NSMutableArray *attributes = [NSMutableArray array];
|
NSMutableArray *attributes = [NSMutableArray array];
|
||||||
[attributes addObject:@{ @"name" : @"requestUrl" , @"value" : urlString }];
|
[attributes addObject:@{@"name" : @"requestUrl", @"value" : urlString}];
|
||||||
[attributes addObject:@{ @"name" : MLNResourceType , @"value" : [parameters objectForKey:MLNResourceType] }];
|
[attributes
|
||||||
|
addObject:@{@"name" : MLNResourceType, @"value" : [parameters objectForKey:MLNResourceType]}];
|
||||||
|
|
||||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||||
NSInteger responseCode = [(NSHTTPURLResponse *)response statusCode];
|
NSInteger responseCode = [(NSHTTPURLResponse *)response statusCode];
|
||||||
[attributes addObject:@{ @"name" : @"responseCode", @"value" : @(responseCode)}];
|
[attributes addObject:@{@"name" : @"responseCode", @"value" : @(responseCode)}];
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL isWIFIOn = [[MLNReachability reachabilityWithHostName:response.URL.host] isReachableViaWiFi];
|
BOOL isWIFIOn = [[MLNReachability reachabilityWithHostName:response.URL.host] isReachableViaWiFi];
|
||||||
[attributes addObject:@{ @"name" : @"wifiOn", @"value" : @(isWIFIOn)}];
|
[attributes addObject:@{@"name" : @"wifiOn", @"value" : @(isWIFIOn)}];
|
||||||
|
|
||||||
if (action) {
|
if (action) {
|
||||||
[attributes addObject:@{ @"name" : @"action" , @"value" : action }];
|
[attributes addObject:@{@"name" : @"action", @"value" : action}];
|
||||||
}
|
}
|
||||||
|
|
||||||
double elapsedTimeInMS = elapsedTime * 1000.0;
|
double elapsedTimeInMS = elapsedTime * 1000.0;
|
||||||
@@ -212,14 +206,14 @@ NSString * const kMLNDownloadPerformanceEvent = @"mobile.performance_trace";
|
|||||||
@"event" : kMLNDownloadPerformanceEvent,
|
@"event" : kMLNDownloadPerformanceEvent,
|
||||||
@"created" : createdDate,
|
@"created" : createdDate,
|
||||||
@"sessionId" : [NSUUID UUID].UUIDString,
|
@"sessionId" : [NSUUID UUID].UUIDString,
|
||||||
@"counters" : @[ @{ @"name" : @"elapsedMS" , @"value" : @(elapsedTimeInMS) } ],
|
@"counters" : @[ @{@"name" : @"elapsedMS", @"value" : @(elapsedTimeInMS)} ],
|
||||||
@"attributes" : attributes
|
@"attributes" : attributes
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Events dictionary access
|
// MARK: - Events dictionary access
|
||||||
|
|
||||||
- (nullable NSDictionary*)eventDictionaryForKey:(nonnull NSString*)key {
|
- (nullable NSDictionary *)eventDictionaryForKey:(nonnull NSString *)key {
|
||||||
__block NSDictionary *dictionary;
|
__block NSDictionary *dictionary;
|
||||||
|
|
||||||
dispatch_sync(self.eventsQueue, ^{
|
dispatch_sync(self.eventsQueue, ^{
|
||||||
@@ -229,13 +223,13 @@ NSString * const kMLNDownloadPerformanceEvent = @"mobile.performance_trace";
|
|||||||
return dictionary;
|
return dictionary;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setEventDictionary:(nonnull NSDictionary*)dictionary forKey:(nonnull NSString*)key {
|
- (void)setEventDictionary:(nonnull NSDictionary *)dictionary forKey:(nonnull NSString *)key {
|
||||||
dispatch_barrier_async(self.eventsQueue, ^{
|
dispatch_barrier_async(self.eventsQueue, ^{
|
||||||
[self.events setObject:dictionary forKey:key];
|
[self.events setObject:dictionary forKey:key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeEventDictionaryForKey:(nonnull NSString*)key {
|
- (void)removeEventDictionaryForKey:(nonnull NSString *)key {
|
||||||
dispatch_barrier_async(self.eventsQueue, ^{
|
dispatch_barrier_async(self.eventsQueue, ^{
|
||||||
[self.events removeObjectForKey:key];
|
[self.events removeObjectForKey:key];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import "MLNNetworkResponse.h"
|
#import "MLNNetworkResponse.h"
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
@implementation MLNNetworkResponse
|
@implementation MLNNetworkResponse
|
||||||
|
|
||||||
+(MLNNetworkResponse *)responseWithData:(NSData *)data
|
+ (MLNNetworkResponse *)responseWithData:(NSData *)data
|
||||||
urlResponse:(NSURLResponse *)response
|
urlResponse:(NSURLResponse *)response
|
||||||
error:(NSError *)error {
|
error:(NSError *)error {
|
||||||
|
|
||||||
MLNNetworkResponse *tempResult = [[MLNNetworkResponse alloc] init];
|
MLNNetworkResponse *tempResult = [[MLNNetworkResponse alloc] init];
|
||||||
tempResult.data = data;
|
tempResult.data = data;
|
||||||
tempResult.response = response;
|
tempResult.response = response;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
#import "MLNOfflinePack_Private.h"
|
#import "MLNOfflinePack_Private.h"
|
||||||
|
|
||||||
#import "MLNOfflineStorage_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
#import "MLNOfflineRegion_Private.h"
|
#import "MLNOfflineRegion_Private.h"
|
||||||
#import "MLNTilePyramidOfflineRegion.h"
|
#import "MLNOfflineStorage_Private.h"
|
||||||
#import "MLNTilePyramidOfflineRegion_Private.h"
|
|
||||||
#import "MLNShapeOfflineRegion.h"
|
#import "MLNShapeOfflineRegion.h"
|
||||||
#import "MLNShapeOfflineRegion_Private.h"
|
#import "MLNShapeOfflineRegion_Private.h"
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNTilePyramidOfflineRegion.h"
|
||||||
|
#import "MLNTilePyramidOfflineRegion_Private.h"
|
||||||
|
|
||||||
#import "NSValue+MLNAdditions.h"
|
#import "NSValue+MLNAdditions.h"
|
||||||
|
|
||||||
@@ -25,15 +25,16 @@ const MLNExceptionName MLNInvalidOfflinePackException = @"MLNInvalidOfflinePackE
|
|||||||
#define MLNAssertOfflinePackIsValid() \
|
#define MLNAssertOfflinePackIsValid() \
|
||||||
do { \
|
do { \
|
||||||
if (_state == MLNOfflinePackStateInvalid) { \
|
if (_state == MLNOfflinePackStateInvalid) { \
|
||||||
[NSException raise:MLNInvalidOfflinePackException \
|
[NSException \
|
||||||
format: \
|
raise:MLNInvalidOfflinePackException \
|
||||||
@"-[MLNOfflineStorage removePack:withCompletionHandler:] has been called " \
|
format:@"-[MLNOfflineStorage removePack:withCompletionHandler:] has been called " \
|
||||||
@"on this instance of MLNOfflinePack, rendering it invalid. It is an " \
|
@"on this instance of MLNOfflinePack, rendering it invalid. It is an " \
|
||||||
@"error to send any message to this pack."]; \
|
@"error to send any message to this pack."]; \
|
||||||
} \
|
} \
|
||||||
} while (NO);
|
} while (NO);
|
||||||
|
|
||||||
@interface MLNTilePyramidOfflineRegion () <MLNOfflineRegion_Private, MLNTilePyramidOfflineRegion_Private>
|
@interface MLNTilePyramidOfflineRegion () <MLNOfflineRegion_Private,
|
||||||
|
MLNTilePyramidOfflineRegion_Private>
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MLNShapeOfflineRegion () <MLNOfflineRegion_Private, MLNShapeOfflineRegion_Private>
|
@interface MLNShapeOfflineRegion () <MLNOfflineRegion_Private, MLNShapeOfflineRegion_Private>
|
||||||
@@ -67,7 +68,9 @@ private:
|
|||||||
MLNLogInfo(@"Calling this initializer is not allowed.");
|
MLNLogInfo(@"Calling this initializer is not allowed.");
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
_state = MLNOfflinePackStateInvalid;
|
_state = MLNOfflinePackStateInvalid;
|
||||||
NSLog(@"%s called; did you mean to call +[MLNOfflineStorage addPackForRegion:withContext:completionHandler:] instead?", __PRETTY_FUNCTION__);
|
NSLog(@"%s called; did you mean to call +[MLNOfflineStorage "
|
||||||
|
@"addPackForRegion:withContext:completionHandler:] instead?",
|
||||||
|
__PRETTY_FUNCTION__);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -78,30 +81,36 @@ private:
|
|||||||
_state = MLNOfflinePackStateUnknown;
|
_state = MLNOfflinePackStateUnknown;
|
||||||
|
|
||||||
_mbglDatabaseFileSource = [[MLNOfflineStorage sharedOfflineStorage] mbglDatabaseFileSource];
|
_mbglDatabaseFileSource = [[MLNOfflineStorage sharedOfflineStorage] mbglDatabaseFileSource];
|
||||||
_mbglDatabaseFileSource->setOfflineRegionObserver(*_mbglOfflineRegion, std::make_unique<MBGLOfflineRegionObserver>(self));
|
_mbglDatabaseFileSource->setOfflineRegionObserver(
|
||||||
|
*_mbglOfflineRegion, std::make_unique<MBGLOfflineRegionObserver>(self));
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
MLNAssert(_state == MLNOfflinePackStateInvalid, @"MLNOfflinePack was not invalided prior to deallocation.");
|
MLNAssert(_state == MLNOfflinePackStateInvalid,
|
||||||
|
@"MLNOfflinePack was not invalided prior to deallocation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id <MLNOfflineRegion>)region {
|
- (id<MLNOfflineRegion>)region {
|
||||||
MLNAssertOfflinePackIsValid();
|
MLNAssertOfflinePackIsValid();
|
||||||
|
|
||||||
const mbgl::OfflineRegionDefinition ®ionDefinition = _mbglOfflineRegion->getDefinition();
|
const mbgl::OfflineRegionDefinition ®ionDefinition = _mbglOfflineRegion->getDefinition();
|
||||||
MLNAssert([MLNTilePyramidOfflineRegion conformsToProtocol:@protocol(MLNOfflineRegion_Private)], @"MLNTilePyramidOfflineRegion should conform to MLNOfflineRegion_Private.");
|
MLNAssert([MLNTilePyramidOfflineRegion conformsToProtocol:@protocol(MLNOfflineRegion_Private)],
|
||||||
MLNAssert([MLNShapeOfflineRegion conformsToProtocol:@protocol(MLNOfflineRegion_Private)], @"MLNShapeOfflineRegion should conform to MLNOfflineRegion_Private.");
|
@"MLNTilePyramidOfflineRegion should conform to MLNOfflineRegion_Private.");
|
||||||
|
MLNAssert([MLNShapeOfflineRegion conformsToProtocol:@protocol(MLNOfflineRegion_Private)],
|
||||||
|
@"MLNShapeOfflineRegion should conform to MLNOfflineRegion_Private.");
|
||||||
|
|
||||||
return std::visit(mbgl::overloaded{
|
return std::visit(
|
||||||
[&] (const mbgl::OfflineTilePyramidRegionDefinition def){
|
mbgl::overloaded{[&](const mbgl::OfflineTilePyramidRegionDefinition def) {
|
||||||
return (id <MLNOfflineRegion>)[[MLNTilePyramidOfflineRegion alloc] initWithOfflineRegionDefinition:def];
|
return (id<MLNOfflineRegion>)[[MLNTilePyramidOfflineRegion alloc]
|
||||||
|
initWithOfflineRegionDefinition:def];
|
||||||
},
|
},
|
||||||
[&] (const mbgl::OfflineGeometryRegionDefinition& def){
|
[&](const mbgl::OfflineGeometryRegionDefinition &def) {
|
||||||
return (id <MLNOfflineRegion>)[[MLNShapeOfflineRegion alloc] initWithOfflineRegionDefinition:def];
|
return (id<MLNOfflineRegion>)[[MLNShapeOfflineRegion alloc]
|
||||||
}
|
initWithOfflineRegionDefinition:def];
|
||||||
}, regionDefinition);
|
}},
|
||||||
|
regionDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSData *)context {
|
- (NSData *)context {
|
||||||
@@ -111,7 +120,8 @@ private:
|
|||||||
return [NSData dataWithBytes:&metadata[0] length:metadata.size()];
|
return [NSData dataWithBytes:&metadata[0] length:metadata.size()];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setContext:(NSData *)context completionHandler:(void (^_Nullable)(NSError * _Nullable error))completion {
|
- (void)setContext:(NSData *)context
|
||||||
|
completionHandler:(void (^_Nullable)(NSError *_Nullable error))completion {
|
||||||
MLNAssertOfflinePackIsValid();
|
MLNAssertOfflinePackIsValid();
|
||||||
|
|
||||||
mbgl::OfflineRegionMetadata metadata(context.length);
|
mbgl::OfflineRegionMetadata metadata(context.length);
|
||||||
@@ -119,16 +129,23 @@ private:
|
|||||||
|
|
||||||
[self willChangeValueForKey:@"context"];
|
[self willChangeValueForKey:@"context"];
|
||||||
__weak MLNOfflinePack *weakSelf = self;
|
__weak MLNOfflinePack *weakSelf = self;
|
||||||
_mbglDatabaseFileSource->updateOfflineMetadata(_mbglOfflineRegion->getID(), metadata, [&, completion, weakSelf](mbgl::expected<mbgl::OfflineRegionMetadata, std::exception_ptr> mbglOfflineRegionMetadata) {
|
_mbglDatabaseFileSource->updateOfflineMetadata(
|
||||||
|
_mbglOfflineRegion->getID(), metadata,
|
||||||
|
[&, completion, weakSelf](mbgl::expected<mbgl::OfflineRegionMetadata, std::exception_ptr>
|
||||||
|
mbglOfflineRegionMetadata) {
|
||||||
NSError *error;
|
NSError *error;
|
||||||
if (!mbglOfflineRegionMetadata) {
|
if (!mbglOfflineRegionMetadata) {
|
||||||
NSString *errorDescription = @(mbgl::util::toString(mbglOfflineRegionMetadata.error()).c_str());
|
NSString *errorDescription =
|
||||||
error = [NSError errorWithDomain:MLNErrorDomain code:MLNErrorCodeModifyingOfflineStorageFailed userInfo:errorDescription ? @{
|
@(mbgl::util::toString(mbglOfflineRegionMetadata.error()).c_str());
|
||||||
NSLocalizedDescriptionKey: errorDescription,
|
error = [NSError errorWithDomain:MLNErrorDomain
|
||||||
} : nil];
|
code:MLNErrorCodeModifyingOfflineStorageFailed
|
||||||
|
userInfo:errorDescription ? @{
|
||||||
|
NSLocalizedDescriptionKey : errorDescription,
|
||||||
|
}
|
||||||
|
: nil];
|
||||||
}
|
}
|
||||||
dispatch_async(dispatch_get_main_queue(), [&, completion, weakSelf, error](void) {
|
dispatch_async(dispatch_get_main_queue(), [&, completion, weakSelf, error](void) {
|
||||||
[weakSelf reloadWithCompletionHandler:^(NSError * _Nullable reloadingError) {
|
[weakSelf reloadWithCompletionHandler:^(NSError *_Nullable reloadingError) {
|
||||||
MLNOfflinePack *strongSelf = weakSelf;
|
MLNOfflinePack *strongSelf = weakSelf;
|
||||||
[strongSelf didChangeValueForKey:@"context"];
|
[strongSelf didChangeValueForKey:@"context"];
|
||||||
if (completion) {
|
if (completion) {
|
||||||
@@ -139,11 +156,12 @@ private:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reloadWithCompletionHandler:(void (^)(NSError * _Nullable error))completion {
|
- (void)reloadWithCompletionHandler:(void (^)(NSError *_Nullable error))completion {
|
||||||
auto regionID = _mbglOfflineRegion->getID();
|
auto regionID = _mbglOfflineRegion->getID();
|
||||||
MLNOfflineStorage *sharedOfflineStorage = [MLNOfflineStorage sharedOfflineStorage];
|
MLNOfflineStorage *sharedOfflineStorage = [MLNOfflineStorage sharedOfflineStorage];
|
||||||
__weak MLNOfflinePack *weakSelf = self;
|
__weak MLNOfflinePack *weakSelf = self;
|
||||||
[sharedOfflineStorage getPacksWithCompletionHandler:^(NSArray<MLNOfflinePack *> *packs, __unused NSError * _Nullable error) {
|
[sharedOfflineStorage getPacksWithCompletionHandler:^(NSArray<MLNOfflinePack *> *packs,
|
||||||
|
__unused NSError *_Nullable error) {
|
||||||
MLNOfflinePack *strongSelf = weakSelf;
|
MLNOfflinePack *strongSelf = weakSelf;
|
||||||
for (MLNOfflinePack *pack in packs) {
|
for (MLNOfflinePack *pack in packs) {
|
||||||
if (pack.mbglOfflineRegion->getID() == regionID) {
|
if (pack.mbglOfflineRegion->getID() == regionID) {
|
||||||
@@ -161,7 +179,8 @@ private:
|
|||||||
|
|
||||||
self.state = MLNOfflinePackStateActive;
|
self.state = MLNOfflinePackStateActive;
|
||||||
|
|
||||||
_mbglDatabaseFileSource->setOfflineRegionDownloadState(*_mbglOfflineRegion, mbgl::OfflineRegionDownloadState::Active);
|
_mbglDatabaseFileSource->setOfflineRegionDownloadState(*_mbglOfflineRegion,
|
||||||
|
mbgl::OfflineRegionDownloadState::Active);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)suspend {
|
- (void)suspend {
|
||||||
@@ -173,15 +192,17 @@ private:
|
|||||||
_isSuspending = YES;
|
_isSuspending = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
_mbglDatabaseFileSource->setOfflineRegionDownloadState(*_mbglOfflineRegion, mbgl::OfflineRegionDownloadState::Inactive);
|
_mbglDatabaseFileSource->setOfflineRegionDownloadState(
|
||||||
|
*_mbglOfflineRegion, mbgl::OfflineRegionDownloadState::Inactive);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)invalidate {
|
- (void)invalidate {
|
||||||
MLNLogInfo(@"Invalidating pack.");
|
MLNLogInfo(@"Invalidating pack.");
|
||||||
MLNAssert(_state != MLNOfflinePackStateInvalid, @"Cannot invalidate an already invalid offline pack.");
|
MLNAssert(_state != MLNOfflinePackStateInvalid,
|
||||||
|
@"Cannot invalidate an already invalid offline pack.");
|
||||||
MLNAssert(self.mbglOfflineRegion, @"Should have a valid region");
|
MLNAssert(self.mbglOfflineRegion, @"Should have a valid region");
|
||||||
|
|
||||||
@synchronized (self) {
|
@synchronized(self) {
|
||||||
self.state = MLNOfflinePackStateInvalid;
|
self.state = MLNOfflinePackStateInvalid;
|
||||||
if (self.mbglOfflineRegion) {
|
if (self.mbglOfflineRegion) {
|
||||||
_mbglDatabaseFileSource->setOfflineRegionObserver(*self.mbglOfflineRegion, nullptr);
|
_mbglDatabaseFileSource->setOfflineRegionObserver(*self.mbglOfflineRegion, nullptr);
|
||||||
@@ -196,11 +217,13 @@ private:
|
|||||||
// A progress update has arrived after the call to
|
// A progress update has arrived after the call to
|
||||||
// -[MLNOfflineStorage removePack:withCompletionHandler:] but before the
|
// -[MLNOfflineStorage removePack:withCompletionHandler:] but before the
|
||||||
// removal is complete and the completion handler is called.
|
// removal is complete and the completion handler is called.
|
||||||
MLNAssert(_state == MLNOfflinePackStateInvalid, @"A valid MLNOfflinePack has no mbgl::OfflineRegion.");
|
MLNAssert(_state == MLNOfflinePackStateInvalid,
|
||||||
|
@"A valid MLNOfflinePack has no mbgl::OfflineRegion.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNAssert(_state != MLNOfflinePackStateInvalid, @"Cannot change the state of an invalid offline pack.");
|
MLNAssert(_state != MLNOfflinePackStateInvalid,
|
||||||
|
@"Cannot change the state of an invalid offline pack.");
|
||||||
|
|
||||||
if (!_isSuspending || state != MLNOfflinePackStateActive) {
|
if (!_isSuspending || state != MLNOfflinePackStateActive) {
|
||||||
_isSuspending = NO;
|
_isSuspending = NO;
|
||||||
@@ -213,7 +236,9 @@ private:
|
|||||||
MLNAssertOfflinePackIsValid();
|
MLNAssertOfflinePackIsValid();
|
||||||
|
|
||||||
__weak MLNOfflinePack *weakSelf = self;
|
__weak MLNOfflinePack *weakSelf = self;
|
||||||
_mbglDatabaseFileSource->getOfflineRegionStatus(*_mbglOfflineRegion, [&, weakSelf](mbgl::expected<mbgl::OfflineRegionStatus, std::exception_ptr> status) {
|
_mbglDatabaseFileSource->getOfflineRegionStatus(
|
||||||
|
*_mbglOfflineRegion,
|
||||||
|
[&, weakSelf](mbgl::expected<mbgl::OfflineRegionStatus, std::exception_ptr> status) {
|
||||||
if (status) {
|
if (status) {
|
||||||
mbgl::OfflineRegionStatus checkedStatus = *status;
|
mbgl::OfflineRegionStatus checkedStatus = *status;
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
@@ -225,7 +250,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)offlineRegionStatusDidChange:(mbgl::OfflineRegionStatus)status {
|
- (void)offlineRegionStatusDidChange:(mbgl::OfflineRegionStatus)status {
|
||||||
MLNAssert(_state != MLNOfflinePackStateInvalid, @"Cannot change update progress of an invalid offline pack.");
|
MLNAssert(_state != MLNOfflinePackStateInvalid,
|
||||||
|
@"Cannot change update progress of an invalid offline pack.");
|
||||||
|
|
||||||
switch (status.downloadState) {
|
switch (status.downloadState) {
|
||||||
case mbgl::OfflineRegionDownloadState::Inactive:
|
case mbgl::OfflineRegionDownloadState::Inactive:
|
||||||
@@ -247,11 +273,14 @@ private:
|
|||||||
progress.countOfTilesCompleted = status.completedTileCount;
|
progress.countOfTilesCompleted = status.completedTileCount;
|
||||||
progress.countOfTileBytesCompleted = status.completedTileSize;
|
progress.countOfTileBytesCompleted = status.completedTileSize;
|
||||||
progress.countOfResourcesExpected = status.requiredResourceCount;
|
progress.countOfResourcesExpected = status.requiredResourceCount;
|
||||||
progress.maximumResourcesExpected = status.requiredResourceCountIsPrecise ? status.requiredResourceCount : UINT64_MAX;
|
progress.maximumResourcesExpected =
|
||||||
|
status.requiredResourceCountIsPrecise ? status.requiredResourceCount : UINT64_MAX;
|
||||||
self.progress = progress;
|
self.progress = progress;
|
||||||
|
|
||||||
NSDictionary *userInfo = @{MLNOfflinePackUserInfoKeyState: @(self.state),
|
NSDictionary *userInfo = @{
|
||||||
MLNOfflinePackUserInfoKeyProgress: [NSValue valueWithMLNOfflinePackProgress:progress]};
|
MLNOfflinePackUserInfoKeyState : @(self.state),
|
||||||
|
MLNOfflinePackUserInfoKeyProgress : [NSValue valueWithMLNOfflinePackProgress:progress]
|
||||||
|
};
|
||||||
|
|
||||||
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
|
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
|
||||||
[noteCenter postNotificationName:MLNOfflinePackProgressChangedNotification
|
[noteCenter postNotificationName:MLNOfflinePackProgressChangedNotification
|
||||||
@@ -263,16 +292,14 @@ private:
|
|||||||
MLNLogError(@"Error: %@", error.localizedDescription);
|
MLNLogError(@"Error: %@", error.localizedDescription);
|
||||||
MLNLogInfo(@"Notifying about pack error.");
|
MLNLogInfo(@"Notifying about pack error.");
|
||||||
|
|
||||||
NSDictionary *userInfo = @{ MLNOfflinePackUserInfoKeyError: error };
|
NSDictionary *userInfo = @{MLNOfflinePackUserInfoKeyError : error};
|
||||||
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
|
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
|
||||||
[noteCenter postNotificationName:MLNOfflinePackErrorNotification
|
[noteCenter postNotificationName:MLNOfflinePackErrorNotification object:self userInfo:userInfo];
|
||||||
object:self
|
|
||||||
userInfo:userInfo];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)didReceiveMaximumAllowedMapboxTiles:(uint64_t)limit {
|
- (void)didReceiveMaximumAllowedMapboxTiles:(uint64_t)limit {
|
||||||
MLNLogInfo(@"Notifying reached maximum allowed Mapbox tiles: %lu", (unsigned long)limit);
|
MLNLogInfo(@"Notifying reached maximum allowed Mapbox tiles: %lu", (unsigned long)limit);
|
||||||
NSDictionary *userInfo = @{ MLNOfflinePackUserInfoKeyMaximumCount: @(limit) };
|
NSDictionary *userInfo = @{MLNOfflinePackUserInfoKeyMaximumCount : @(limit)};
|
||||||
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
|
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
|
||||||
[noteCenter postNotificationName:MLNOfflinePackMaximumMapboxTilesReachedNotification
|
[noteCenter postNotificationName:MLNOfflinePackMaximumMapboxTilesReachedNotification
|
||||||
object:self
|
object:self
|
||||||
@@ -297,8 +324,10 @@ NSError *MLNErrorFromResponseError(mbgl::Response::Error error) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return [NSError errorWithDomain:MLNErrorDomain code:errorCode userInfo:@{
|
return [NSError errorWithDomain:MLNErrorDomain
|
||||||
NSLocalizedFailureReasonErrorKey: @(error.message.c_str())
|
code:errorCode
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedFailureReasonErrorKey : @(error.message.c_str())
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
#import "MLNOfflineStorage_Private.h"
|
#import "MLNOfflineStorage_Private.h"
|
||||||
|
|
||||||
#import "MLNFoundation_Private.h"
|
#import "MLNFoundation_Private.h"
|
||||||
#import "MLNSettings_Private.h"
|
|
||||||
#import "MLNGeometry_Private.h"
|
#import "MLNGeometry_Private.h"
|
||||||
#import "MLNOfflinePack_Private.h"
|
|
||||||
#import "MLNOfflineRegion_Private.h"
|
|
||||||
#import "MLNTilePyramidOfflineRegion.h"
|
|
||||||
#import "MLNShapeOfflineRegion.h"
|
|
||||||
#import "NSBundle+MLNAdditions.h"
|
|
||||||
#import "NSValue+MLNAdditions.h"
|
|
||||||
#import "NSDate+MLNAdditions.h"
|
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
#import "MLNNetworkConfiguration_Private.h"
|
#import "MLNNetworkConfiguration_Private.h"
|
||||||
|
#import "MLNOfflinePack_Private.h"
|
||||||
|
#import "MLNOfflineRegion_Private.h"
|
||||||
|
#import "MLNSettings_Private.h"
|
||||||
|
#import "MLNShapeOfflineRegion.h"
|
||||||
|
#import "MLNTilePyramidOfflineRegion.h"
|
||||||
|
#import "NSBundle+MLNAdditions.h"
|
||||||
|
#import "NSDate+MLNAdditions.h"
|
||||||
|
#import "NSValue+MLNAdditions.h"
|
||||||
|
|
||||||
#include <mbgl/actor/actor.hpp>
|
#include <mbgl/actor/actor.hpp>
|
||||||
#include <mbgl/actor/scheduler.hpp>
|
#include <mbgl/actor/scheduler.hpp>
|
||||||
@@ -25,13 +25,16 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
static NSString * const MLNOfflineStorageDatabasePathInfoDictionaryKey = @"MLNOfflineStorageDatabasePath";
|
static NSString *const MLNOfflineStorageDatabasePathInfoDictionaryKey =
|
||||||
static NSString * const MLNOfflineStorageFileName = @"cache.db";
|
@"MLNOfflineStorageDatabasePath";
|
||||||
static NSString * const MLNOfflineStorageFileName3_2_0_beta_1 = @"offline.db";
|
static NSString *const MLNOfflineStorageFileName = @"cache.db";
|
||||||
|
static NSString *const MLNOfflineStorageFileName3_2_0_beta_1 = @"offline.db";
|
||||||
|
|
||||||
const NSNotificationName MLNOfflinePackProgressChangedNotification = @"MLNOfflinePackProgressChanged";
|
const NSNotificationName MLNOfflinePackProgressChangedNotification =
|
||||||
|
@"MLNOfflinePackProgressChanged";
|
||||||
const NSNotificationName MLNOfflinePackErrorNotification = @"MLNOfflinePackError";
|
const NSNotificationName MLNOfflinePackErrorNotification = @"MLNOfflinePackError";
|
||||||
const NSNotificationName MLNOfflinePackMaximumMapboxTilesReachedNotification = @"MLNOfflinePackMaximumMapboxTilesReached";
|
const NSNotificationName MLNOfflinePackMaximumMapboxTilesReachedNotification =
|
||||||
|
@"MLNOfflinePackMaximumMapboxTilesReached";
|
||||||
|
|
||||||
const MLNOfflinePackUserInfoKey MLNOfflinePackUserInfoKeyState = @"State";
|
const MLNOfflinePackUserInfoKey MLNOfflinePackUserInfoKeyState = @"State";
|
||||||
const MLNOfflinePackUserInfoKey MLNOfflinePackUserInfoKeyProgress = @"Progress";
|
const MLNOfflinePackUserInfoKey MLNOfflinePackUserInfoKeyProgress = @"Progress";
|
||||||
@@ -60,8 +63,14 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
sharedOfflineStorage = [[self alloc] init];
|
sharedOfflineStorage = [[self alloc] init];
|
||||||
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:sharedOfflineStorage selector:@selector(unpauseFileSource:) name:UIApplicationWillEnterForegroundNotification object:nil];
|
[[NSNotificationCenter defaultCenter] addObserver:sharedOfflineStorage
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:sharedOfflineStorage selector:@selector(pauseFileSource:) name:UIApplicationDidEnterBackgroundNotification object:nil];
|
selector:@selector(unpauseFileSource:)
|
||||||
|
name:UIApplicationWillEnterForegroundNotification
|
||||||
|
object:nil];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:sharedOfflineStorage
|
||||||
|
selector:@selector(pauseFileSource:)
|
||||||
|
name:UIApplicationDidEnterBackgroundNotification
|
||||||
|
object:nil];
|
||||||
#endif
|
#endif
|
||||||
[sharedOfflineStorage reloadPacks];
|
[sharedOfflineStorage reloadPacks];
|
||||||
});
|
});
|
||||||
@@ -102,8 +111,12 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
MLNLogDebug(@"Setting delegate: %@", newValue);
|
MLNLogDebug(@"Setting delegate: %@", newValue);
|
||||||
_delegate = newValue;
|
_delegate = newValue;
|
||||||
if ([self.delegate respondsToSelector:@selector(offlineStorage:URLForResourceOfKind:withURL:)]) {
|
if ([self.delegate respondsToSelector:@selector(offlineStorage:URLForResourceOfKind:withURL:)]) {
|
||||||
_mbglResourceTransform = std::make_unique<mbgl::Actor<mbgl::ResourceTransform::TransformCallback>>(*mbgl::Scheduler::GetCurrent(), [offlineStorage = self](auto kind_, const std::string& url_, mbgl::ResourceTransform::FinishedCallback cb) {
|
_mbglResourceTransform =
|
||||||
NSURL* url =
|
std::make_unique<mbgl::Actor<mbgl::ResourceTransform::TransformCallback>>(
|
||||||
|
*mbgl::Scheduler::GetCurrent(),
|
||||||
|
[offlineStorage = self](auto kind_, const std::string &url_,
|
||||||
|
mbgl::ResourceTransform::FinishedCallback cb) {
|
||||||
|
NSURL *url =
|
||||||
[NSURL URLWithString:[[NSString alloc] initWithBytes:url_.data()
|
[NSURL URLWithString:[[NSString alloc] initWithBytes:url_.data()
|
||||||
length:url_.length()
|
length:url_.length()
|
||||||
encoding:NSUTF8StringEncoding]];
|
encoding:NSUTF8StringEncoding]];
|
||||||
@@ -133,7 +146,6 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
case mbgl::Resource::Kind::Unknown:
|
case mbgl::Resource::Kind::Unknown:
|
||||||
kind = MLNResourceKindUnknown;
|
kind = MLNResourceKindUnknown;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
url = [offlineStorage.delegate offlineStorage:offlineStorage
|
url = [offlineStorage.delegate offlineStorage:offlineStorage
|
||||||
URLForResourceOfKind:kind
|
URLForResourceOfKind:kind
|
||||||
@@ -141,8 +153,11 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
cb(url.absoluteString.UTF8String);
|
cb(url.absoluteString.UTF8String);
|
||||||
});
|
});
|
||||||
|
|
||||||
_mbglOnlineFileSource->setResourceTransform({[actorRef = _mbglResourceTransform->self()](auto kind_, const std::string& url_, mbgl::ResourceTransform::FinishedCallback cb_){
|
_mbglOnlineFileSource->setResourceTransform(
|
||||||
actorRef.invoke(&mbgl::ResourceTransform::TransformCallback::operator(), kind_, url_, std::move(cb_));
|
{[actorRef = _mbglResourceTransform->self()](
|
||||||
|
auto kind_, const std::string &url_, mbgl::ResourceTransform::FinishedCallback cb_) {
|
||||||
|
actorRef.invoke(&mbgl::ResourceTransform::TransformCallback::operator(), kind_, url_,
|
||||||
|
std::move(cb_));
|
||||||
}});
|
}});
|
||||||
} else {
|
} else {
|
||||||
_mbglResourceTransform.reset();
|
_mbglResourceTransform.reset();
|
||||||
@@ -159,28 +174,33 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
MLNInitializeRunLoop();
|
MLNInitializeRunLoop();
|
||||||
|
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
mbgl::TileServerOptions* tileServerOptions = [[MLNSettings sharedSettings] tileServerOptionsInternal];
|
mbgl::TileServerOptions *tileServerOptions =
|
||||||
|
[[MLNSettings sharedSettings] tileServerOptionsInternal];
|
||||||
mbgl::ResourceOptions resourceOptions;
|
mbgl::ResourceOptions resourceOptions;
|
||||||
resourceOptions.withCachePath(self.databasePath.UTF8String)
|
resourceOptions.withCachePath(self.databasePath.UTF8String)
|
||||||
.withAssetPath([NSBundle mainBundle].resourceURL.path.UTF8String)
|
.withAssetPath([NSBundle mainBundle].resourceURL.path.UTF8String)
|
||||||
.withTileServerOptions(*tileServerOptions);
|
.withTileServerOptions(*tileServerOptions);
|
||||||
mbgl::ClientOptions clientOptions;
|
mbgl::ClientOptions clientOptions;
|
||||||
_mbglFileSource = mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::ResourceLoader, resourceOptions, clientOptions);
|
_mbglFileSource = mbgl::FileSourceManager::get()->getFileSource(
|
||||||
_mbglOnlineFileSource = mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::Network, resourceOptions, clientOptions);
|
mbgl::FileSourceType::ResourceLoader, resourceOptions, clientOptions);
|
||||||
_mbglDatabaseFileSource = std::static_pointer_cast<mbgl::DatabaseFileSource>(std::shared_ptr<mbgl::FileSource>(mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::Database, resourceOptions, clientOptions)));
|
_mbglOnlineFileSource = mbgl::FileSourceManager::get()->getFileSource(
|
||||||
|
mbgl::FileSourceType::Network, resourceOptions, clientOptions);
|
||||||
|
_mbglDatabaseFileSource = std::static_pointer_cast<mbgl::DatabaseFileSource>(
|
||||||
|
std::shared_ptr<mbgl::FileSource>(mbgl::FileSourceManager::get()->getFileSource(
|
||||||
|
mbgl::FileSourceType::Database, resourceOptions, clientOptions)));
|
||||||
|
|
||||||
// Observe for changes to the tile server options (and find out the current one).
|
// Observe for changes to the tile server options (and find out the current one).
|
||||||
[[MLNSettings sharedSettings] addObserver:self
|
[[MLNSettings sharedSettings]
|
||||||
|
addObserver:self
|
||||||
forKeyPath:@"tileServerOptionsChangeToken"
|
forKeyPath:@"tileServerOptionsChangeToken"
|
||||||
options:(NSKeyValueObservingOptionInitial |
|
options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew)
|
||||||
NSKeyValueObservingOptionNew)
|
|
||||||
context:NULL];
|
context:NULL];
|
||||||
|
|
||||||
// Observe for changes to the global access token (and find out the current one).
|
// Observe for changes to the global access token (and find out the current one).
|
||||||
[[MLNSettings sharedSettings] addObserver:self
|
[[MLNSettings sharedSettings]
|
||||||
|
addObserver:self
|
||||||
forKeyPath:@"apiKey"
|
forKeyPath:@"apiKey"
|
||||||
options:(NSKeyValueObservingOptionInitial |
|
options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew)
|
||||||
NSKeyValueObservingOptionNew)
|
|
||||||
context:NULL];
|
context:NULL];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@@ -197,18 +217,23 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context {
|
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||||
|
ofObject:(id)object
|
||||||
|
change:(NSDictionary<NSString *, id> *)change
|
||||||
|
context:(void *)context {
|
||||||
// Synchronize the file source’s access token with the global one in MLNSettings.
|
// Synchronize the file source’s access token with the global one in MLNSettings.
|
||||||
if ([keyPath isEqualToString:@"apiKey"] && object == [MLNSettings sharedSettings]) {
|
if ([keyPath isEqualToString:@"apiKey"] && object == [MLNSettings sharedSettings]) {
|
||||||
NSString *apiKey = change[NSKeyValueChangeNewKey];
|
NSString *apiKey = change[NSKeyValueChangeNewKey];
|
||||||
if (![apiKey isKindOfClass:[NSNull class]]) {
|
if (![apiKey isKindOfClass:[NSNull class]]) {
|
||||||
_mbglOnlineFileSource->setProperty(mbgl::API_KEY_KEY, apiKey.UTF8String);
|
_mbglOnlineFileSource->setProperty(mbgl::API_KEY_KEY, apiKey.UTF8String);
|
||||||
}
|
}
|
||||||
} else if ([keyPath isEqualToString:@"tileServerOptionsChangeToken"] && object == [MLNSettings sharedSettings]) {
|
} else if ([keyPath isEqualToString:@"tileServerOptionsChangeToken"] &&
|
||||||
|
object == [MLNSettings sharedSettings]) {
|
||||||
auto tileServerOptions = [[MLNSettings sharedSettings] tileServerOptionsInternal];
|
auto tileServerOptions = [[MLNSettings sharedSettings] tileServerOptionsInternal];
|
||||||
auto apiBaseURL = tileServerOptions->baseURL();
|
auto apiBaseURL = tileServerOptions->baseURL();
|
||||||
auto resourceOptions = _mbglOnlineFileSource->getResourceOptions().clone();
|
auto resourceOptions = _mbglOnlineFileSource->getResourceOptions().clone();
|
||||||
_mbglOnlineFileSource->setResourceOptions(resourceOptions.withTileServerOptions(*tileServerOptions).clone());
|
_mbglOnlineFileSource->setResourceOptions(
|
||||||
|
resourceOptions.withTileServerOptions(*tileServerOptions).clone());
|
||||||
} else {
|
} else {
|
||||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||||
}
|
}
|
||||||
@@ -222,9 +247,12 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
|
|
||||||
- (NSURL *)databaseURL {
|
- (NSURL *)databaseURL {
|
||||||
if (!_databaseURL) {
|
if (!_databaseURL) {
|
||||||
NSString *customPath = [NSBundle.mainBundle objectForInfoDictionaryKey:MLNOfflineStorageDatabasePathInfoDictionaryKey];
|
NSString *customPath = [NSBundle.mainBundle
|
||||||
|
objectForInfoDictionaryKey:MLNOfflineStorageDatabasePathInfoDictionaryKey];
|
||||||
if ([customPath isKindOfClass:[NSString class]]) {
|
if ([customPath isKindOfClass:[NSString class]]) {
|
||||||
_databaseURL = [NSURL fileURLWithPath:customPath.stringByStandardizingPath isDirectory:NO relativeToURL:NSBundle.mainBundle.resourceURL];
|
_databaseURL = [NSURL fileURLWithPath:customPath.stringByStandardizingPath
|
||||||
|
isDirectory:NO
|
||||||
|
relativeToURL:NSBundle.mainBundle.resourceURL];
|
||||||
|
|
||||||
NSURL *directoryURL = _databaseURL.URLByDeletingLastPathComponent;
|
NSURL *directoryURL = _databaseURL.URLByDeletingLastPathComponent;
|
||||||
[NSFileManager.defaultManager createDirectoryAtURL:directoryURL
|
[NSFileManager.defaultManager createDirectoryAtURL:directoryURL
|
||||||
@@ -235,20 +263,26 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
_databaseURL = [[self class] defaultDatabaseURLIncludingSubdirectory:YES];
|
_databaseURL = [[self class] defaultDatabaseURLIncludingSubdirectory:YES];
|
||||||
}
|
}
|
||||||
NSString *databasePath = self.databasePath;
|
NSString *databasePath = self.databasePath;
|
||||||
NSAssert(databasePath, @"Offline pack database URL “%@” is not a valid file URL.", _databaseURL);
|
NSAssert(databasePath, @"Offline pack database URL “%@” is not a valid file URL.",
|
||||||
|
_databaseURL);
|
||||||
|
|
||||||
// Move the offline database from v3.2.0-beta.1 to a location that can
|
// Move the offline database from v3.2.0-beta.1 to a location that can
|
||||||
// also be used for ambient caching.
|
// also be used for ambient caching.
|
||||||
if (![[NSFileManager defaultManager] fileExistsAtPath:databasePath]) {
|
if (![[NSFileManager defaultManager] fileExistsAtPath:databasePath]) {
|
||||||
NSString *legacyDatabasePath = [[self class] legacyDatabasePath];
|
NSString *legacyDatabasePath = [[self class] legacyDatabasePath];
|
||||||
[[NSFileManager defaultManager] moveItemAtPath:legacyDatabasePath toPath:databasePath error:NULL];
|
[[NSFileManager defaultManager] moveItemAtPath:legacyDatabasePath
|
||||||
|
toPath:databasePath
|
||||||
|
error:NULL];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the offline database from v3.2.x path to a subdirectory that can
|
// Move the offline database from v3.2.x path to a subdirectory that can
|
||||||
// be reliably excluded from backups.
|
// be reliably excluded from backups.
|
||||||
if (![[NSFileManager defaultManager] fileExistsAtPath:databasePath]) {
|
if (![[NSFileManager defaultManager] fileExistsAtPath:databasePath]) {
|
||||||
NSURL *subdirectorylessDatabaseURL = [[self class] defaultDatabaseURLIncludingSubdirectory:NO];
|
NSURL *subdirectorylessDatabaseURL =
|
||||||
[[NSFileManager defaultManager] moveItemAtPath:subdirectorylessDatabaseURL.path toPath:databasePath error:NULL];
|
[[self class] defaultDatabaseURLIncludingSubdirectory:NO];
|
||||||
|
[[NSFileManager defaultManager] moveItemAtPath:subdirectorylessDatabaseURL.path
|
||||||
|
toPath:databasePath
|
||||||
|
error:NULL];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _databaseURL;
|
return _databaseURL;
|
||||||
@@ -269,7 +303,8 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
~/Library/Application Support/tld.app.bundle.id/cache.db
|
~/Library/Application Support/tld.app.bundle.id/cache.db
|
||||||
*/
|
*/
|
||||||
+ (NSURL *)defaultDatabaseURLIncludingSubdirectory:(BOOL)useSubdirectory {
|
+ (NSURL *)defaultDatabaseURLIncludingSubdirectory:(BOOL)useSubdirectory {
|
||||||
NSURL *databaseDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
|
NSURL *databaseDirectoryURL =
|
||||||
|
[[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
|
||||||
inDomain:NSUserDomainMask
|
inDomain:NSUserDomainMask
|
||||||
appropriateForURL:nil
|
appropriateForURL:nil
|
||||||
create:YES
|
create:YES
|
||||||
@@ -304,44 +339,54 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
+ (NSString *)legacyDatabasePath {
|
+ (NSString *)legacyDatabasePath {
|
||||||
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
||||||
// ~/Documents/offline.db
|
// ~/Documents/offline.db
|
||||||
NSArray *legacyPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
NSArray *legacyPaths =
|
||||||
NSString *legacyDatabasePath = [legacyPaths.firstObject stringByAppendingPathComponent:MLNOfflineStorageFileName3_2_0_beta_1];
|
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||||
|
NSString *legacyDatabasePath = [legacyPaths.firstObject
|
||||||
|
stringByAppendingPathComponent:MLNOfflineStorageFileName3_2_0_beta_1];
|
||||||
#elif TARGET_OS_MAC
|
#elif TARGET_OS_MAC
|
||||||
// ~/Library/Caches/tld.app.bundle.id/offline.db
|
// ~/Library/Caches/tld.app.bundle.id/offline.db
|
||||||
NSString *bundleIdentifier = [NSBundle mgl_applicationBundleIdentifier];
|
NSString *bundleIdentifier = [NSBundle mgl_applicationBundleIdentifier];
|
||||||
NSURL *legacyDatabaseDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
|
NSURL *legacyDatabaseDirectoryURL =
|
||||||
|
[[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
|
||||||
inDomain:NSUserDomainMask
|
inDomain:NSUserDomainMask
|
||||||
appropriateForURL:nil
|
appropriateForURL:nil
|
||||||
create:NO
|
create:NO
|
||||||
error:nil];
|
error:nil];
|
||||||
legacyDatabaseDirectoryURL = [legacyDatabaseDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
|
legacyDatabaseDirectoryURL =
|
||||||
NSURL *legacyDatabaseURL = [legacyDatabaseDirectoryURL URLByAppendingPathComponent:MLNOfflineStorageFileName3_2_0_beta_1];
|
[legacyDatabaseDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
|
||||||
|
NSURL *legacyDatabaseURL = [legacyDatabaseDirectoryURL
|
||||||
|
URLByAppendingPathComponent:MLNOfflineStorageFileName3_2_0_beta_1];
|
||||||
NSString *legacyDatabasePath = legacyDatabaseURL ? legacyDatabaseURL.path : @"";
|
NSString *legacyDatabasePath = legacyDatabaseURL ? legacyDatabaseURL.path : @"";
|
||||||
#endif
|
#endif
|
||||||
return legacyDatabasePath;
|
return legacyDatabasePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addContentsOfFile:(NSString *)filePath withCompletionHandler:(MLNBatchedOfflinePackAdditionCompletionHandler)completion {
|
- (void)addContentsOfFile:(NSString *)filePath
|
||||||
|
withCompletionHandler:(MLNBatchedOfflinePackAdditionCompletionHandler)completion {
|
||||||
MLNLogDebug(@"Adding contentsOfFile: %@ completionHandler: %@", filePath, completion);
|
MLNLogDebug(@"Adding contentsOfFile: %@ completionHandler: %@", filePath, completion);
|
||||||
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
|
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
|
||||||
|
|
||||||
[self addContentsOfURL:fileURL withCompletionHandler:completion];
|
[self addContentsOfURL:fileURL withCompletionHandler:completion];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addContentsOfURL:(NSURL *)fileURL withCompletionHandler:(MLNBatchedOfflinePackAdditionCompletionHandler)completion {
|
- (void)addContentsOfURL:(NSURL *)fileURL
|
||||||
|
withCompletionHandler:(MLNBatchedOfflinePackAdditionCompletionHandler)completion {
|
||||||
MLNLogDebug(@"Adding contentsOfURL: %@ completionHandler: %@", fileURL, completion);
|
MLNLogDebug(@"Adding contentsOfURL: %@ completionHandler: %@", fileURL, completion);
|
||||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||||
|
|
||||||
if (!fileURL.isFileURL) {
|
if (!fileURL.isFileURL) {
|
||||||
[NSException raise:NSInvalidArgumentException format:@"%@ must be a valid file path", fileURL.absoluteString];
|
[NSException raise:NSInvalidArgumentException
|
||||||
|
format:@"%@ must be a valid file path", fileURL.absoluteString];
|
||||||
}
|
}
|
||||||
if (![fileManager isWritableFileAtPath:fileURL.path]) {
|
if (![fileManager isWritableFileAtPath:fileURL.path]) {
|
||||||
[NSException raise:NSInvalidArgumentException format:@"The file path: %@ must be writable", fileURL.absoluteString];
|
[NSException raise:NSInvalidArgumentException
|
||||||
|
format:@"The file path: %@ must be writable", fileURL.absoluteString];
|
||||||
}
|
}
|
||||||
|
|
||||||
__weak MLNOfflineStorage *weakSelf = self;
|
__weak MLNOfflineStorage *weakSelf = self;
|
||||||
[self _addContentsOfFile:fileURL.path withCompletionHandler:^(NSArray<MLNOfflinePack *> * _Nullable packs, NSError * _Nullable error) {
|
[self _addContentsOfFile:fileURL.path
|
||||||
|
withCompletionHandler:^(NSArray<MLNOfflinePack *> *_Nullable packs,
|
||||||
|
NSError *_Nullable error) {
|
||||||
if (packs) {
|
if (packs) {
|
||||||
NSMutableDictionary *packsByIdentifier = [NSMutableDictionary dictionary];
|
NSMutableDictionary *packsByIdentifier = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
@@ -353,16 +398,17 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
id mutablePacks = [strongSelf mutableArrayValueForKey:@"packs"];
|
id mutablePacks = [strongSelf mutableArrayValueForKey:@"packs"];
|
||||||
NSMutableIndexSet *replaceIndexSet = [NSMutableIndexSet indexSet];
|
NSMutableIndexSet *replaceIndexSet = [NSMutableIndexSet indexSet];
|
||||||
NSMutableArray *replacePacksArray = [NSMutableArray array];
|
NSMutableArray *replacePacksArray = [NSMutableArray array];
|
||||||
[strongSelf.packs enumerateObjectsUsingBlock:^(MLNOfflinePack * _Nonnull pack, NSUInteger idx, BOOL * _Nonnull stop) {
|
[strongSelf.packs enumerateObjectsUsingBlock:^(MLNOfflinePack *_Nonnull pack,
|
||||||
|
NSUInteger idx, BOOL *_Nonnull stop) {
|
||||||
MLNOfflinePack *newPack = packsByIdentifier[@(pack.mbglOfflineRegion->getID())];
|
MLNOfflinePack *newPack = packsByIdentifier[@(pack.mbglOfflineRegion->getID())];
|
||||||
if (newPack) {
|
if (newPack) {
|
||||||
MLNOfflinePack *previousPack = [mutablePacks objectAtIndex:idx];
|
MLNOfflinePack *previousPack = [mutablePacks objectAtIndex:idx];
|
||||||
[previousPack invalidate];
|
[previousPack invalidate];
|
||||||
[replaceIndexSet addIndex:idx];
|
[replaceIndexSet addIndex:idx];
|
||||||
[replacePacksArray addObject:[packsByIdentifier objectForKey:@(newPack.mbglOfflineRegion->getID())]];
|
[replacePacksArray
|
||||||
|
addObject:[packsByIdentifier objectForKey:@(newPack.mbglOfflineRegion->getID())]];
|
||||||
[packsByIdentifier removeObjectForKey:@(newPack.mbglOfflineRegion->getID())];
|
[packsByIdentifier removeObjectForKey:@(newPack.mbglOfflineRegion->getID())];
|
||||||
}
|
}
|
||||||
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (replaceIndexSet.count > 0) {
|
if (replaceIndexSet.count > 0) {
|
||||||
@@ -377,39 +423,55 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_addContentsOfFile:(NSString *)filePath withCompletionHandler:(void (^)(NSArray<MLNOfflinePack *> * _Nullable packs, NSError * _Nullable error))completion {
|
- (void)_addContentsOfFile:(NSString *)filePath
|
||||||
_mbglDatabaseFileSource->mergeOfflineRegions(std::string(static_cast<const char *>([filePath UTF8String])), [&, completion, filePath](mbgl::expected<mbgl::OfflineRegions, std::exception_ptr> result) {
|
withCompletionHandler:(void (^)(NSArray<MLNOfflinePack *> *_Nullable packs,
|
||||||
|
NSError *_Nullable error))completion {
|
||||||
|
_mbglDatabaseFileSource->mergeOfflineRegions(
|
||||||
|
std::string(static_cast<const char *>([filePath UTF8String])),
|
||||||
|
[&, completion, filePath](mbgl::expected<mbgl::OfflineRegions, std::exception_ptr> result) {
|
||||||
NSError *error;
|
NSError *error;
|
||||||
NSMutableArray *packs;
|
NSMutableArray *packs;
|
||||||
if (!result) {
|
if (!result) {
|
||||||
NSString *description = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"ADD_FILE_CONTENTS_FAILED_DESC", @"Foundation", nil, @"Unable to add offline packs from the file at %@.", @"User-friendly error description"), filePath];
|
NSString *description =
|
||||||
error = [NSError errorWithDomain:MLNErrorDomain code:MLNErrorCodeModifyingOfflineStorageFailed
|
[NSString stringWithFormat:NSLocalizedStringWithDefaultValue(
|
||||||
|
@"ADD_FILE_CONTENTS_FAILED_DESC", @"Foundation", nil,
|
||||||
|
@"Unable to add offline packs from the file at %@.",
|
||||||
|
@"User-friendly error description"),
|
||||||
|
filePath];
|
||||||
|
error = [NSError errorWithDomain:MLNErrorDomain
|
||||||
|
code:MLNErrorCodeModifyingOfflineStorageFailed
|
||||||
userInfo:@{
|
userInfo:@{
|
||||||
NSLocalizedDescriptionKey: description,
|
NSLocalizedDescriptionKey : description,
|
||||||
NSLocalizedFailureReasonErrorKey: @(mbgl::util::toString(result.error()).c_str())
|
NSLocalizedFailureReasonErrorKey :
|
||||||
|
@(mbgl::util::toString(result.error()).c_str())
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
auto& regions = result.value();
|
auto ®ions = result.value();
|
||||||
packs = [NSMutableArray arrayWithCapacity:regions.size()];
|
packs = [NSMutableArray arrayWithCapacity:regions.size()];
|
||||||
for (auto ®ion : regions) {
|
for (auto ®ion : regions) {
|
||||||
MLNOfflinePack *pack = [[MLNOfflinePack alloc] initWithMBGLRegion:new mbgl::OfflineRegion(std::move(region))];
|
MLNOfflinePack *pack = [[MLNOfflinePack alloc]
|
||||||
|
initWithMBGLRegion:new mbgl::OfflineRegion(std::move(region))];
|
||||||
[packs addObject:pack];
|
[packs addObject:pack];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (completion) {
|
if (completion) {
|
||||||
dispatch_async(dispatch_get_main_queue(), [&, completion, error, packs](void) {
|
dispatch_async(dispatch_get_main_queue(),
|
||||||
completion(packs, error);
|
[&, completion, error, packs](void) { completion(packs, error); });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Pack management methods
|
// MARK: Pack management methods
|
||||||
|
|
||||||
- (void)addPackForRegion:(id <MLNOfflineRegion>)region withContext:(NSData *)context completionHandler:(MLNOfflinePackAdditionCompletionHandler)completion {
|
- (void)addPackForRegion:(id<MLNOfflineRegion>)region
|
||||||
MLNLogDebug(@"Adding packForRegion: %@ contextLength: %lu completionHandler: %@", region, (unsigned long)context.length, completion);
|
withContext:(NSData *)context
|
||||||
|
completionHandler:(MLNOfflinePackAdditionCompletionHandler)completion {
|
||||||
|
MLNLogDebug(@"Adding packForRegion: %@ contextLength: %lu completionHandler: %@", region,
|
||||||
|
(unsigned long)context.length, completion);
|
||||||
__weak MLNOfflineStorage *weakSelf = self;
|
__weak MLNOfflineStorage *weakSelf = self;
|
||||||
[self _addPackForRegion:region withContext:context completionHandler:^(MLNOfflinePack * _Nullable pack, NSError * _Nullable error) {
|
[self _addPackForRegion:region
|
||||||
|
withContext:context
|
||||||
|
completionHandler:^(MLNOfflinePack *_Nullable pack, NSError *_Nullable error) {
|
||||||
pack.state = MLNOfflinePackStateInactive;
|
pack.state = MLNOfflinePackStateInactive;
|
||||||
MLNOfflineStorage *strongSelf = weakSelf;
|
MLNOfflineStorage *strongSelf = weakSelf;
|
||||||
[[strongSelf mutableArrayValueForKey:@"packs"] addObject:pack];
|
[[strongSelf mutableArrayValueForKey:@"packs"] addObject:pack];
|
||||||
@@ -419,44 +481,58 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_addPackForRegion:(id <MLNOfflineRegion>)region withContext:(NSData *)context completionHandler:(MLNOfflinePackAdditionCompletionHandler)completion {
|
- (void)_addPackForRegion:(id<MLNOfflineRegion>)region
|
||||||
|
withContext:(NSData *)context
|
||||||
|
completionHandler:(MLNOfflinePackAdditionCompletionHandler)completion {
|
||||||
if (![region conformsToProtocol:@protocol(MLNOfflineRegion_Private)]) {
|
if (![region conformsToProtocol:@protocol(MLNOfflineRegion_Private)]) {
|
||||||
[NSException raise:MLNUnsupportedRegionTypeException
|
[NSException raise:MLNUnsupportedRegionTypeException
|
||||||
format:@"Regions of type %@ are unsupported.", NSStringFromClass([region class])];
|
format:@"Regions of type %@ are unsupported.", NSStringFromClass([region class])];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mbgl::OfflineRegionDefinition regionDefinition = [(id <MLNOfflineRegion_Private>)region offlineRegionDefinition];
|
const mbgl::OfflineRegionDefinition regionDefinition =
|
||||||
|
[(id<MLNOfflineRegion_Private>)region offlineRegionDefinition];
|
||||||
mbgl::OfflineRegionMetadata metadata(context.length);
|
mbgl::OfflineRegionMetadata metadata(context.length);
|
||||||
[context getBytes:&metadata[0] length:metadata.size()];
|
[context getBytes:&metadata[0] length:metadata.size()];
|
||||||
_mbglDatabaseFileSource->createOfflineRegion(regionDefinition, metadata, [&, completion](mbgl::expected<mbgl::OfflineRegion, std::exception_ptr> mbglOfflineRegion) {
|
_mbglDatabaseFileSource->createOfflineRegion(
|
||||||
|
regionDefinition, metadata,
|
||||||
|
[&, completion](mbgl::expected<mbgl::OfflineRegion, std::exception_ptr> mbglOfflineRegion) {
|
||||||
NSError *error;
|
NSError *error;
|
||||||
if (!mbglOfflineRegion) {
|
if (!mbglOfflineRegion) {
|
||||||
NSString *errorDescription = @(mbgl::util::toString(mbglOfflineRegion.error()).c_str());
|
NSString *errorDescription = @(mbgl::util::toString(mbglOfflineRegion.error()).c_str());
|
||||||
error = [NSError errorWithDomain:MLNErrorDomain code:MLNErrorCodeModifyingOfflineStorageFailed userInfo:errorDescription ? @{
|
error = [NSError errorWithDomain:MLNErrorDomain
|
||||||
NSLocalizedDescriptionKey: errorDescription,
|
code:MLNErrorCodeModifyingOfflineStorageFailed
|
||||||
} : nil];
|
userInfo:errorDescription ? @{
|
||||||
|
NSLocalizedDescriptionKey : errorDescription,
|
||||||
|
}
|
||||||
|
: nil];
|
||||||
}
|
}
|
||||||
if (completion) {
|
if (completion) {
|
||||||
MLNOfflinePack *pack = mbglOfflineRegion ? [[MLNOfflinePack alloc] initWithMBGLRegion:new mbgl::OfflineRegion(std::move(mbglOfflineRegion.value()))] : nil;
|
MLNOfflinePack *pack =
|
||||||
dispatch_async(dispatch_get_main_queue(), [&, completion, error, pack](void) {
|
mbglOfflineRegion
|
||||||
completion(pack, error);
|
? [[MLNOfflinePack alloc] initWithMBGLRegion:new mbgl::OfflineRegion(std::move(
|
||||||
});
|
mbglOfflineRegion.value()))]
|
||||||
|
: nil;
|
||||||
|
dispatch_async(dispatch_get_main_queue(),
|
||||||
|
[&, completion, error, pack](void) { completion(pack, error); });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removePack:(MLNOfflinePack *)pack withCompletionHandler:(MLNOfflinePackRemovalCompletionHandler)completion {
|
- (void)removePack:(MLNOfflinePack *)pack
|
||||||
|
withCompletionHandler:(MLNOfflinePackRemovalCompletionHandler)completion {
|
||||||
MLNLogDebug(@"Removing pack: %@ completionHandler: %@", pack, completion);
|
MLNLogDebug(@"Removing pack: %@ completionHandler: %@", pack, completion);
|
||||||
[[self mutableArrayValueForKey:@"packs"] removeObject:pack];
|
[[self mutableArrayValueForKey:@"packs"] removeObject:pack];
|
||||||
[self _removePack:pack withCompletionHandler:^(NSError * _Nullable error) {
|
[self _removePack:pack
|
||||||
|
withCompletionHandler:^(NSError *_Nullable error) {
|
||||||
if (completion) {
|
if (completion) {
|
||||||
completion(error);
|
completion(error);
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_removePack:(MLNOfflinePack *)pack withCompletionHandler:(MLNOfflinePackRemovalCompletionHandler)completion {
|
- (void)_removePack:(MLNOfflinePack *)pack
|
||||||
|
withCompletionHandler:(MLNOfflinePackRemovalCompletionHandler)completion {
|
||||||
mbgl::OfflineRegion *mbglOfflineRegion = pack.mbglOfflineRegion;
|
mbgl::OfflineRegion *mbglOfflineRegion = pack.mbglOfflineRegion;
|
||||||
|
|
||||||
[pack invalidate];
|
[pack invalidate];
|
||||||
@@ -467,24 +543,27 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_mbglDatabaseFileSource->deleteOfflineRegion(std::move(*mbglOfflineRegion), [&, completion](std::exception_ptr exception) {
|
_mbglDatabaseFileSource->deleteOfflineRegion(
|
||||||
|
std::move(*mbglOfflineRegion), [&, completion](std::exception_ptr exception) {
|
||||||
NSError *error;
|
NSError *error;
|
||||||
if (exception) {
|
if (exception) {
|
||||||
error = [NSError errorWithDomain:MLNErrorDomain code:MLNErrorCodeModifyingOfflineStorageFailed userInfo:@{
|
error = [NSError
|
||||||
NSLocalizedDescriptionKey: @(mbgl::util::toString(exception).c_str()),
|
errorWithDomain:MLNErrorDomain
|
||||||
|
code:MLNErrorCodeModifyingOfflineStorageFailed
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : @(mbgl::util::toString(exception).c_str()),
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
if (completion) {
|
if (completion) {
|
||||||
dispatch_async(dispatch_get_main_queue(), [&, completion, error](void) {
|
dispatch_async(dispatch_get_main_queue(),
|
||||||
completion(error);
|
[&, completion, error](void) { completion(error); });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)invalidatePack:(MLNOfflinePack *)pack withCompletionHandler:(void (^)(NSError * _Nullable))completion {
|
- (void)invalidatePack:(MLNOfflinePack *)pack
|
||||||
mbgl::OfflineRegion& region = *pack.mbglOfflineRegion;
|
withCompletionHandler:(void (^)(NSError *_Nullable))completion {
|
||||||
|
mbgl::OfflineRegion ®ion = *pack.mbglOfflineRegion;
|
||||||
NSError *error;
|
NSError *error;
|
||||||
if (!pack.mbglOfflineRegion) {
|
if (!pack.mbglOfflineRegion) {
|
||||||
completion(nil);
|
completion(nil);
|
||||||
@@ -493,21 +572,23 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
|
|
||||||
_mbglDatabaseFileSource->invalidateOfflineRegion(region, [&](std::exception_ptr exception) {
|
_mbglDatabaseFileSource->invalidateOfflineRegion(region, [&](std::exception_ptr exception) {
|
||||||
if (exception) {
|
if (exception) {
|
||||||
error = [NSError errorWithDomain:MLNErrorDomain code:MLNErrorCodeModifyingOfflineStorageFailed userInfo:@{
|
error =
|
||||||
NSLocalizedDescriptionKey: @(mbgl::util::toString(exception).c_str()),
|
[NSError errorWithDomain:MLNErrorDomain
|
||||||
|
code:MLNErrorCodeModifyingOfflineStorageFailed
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : @(mbgl::util::toString(exception).c_str()),
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (completion) {
|
if (completion) {
|
||||||
dispatch_async(dispatch_get_main_queue(), [&, completion, error](void) {
|
dispatch_async(dispatch_get_main_queue(), [&, completion, error](void) { completion(error); });
|
||||||
completion(error);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reloadPacks {
|
- (void)reloadPacks {
|
||||||
MLNLogInfo(@"Reloading packs.");
|
MLNLogInfo(@"Reloading packs.");
|
||||||
[self getPacksWithCompletionHandler:^(NSArray<MLNOfflinePack *> *packs, __unused NSError * _Nullable error) {
|
[self getPacksWithCompletionHandler:^(NSArray<MLNOfflinePack *> *packs,
|
||||||
|
__unused NSError *_Nullable error) {
|
||||||
for (MLNOfflinePack *pack in self.packs) {
|
for (MLNOfflinePack *pack in self.packs) {
|
||||||
[pack invalidate];
|
[pack invalidate];
|
||||||
}
|
}
|
||||||
@@ -515,26 +596,31 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)getPacksWithCompletionHandler:(void (^)(NSArray<MLNOfflinePack *> *packs, NSError * _Nullable error))completion {
|
- (void)getPacksWithCompletionHandler:(void (^)(NSArray<MLNOfflinePack *> *packs,
|
||||||
_mbglDatabaseFileSource->listOfflineRegions([&, completion](mbgl::expected<mbgl::OfflineRegions, std::exception_ptr> result) {
|
NSError *_Nullable error))completion {
|
||||||
|
_mbglDatabaseFileSource->listOfflineRegions(
|
||||||
|
[&, completion](mbgl::expected<mbgl::OfflineRegions, std::exception_ptr> result) {
|
||||||
NSError *error;
|
NSError *error;
|
||||||
NSMutableArray *packs;
|
NSMutableArray *packs;
|
||||||
if (!result) {
|
if (!result) {
|
||||||
error = [NSError errorWithDomain:MLNErrorDomain code:MLNErrorCodeUnknown userInfo:@{
|
error = [NSError
|
||||||
NSLocalizedDescriptionKey: @(mbgl::util::toString(result.error()).c_str()),
|
errorWithDomain:MLNErrorDomain
|
||||||
|
code:MLNErrorCodeUnknown
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : @(mbgl::util::toString(result.error()).c_str()),
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
auto& regions = result.value();
|
auto ®ions = result.value();
|
||||||
packs = [NSMutableArray arrayWithCapacity:regions.size()];
|
packs = [NSMutableArray arrayWithCapacity:regions.size()];
|
||||||
for (auto ®ion : regions) {
|
for (auto ®ion : regions) {
|
||||||
MLNOfflinePack *pack = [[MLNOfflinePack alloc] initWithMBGLRegion:new mbgl::OfflineRegion(std::move(region))];
|
MLNOfflinePack *pack = [[MLNOfflinePack alloc]
|
||||||
|
initWithMBGLRegion:new mbgl::OfflineRegion(std::move(region))];
|
||||||
[packs addObject:pack];
|
[packs addObject:pack];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (completion) {
|
if (completion) {
|
||||||
dispatch_async(dispatch_get_main_queue(), [&, completion, error, packs](void) {
|
dispatch_async(dispatch_get_main_queue(),
|
||||||
completion(packs, error);
|
[&, completion, error, packs](void) { completion(packs, error); });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -546,16 +632,21 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
|
|
||||||
// MARK: - Ambient cache management
|
// MARK: - Ambient cache management
|
||||||
|
|
||||||
- (void)setMaximumAmbientCacheSize:(NSUInteger)cacheSize withCompletionHandler:(void (^)(NSError * _Nullable))completion {
|
- (void)setMaximumAmbientCacheSize:(NSUInteger)cacheSize
|
||||||
_mbglDatabaseFileSource->setMaximumAmbientCacheSize(cacheSize, [&, completion](std::exception_ptr exception) {
|
withCompletionHandler:(void (^)(NSError *_Nullable))completion {
|
||||||
|
_mbglDatabaseFileSource->setMaximumAmbientCacheSize(
|
||||||
|
cacheSize, [&, completion](std::exception_ptr exception) {
|
||||||
NSError *error;
|
NSError *error;
|
||||||
if (completion) {
|
if (completion) {
|
||||||
if (exception) {
|
if (exception) {
|
||||||
error = [NSError errorWithDomain:MLNErrorDomain code:MLNErrorCodeModifyingOfflineStorageFailed userInfo:@{
|
error = [NSError
|
||||||
NSLocalizedDescriptionKey: @(mbgl::util::toString(exception).c_str()),
|
errorWithDomain:MLNErrorDomain
|
||||||
|
code:MLNErrorCodeModifyingOfflineStorageFailed
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : @(mbgl::util::toString(exception).c_str()),
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^ {
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||||
completion(error);
|
completion(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -563,16 +654,19 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)invalidateAmbientCacheWithCompletionHandler:(void (^)(NSError *_Nullable))completion {
|
- (void)invalidateAmbientCacheWithCompletionHandler:(void (^)(NSError *_Nullable))completion {
|
||||||
_mbglDatabaseFileSource->invalidateAmbientCache([&, completion](std::exception_ptr exception){
|
_mbglDatabaseFileSource->invalidateAmbientCache([&, completion](std::exception_ptr exception) {
|
||||||
NSError *error;
|
NSError *error;
|
||||||
if (completion) {
|
if (completion) {
|
||||||
if (exception) {
|
if (exception) {
|
||||||
// Convert std::exception_ptr to an NSError.
|
// Convert std::exception_ptr to an NSError.
|
||||||
error = [NSError errorWithDomain:MLNErrorDomain code:MLNErrorCodeModifyingOfflineStorageFailed userInfo:@{
|
error = [NSError
|
||||||
NSLocalizedDescriptionKey: @(mbgl::util::toString(exception).c_str()),
|
errorWithDomain:MLNErrorDomain
|
||||||
|
code:MLNErrorCodeModifyingOfflineStorageFailed
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : @(mbgl::util::toString(exception).c_str()),
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
dispatch_async(dispatch_get_main_queue(), ^ {
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
completion(error);
|
completion(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -580,17 +674,19 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)clearAmbientCacheWithCompletionHandler:(void (^)(NSError *_Nullable error))completion {
|
- (void)clearAmbientCacheWithCompletionHandler:(void (^)(NSError *_Nullable error))completion {
|
||||||
_mbglDatabaseFileSource->clearAmbientCache([&, completion](std::exception_ptr exception){
|
_mbglDatabaseFileSource->clearAmbientCache([&, completion](std::exception_ptr exception) {
|
||||||
NSError *error;
|
NSError *error;
|
||||||
if (completion) {
|
if (completion) {
|
||||||
if (exception) {
|
if (exception) {
|
||||||
error = [NSError errorWithDomain:MLNErrorDomain code:MLNErrorCodeModifyingOfflineStorageFailed userInfo:@{
|
error = [NSError
|
||||||
NSLocalizedDescriptionKey: @(mbgl::util::toString(exception).c_str()),
|
errorWithDomain:MLNErrorDomain
|
||||||
|
code:MLNErrorCodeModifyingOfflineStorageFailed
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : @(mbgl::util::toString(exception).c_str()),
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
dispatch_async(dispatch_get_main_queue(), [&, completion, error](void) {
|
dispatch_async(dispatch_get_main_queue(),
|
||||||
completion(error);
|
[&, completion, error](void) { completion(error); });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -600,8 +696,11 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
NSError *error;
|
NSError *error;
|
||||||
if (completion) {
|
if (completion) {
|
||||||
if (exception) {
|
if (exception) {
|
||||||
error = [NSError errorWithDomain:MLNErrorDomain code:MLNErrorCodeUnknown userInfo:@{
|
error = [NSError
|
||||||
NSLocalizedDescriptionKey: @(mbgl::util::toString(exception).c_str()),
|
errorWithDomain:MLNErrorDomain
|
||||||
|
code:MLNErrorCodeUnknown
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : @(mbgl::util::toString(exception).c_str()),
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
@@ -613,19 +712,36 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
// MARK: -
|
// MARK: -
|
||||||
|
|
||||||
- (unsigned long long)countOfBytesCompleted {
|
- (unsigned long long)countOfBytesCompleted {
|
||||||
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self.databasePath error:NULL];
|
NSDictionary *attributes =
|
||||||
|
[[NSFileManager defaultManager] attributesOfItemAtPath:self.databasePath error:NULL];
|
||||||
return attributes.fileSize;
|
return attributes.fileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)preloadData:(NSData *)data forURL:(NSURL *)url modificationDate:(nullable NSDate *)modified expirationDate:(nullable NSDate *)expires eTag:(nullable NSString *)eTag mustRevalidate:(BOOL)mustRevalidate {
|
- (void)preloadData:(NSData *)data
|
||||||
[self preloadData:data forURL:url modificationDate:modified expirationDate:expires eTag:eTag mustRevalidate:mustRevalidate completionHandler:nil];
|
forURL:(NSURL *)url
|
||||||
|
modificationDate:(nullable NSDate *)modified
|
||||||
|
expirationDate:(nullable NSDate *)expires
|
||||||
|
eTag:(nullable NSString *)eTag
|
||||||
|
mustRevalidate:(BOOL)mustRevalidate {
|
||||||
|
[self preloadData:data
|
||||||
|
forURL:url
|
||||||
|
modificationDate:modified
|
||||||
|
expirationDate:expires
|
||||||
|
eTag:eTag
|
||||||
|
mustRevalidate:mustRevalidate
|
||||||
|
completionHandler:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)preloadData:(NSData *)data forURL:(NSURL *)url modificationDate:(nullable NSDate *)modified expirationDate:(nullable NSDate *)expires eTag:(nullable NSString *)eTag mustRevalidate:(BOOL)mustRevalidate
|
- (void)preloadData:(NSData *)data
|
||||||
|
forURL:(NSURL *)url
|
||||||
|
modificationDate:(nullable NSDate *)modified
|
||||||
|
expirationDate:(nullable NSDate *)expires
|
||||||
|
eTag:(nullable NSString *)eTag
|
||||||
|
mustRevalidate:(BOOL)mustRevalidate
|
||||||
completionHandler:(nullable MLNOfflinePreloadDataCompletionHandler)completion {
|
completionHandler:(nullable MLNOfflinePreloadDataCompletionHandler)completion {
|
||||||
mbgl::Resource resource(mbgl::Resource::Kind::Unknown, url.absoluteString.UTF8String);
|
mbgl::Resource resource(mbgl::Resource::Kind::Unknown, url.absoluteString.UTF8String);
|
||||||
mbgl::Response response;
|
mbgl::Response response;
|
||||||
response.data = std::make_shared<std::string>(static_cast<const char*>(data.bytes), data.length);
|
response.data = std::make_shared<std::string>(static_cast<const char *>(data.bytes), data.length);
|
||||||
response.mustRevalidate = mustRevalidate;
|
response.mustRevalidate = mustRevalidate;
|
||||||
|
|
||||||
if (eTag) {
|
if (eTag) {
|
||||||
@@ -633,11 +749,15 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (modified) {
|
if (modified) {
|
||||||
response.modified = mbgl::Timestamp() + std::chrono::duration_cast<mbgl::Seconds>(MLNDurationFromTimeInterval(modified.timeIntervalSince1970));
|
response.modified =
|
||||||
|
mbgl::Timestamp() + std::chrono::duration_cast<mbgl::Seconds>(
|
||||||
|
MLNDurationFromTimeInterval(modified.timeIntervalSince1970));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expires) {
|
if (expires) {
|
||||||
response.expires = mbgl::Timestamp() + std::chrono::duration_cast<mbgl::Seconds>(MLNDurationFromTimeInterval(expires.timeIntervalSince1970));
|
response.expires =
|
||||||
|
mbgl::Timestamp() + std::chrono::duration_cast<mbgl::Seconds>(
|
||||||
|
MLNDurationFromTimeInterval(expires.timeIntervalSince1970));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<void()> callback;
|
std::function<void()> callback;
|
||||||
@@ -651,8 +771,18 @@ const MLNExceptionName MLNUnsupportedRegionTypeException = @"MLNUnsupportedRegio
|
|||||||
_mbglDatabaseFileSource->forward(resource, response, callback);
|
_mbglDatabaseFileSource->forward(resource, response, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)putResourceWithUrl:(NSURL *)url data:(NSData *)data modified:(nullable NSDate *)modified expires:(nullable NSDate *)expires etag:(nullable NSString *)etag mustRevalidate:(BOOL)mustRevalidate {
|
- (void)putResourceWithUrl:(NSURL *)url
|
||||||
[self preloadData:data forURL:url modificationDate:modified expirationDate:expires eTag:etag mustRevalidate:mustRevalidate];
|
data:(NSData *)data
|
||||||
|
modified:(nullable NSDate *)modified
|
||||||
|
expires:(nullable NSDate *)expires
|
||||||
|
etag:(nullable NSString *)etag
|
||||||
|
mustRevalidate:(BOOL)mustRevalidate {
|
||||||
|
[self preloadData:data
|
||||||
|
forURL:url
|
||||||
|
modificationDate:modified
|
||||||
|
expirationDate:expires
|
||||||
|
eTag:etag
|
||||||
|
mustRevalidate:mustRevalidate];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
@implementation MLNPluginLayerProperty
|
@implementation MLNPluginLayerProperty
|
||||||
|
|
||||||
+(MLNPluginLayerProperty *)propertyWithName:(NSString *)propertyName
|
+ (MLNPluginLayerProperty *)propertyWithName:(NSString *)propertyName
|
||||||
propertyType:(MLNPluginLayerPropertyType)propertyType
|
propertyType:(MLNPluginLayerPropertyType)propertyType
|
||||||
defaultValue:(id)defaultValue {
|
defaultValue:(id)defaultValue {
|
||||||
MLNPluginLayerProperty *tempResult = [[MLNPluginLayerProperty alloc] init];
|
MLNPluginLayerProperty *tempResult = [[MLNPluginLayerProperty alloc] init];
|
||||||
@@ -26,8 +26,7 @@
|
|||||||
return tempResult;
|
return tempResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (id)init {
|
||||||
-(id)init {
|
|
||||||
// Base class implemenation
|
// Base class implemenation
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
// Default setup
|
// Default setup
|
||||||
@@ -45,12 +44,10 @@
|
|||||||
@implementation MLNPluginLayer
|
@implementation MLNPluginLayer
|
||||||
|
|
||||||
// This is the layer type in the style that is used
|
// This is the layer type in the style that is used
|
||||||
+(MLNPluginLayerCapabilities *)layerCapabilities {
|
+ (MLNPluginLayerCapabilities *)layerCapabilities {
|
||||||
|
|
||||||
// Base class returns the class name just to return something
|
// Base class returns the class name just to return something
|
||||||
// TODO: Add an assert/etc or something to notify the developer that this needs to be overridden
|
// TODO: Add an assert/etc or something to notify the developer that this needs to be overridden
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)onRenderLayer:(MLNMapView *)mapView
|
- (void)onRenderLayer:(MLNMapView *)mapView
|
||||||
@@ -62,7 +59,7 @@
|
|||||||
// Base class does nothing
|
// Base class does nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)onUpdateLayerProperties:(NSDictionary *)layerProperties {
|
- (void)onUpdateLayerProperties:(NSDictionary *)layerProperties {
|
||||||
// Base class does nothing
|
// Base class does nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,10 +68,7 @@
|
|||||||
// Base class does nothing
|
// Base class does nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MLNPluginLayerCapabilities
|
@implementation MLNPluginLayerCapabilities
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
#import "MLNPluginStyleLayer.h"
|
#import "MLNPluginStyleLayer.h"
|
||||||
#import "MLNPluginStyleLayer_Private.h"
|
|
||||||
#import <mbgl/plugin/plugin_layer.hpp>
|
#import <mbgl/plugin/plugin_layer.hpp>
|
||||||
#import <mbgl/plugin/plugin_layer_impl.hpp>
|
#import <mbgl/plugin/plugin_layer_impl.hpp>
|
||||||
#import "MLNPluginLayer.h"
|
#import "MLNPluginLayer.h"
|
||||||
|
#import "MLNPluginStyleLayer_Private.h"
|
||||||
|
|
||||||
@implementation MLNPluginStyleLayer
|
@implementation MLNPluginStyleLayer
|
||||||
|
|
||||||
-(void)getStats {
|
- (void)getStats {
|
||||||
|
|
||||||
mbgl::style::PluginLayer *l = (mbgl::style::PluginLayer *)self.rawLayer;
|
mbgl::style::PluginLayer *l = (mbgl::style::PluginLayer *)self.rawLayer;
|
||||||
auto pl = l->impl();
|
auto pl = l->impl();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(MLNPluginLayer *)pluginLayer {
|
- (MLNPluginLayer *)pluginLayer {
|
||||||
|
|
||||||
mbgl::style::PluginLayer *l = (mbgl::style::PluginLayer *)self.rawLayer;
|
mbgl::style::PluginLayer *l = (mbgl::style::PluginLayer *)self.rawLayer;
|
||||||
if (l->_platformReference) {
|
if (l->_platformReference) {
|
||||||
MLNPluginLayer *pl = (__bridge MLNPluginLayer *)l->_platformReference;
|
MLNPluginLayer *pl = (__bridge MLNPluginLayer *)l->_platformReference;
|
||||||
@@ -22,14 +19,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
MLNStyleLayer *mbgl::PluginLayerPeerFactory::createPeer(style::Layer *rawLayer) {
|
||||||
MLNStyleLayer* mbgl::PluginLayerPeerFactory::createPeer(style::Layer *rawLayer) {
|
|
||||||
return [[MLNPluginStyleLayer alloc] initWithRawLayer:rawLayer];
|
return [[MLNPluginStyleLayer alloc] initWithRawLayer:rawLayer];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,20 @@
|
|||||||
#import "MLNPointAnnotation.h"
|
#import "MLNPointAnnotation.h"
|
||||||
|
|
||||||
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
#import "MLNShape_Private.h"
|
#import "MLNShape_Private.h"
|
||||||
#import "NSCoder+MLNAdditions.h"
|
#import "NSCoder+MLNAdditions.h"
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
|
||||||
|
|
||||||
#import <mbgl/util/geometry.hpp>
|
#import <mbgl/util/geometry.hpp>
|
||||||
|
|
||||||
|
|
||||||
@implementation MLNPointAnnotation
|
@implementation MLNPointAnnotation
|
||||||
|
|
||||||
@synthesize coordinate;
|
@synthesize coordinate;
|
||||||
|
|
||||||
+ (BOOL)supportsSecureCoding
|
+ (BOOL)supportsSecureCoding {
|
||||||
{
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithCoder:(NSCoder *)coder
|
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||||
{
|
|
||||||
MLNLogInfo(@"Initializing with coder.");
|
MLNLogInfo(@"Initializing with coder.");
|
||||||
if (self = [super initWithCoder:coder]) {
|
if (self = [super initWithCoder:coder]) {
|
||||||
self.coordinate = [coder decodeMLNCoordinateForKey:@"coordinate"];
|
self.coordinate = [coder decodeMLNCoordinateForKey:@"coordinate"];
|
||||||
@@ -25,46 +22,44 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)encodeWithCoder:(NSCoder *)coder
|
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||||
{
|
|
||||||
[super encodeWithCoder:coder];
|
[super encodeWithCoder:coder];
|
||||||
[coder encodeMLNCoordinate:coordinate forKey:@"coordinate"];
|
[coder encodeMLNCoordinate:coordinate forKey:@"coordinate"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)other
|
- (BOOL)isEqual:(id)other {
|
||||||
{
|
|
||||||
if (other == self) return YES;
|
if (other == self) return YES;
|
||||||
if (![other isKindOfClass:[MLNPointAnnotation class]]) return NO;
|
if (![other isKindOfClass:[MLNPointAnnotation class]]) return NO;
|
||||||
|
|
||||||
MLNPointAnnotation *otherAnnotation = other;
|
MLNPointAnnotation *otherAnnotation = other;
|
||||||
return ([super isEqual:other]
|
return ([super isEqual:other] &&
|
||||||
&& self.coordinate.latitude == otherAnnotation.coordinate.latitude
|
self.coordinate.latitude == otherAnnotation.coordinate.latitude &&
|
||||||
&& self.coordinate.longitude == otherAnnotation.coordinate.longitude);
|
self.coordinate.longitude == otherAnnotation.coordinate.longitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)hash
|
- (NSUInteger)hash {
|
||||||
{
|
|
||||||
return [super hash] + @(self.coordinate.latitude).hash + @(self.coordinate.longitude).hash;
|
return [super hash] + @(self.coordinate.latitude).hash + @(self.coordinate.longitude).hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
return [NSString
|
||||||
return [NSString stringWithFormat:@"<%@: %p; title = %@; subtitle = %@; coordinate = %f, %f>",
|
stringWithFormat:@"<%@: %p; title = %@; subtitle = %@; coordinate = %f, %f>",
|
||||||
NSStringFromClass([self class]), (void *)self,
|
NSStringFromClass([self class]), (void *)self,
|
||||||
self.title ? [NSString stringWithFormat:@"\"%@\"", self.title] : self.title,
|
self.title ? [NSString stringWithFormat:@"\"%@\"", self.title] : self.title,
|
||||||
self.subtitle ? [NSString stringWithFormat:@"\"%@\"", self.subtitle] : self.subtitle,
|
self.subtitle ? [NSString stringWithFormat:@"\"%@\"", self.subtitle]
|
||||||
|
: self.subtitle,
|
||||||
coordinate.latitude, coordinate.longitude];
|
coordinate.latitude, coordinate.longitude];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)geoJSONDictionary
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
{
|
return @{
|
||||||
return @{@"type": @"Point",
|
@"type" : @"Point",
|
||||||
@"coordinates": @[@(self.coordinate.longitude), @(self.coordinate.latitude)]};
|
@"coordinates" : @[ @(self.coordinate.longitude), @(self.coordinate.latitude) ]
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::Geometry<double>)geometryObject
|
- (mbgl::Geometry<double>)geometryObject {
|
||||||
{
|
mbgl::Point<double> point = {self.coordinate.longitude, self.coordinate.latitude};
|
||||||
mbgl::Point<double> point = { self.coordinate.longitude, self.coordinate.latitude };
|
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
#import "MLNPointCollection_Private.h"
|
|
||||||
#import "MLNGeometry_Private.h"
|
#import "MLNGeometry_Private.h"
|
||||||
#import "NSArray+MLNAdditions.h"
|
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
#import "MLNPointCollection_Private.h"
|
||||||
|
#import "NSArray+MLNAdditions.h"
|
||||||
|
|
||||||
#import <mbgl/util/geojson.hpp>
|
#import <mbgl/util/geojson.hpp>
|
||||||
#import <mbgl/util/geometry.hpp>
|
#import <mbgl/util/geometry.hpp>
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
@implementation MLNPointCollection
|
@implementation MLNPointCollection {
|
||||||
{
|
|
||||||
std::optional<mbgl::LatLngBounds> _bounds;
|
std::optional<mbgl::LatLngBounds> _bounds;
|
||||||
std::vector<CLLocationCoordinate2D> _coordinates;
|
std::vector<CLLocationCoordinate2D> _coordinates;
|
||||||
}
|
}
|
||||||
@@ -18,18 +17,16 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (instancetype)pointCollectionWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count
|
+ (instancetype)pointCollectionWithCoordinates:(const CLLocationCoordinate2D *)coords
|
||||||
{
|
count:(NSUInteger)count {
|
||||||
return [[self alloc] initWithCoordinates:coords count:count];
|
return [[self alloc] initWithCoordinates:coords count:count];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count
|
- (instancetype)initWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Initializing with %lu coordinates.", (unsigned long)count);
|
MLNLogDebug(@"Initializing with %lu coordinates.", (unsigned long)count);
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self)
|
if (self) {
|
||||||
{
|
_coordinates = {coords, coords + count};
|
||||||
_coordinates = { coords, coords + count };
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -37,7 +34,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
- (nullable instancetype)initWithCoder:(NSCoder *)decoder {
|
- (nullable instancetype)initWithCoder:(NSCoder *)decoder {
|
||||||
MLNLogInfo(@"Initializing with coder.O");
|
MLNLogInfo(@"Initializing with coder.O");
|
||||||
if (self = [super initWithCoder:decoder]) {
|
if (self = [super initWithCoder:decoder]) {
|
||||||
NSSet<Class> *coordinatesClasses = [NSSet setWithArray:@[[NSDictionary class], [NSArray class]]];
|
NSSet<Class> *coordinatesClasses =
|
||||||
|
[NSSet setWithArray:@[ [NSDictionary class], [NSArray class] ]];
|
||||||
NSArray *coordinates = [decoder decodeObjectOfClasses:coordinatesClasses forKey:@"coordinates"];
|
NSArray *coordinates = [decoder decodeObjectOfClasses:coordinatesClasses forKey:@"coordinates"];
|
||||||
_coordinates = [coordinates mgl_coordinates];
|
_coordinates = [coordinates mgl_coordinates];
|
||||||
}
|
}
|
||||||
@@ -54,8 +52,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
if (![other isKindOfClass:[MLNPointCollection class]]) return NO;
|
if (![other isKindOfClass:[MLNPointCollection class]]) return NO;
|
||||||
|
|
||||||
MLNPointCollection *otherCollection = (MLNPointCollection *)other;
|
MLNPointCollection *otherCollection = (MLNPointCollection *)other;
|
||||||
return ([super isEqual:other]
|
return ([super isEqual:other] &&
|
||||||
&& ((![self geoJSONDictionary] && ![otherCollection geoJSONDictionary]) || [[self geoJSONDictionary] isEqualToDictionary:[otherCollection geoJSONDictionary]]));
|
((![self geoJSONDictionary] && ![otherCollection geoJSONDictionary]) ||
|
||||||
|
[[self geoJSONDictionary] isEqualToDictionary:[otherCollection geoJSONDictionary]]));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MLNCoordinateBounds)overlayBounds {
|
- (MLNCoordinateBounds)overlayBounds {
|
||||||
@@ -73,66 +72,59 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
return MLNCoordinateBoundsFromLatLngBounds(*_bounds);
|
return MLNCoordinateBoundsFromLatLngBounds(*_bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)pointCount
|
- (NSUInteger)pointCount {
|
||||||
{
|
|
||||||
return _coordinates.size();
|
return _coordinates.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CLLocationCoordinate2D *)coordinates
|
- (CLLocationCoordinate2D *)coordinates {
|
||||||
{
|
|
||||||
return _coordinates.data();
|
return _coordinates.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CLLocationCoordinate2D)coordinate
|
- (CLLocationCoordinate2D)coordinate {
|
||||||
{
|
|
||||||
MLNAssert([self pointCount] > 0, @"A multipoint must have coordinates");
|
MLNAssert([self pointCount] > 0, @"A multipoint must have coordinates");
|
||||||
return _coordinates.at(0);
|
return _coordinates.at(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range
|
- (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range {
|
||||||
{
|
if (range.location + range.length > [self pointCount]) {
|
||||||
if (range.location + range.length > [self pointCount])
|
[NSException
|
||||||
{
|
raise:NSRangeException
|
||||||
[NSException raise:NSRangeException
|
|
||||||
format:@"Invalid coordinate range %@ extends beyond current coordinate count of %ld",
|
format:@"Invalid coordinate range %@ extends beyond current coordinate count of %ld",
|
||||||
NSStringFromRange(range), (unsigned long)[self pointCount]];
|
NSStringFromRange(range), (unsigned long)[self pointCount]];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::copy(_coordinates.begin() + range.location, _coordinates.begin() + NSMaxRange(range), coords);
|
std::copy(_coordinates.begin() + range.location, _coordinates.begin() + NSMaxRange(range),
|
||||||
|
coords);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)intersectsOverlayBounds:(MLNCoordinateBounds)overlayBounds
|
- (BOOL)intersectsOverlayBounds:(MLNCoordinateBounds)overlayBounds {
|
||||||
{
|
|
||||||
return MLNCoordinateBoundsIntersectsCoordinateBounds(self.overlayBounds, overlayBounds);
|
return MLNCoordinateBoundsIntersectsCoordinateBounds(self.overlayBounds, overlayBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::Geometry<double>)geometryObject
|
- (mbgl::Geometry<double>)geometryObject {
|
||||||
{
|
|
||||||
mbgl::MultiPoint<double> multiPoint;
|
mbgl::MultiPoint<double> multiPoint;
|
||||||
multiPoint.reserve(self.pointCount);
|
multiPoint.reserve(self.pointCount);
|
||||||
for (NSUInteger i = 0; i < self.pointCount; i++)
|
for (NSUInteger i = 0; i < self.pointCount; i++) {
|
||||||
{
|
multiPoint.push_back(
|
||||||
multiPoint.push_back(mbgl::Point<double>(self.coordinates[i].longitude, self.coordinates[i].latitude));
|
mbgl::Point<double>(self.coordinates[i].longitude, self.coordinates[i].latitude));
|
||||||
}
|
}
|
||||||
return multiPoint;
|
return multiPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)geoJSONDictionary
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
{
|
|
||||||
NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.pointCount];
|
NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.pointCount];
|
||||||
for (NSUInteger index = 0; index < self.pointCount; index++) {
|
for (NSUInteger index = 0; index < self.pointCount; index++) {
|
||||||
CLLocationCoordinate2D coordinate = self.coordinates[index];
|
CLLocationCoordinate2D coordinate = self.coordinates[index];
|
||||||
[coordinates addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
|
[coordinates addObject:@[ @(coordinate.longitude), @(coordinate.latitude) ]];
|
||||||
}
|
}
|
||||||
|
|
||||||
return @{@"type": @"MultiPoint",
|
return @{@"type" : @"MultiPoint", @"coordinates" : coordinates};
|
||||||
@"coordinates": coordinates};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
|
||||||
return [NSString stringWithFormat:@"<%@: %p; count = %lu; bounds = %@>",
|
return [NSString stringWithFormat:@"<%@: %p; count = %lu; bounds = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self, (unsigned long)[self pointCount],
|
NSStringFromClass([self class]), (void *)self,
|
||||||
|
(unsigned long)[self pointCount],
|
||||||
MLNStringFromCoordinateBounds(self.overlayBounds)];
|
MLNStringFromCoordinateBounds(self.overlayBounds)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
#import "MLNPolygon_Private.h"
|
#import "MLNPolygon_Private.h"
|
||||||
|
|
||||||
#import "MLNMultiPoint_Private.h"
|
|
||||||
#import "MLNGeometry_Private.h"
|
#import "MLNGeometry_Private.h"
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
#import "MLNMultiPoint_Private.h"
|
||||||
|
|
||||||
#import "MLNFeature.h"
|
#import "MLNFeature.h"
|
||||||
|
|
||||||
#import <mbgl/util/geojson.hpp>
|
|
||||||
#import <mapbox/polylabel.hpp>
|
#import <mapbox/polylabel.hpp>
|
||||||
|
#import <mbgl/util/geojson.hpp>
|
||||||
|
|
||||||
@implementation MLNPolygon
|
@implementation MLNPolygon
|
||||||
|
|
||||||
@@ -17,16 +17,22 @@
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (instancetype)polygonWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count {
|
+ (instancetype)polygonWithCoordinates:(const CLLocationCoordinate2D *)coords
|
||||||
|
count:(NSUInteger)count {
|
||||||
return [self polygonWithCoordinates:coords count:count interiorPolygons:nil];
|
return [self polygonWithCoordinates:coords count:count interiorPolygons:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (instancetype)polygonWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(NSArray<MLNPolygon *> *)interiorPolygons {
|
+ (instancetype)polygonWithCoordinates:(const CLLocationCoordinate2D *)coords
|
||||||
|
count:(NSUInteger)count
|
||||||
|
interiorPolygons:(NSArray<MLNPolygon *> *)interiorPolygons {
|
||||||
return [[self alloc] initWithCoordinates:coords count:count interiorPolygons:interiorPolygons];
|
return [[self alloc] initWithCoordinates:coords count:count interiorPolygons:interiorPolygons];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(NSArray<MLNPolygon *> *)interiorPolygons {
|
- (instancetype)initWithCoordinates:(const CLLocationCoordinate2D *)coords
|
||||||
MLNLogDebug(@"Initializing with %lu coordinates and %lu interiorPolygons.", (unsigned long)count, (unsigned long)interiorPolygons);
|
count:(NSUInteger)count
|
||||||
|
interiorPolygons:(NSArray<MLNPolygon *> *)interiorPolygons {
|
||||||
|
MLNLogDebug(@"Initializing with %lu coordinates and %lu interiorPolygons.", (unsigned long)count,
|
||||||
|
(unsigned long)interiorPolygons);
|
||||||
if (self = [super initWithCoordinates:coords count:count]) {
|
if (self = [super initWithCoordinates:coords count:count]) {
|
||||||
if (interiorPolygons.count) {
|
if (interiorPolygons.count) {
|
||||||
_interiorPolygons = interiorPolygons;
|
_interiorPolygons = interiorPolygons;
|
||||||
@@ -39,7 +45,8 @@
|
|||||||
MLNLogInfo(@"Initializng with coder.");
|
MLNLogInfo(@"Initializng with coder.");
|
||||||
self = [super initWithCoder:decoder];
|
self = [super initWithCoder:decoder];
|
||||||
if (self) {
|
if (self) {
|
||||||
NSSet<Class> *polygonClasses = [NSSet setWithArray:@[[NSDictionary class], [NSArray class], [MLNPolygon class]]];
|
NSSet<Class> *polygonClasses =
|
||||||
|
[NSSet setWithArray:@[ [NSDictionary class], [NSArray class], [MLNPolygon class] ]];
|
||||||
_interiorPolygons = [decoder decodeObjectOfClasses:polygonClasses forKey:@"interiorPolygons"];
|
_interiorPolygons = [decoder decodeObjectOfClasses:polygonClasses forKey:@"interiorPolygons"];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@@ -95,19 +102,17 @@
|
|||||||
return [self polygon];
|
return [self polygon];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::Annotation)annotationObjectWithDelegate:(id <MLNMultiPointDelegate>)delegate {
|
- (mbgl::Annotation)annotationObjectWithDelegate:(id<MLNMultiPointDelegate>)delegate {
|
||||||
|
mbgl::FillAnnotation annotation{[self polygon]};
|
||||||
mbgl::FillAnnotation annotation { [self polygon] };
|
annotation.opacity = {static_cast<float>([delegate alphaForShapeAnnotation:self])};
|
||||||
annotation.opacity = { static_cast<float>([delegate alphaForShapeAnnotation:self]) };
|
annotation.outlineColor = {[delegate strokeColorForShapeAnnotation:self]};
|
||||||
annotation.outlineColor = { [delegate strokeColorForShapeAnnotation:self] };
|
annotation.color = {[delegate fillColorForPolygonAnnotation:self]};
|
||||||
annotation.color = { [delegate fillColorForPolygonAnnotation:self] };
|
|
||||||
|
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)geoJSONDictionary {
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
return @{@"type": @"Polygon",
|
return @{@"type" : @"Polygon", @"coordinates" : self.mgl_coordinates};
|
||||||
@"coordinates": self.mgl_coordinates};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray<id> *)mgl_coordinates {
|
- (NSArray<id> *)mgl_coordinates {
|
||||||
@@ -116,7 +121,7 @@
|
|||||||
NSMutableArray *exteriorRing = [NSMutableArray array];
|
NSMutableArray *exteriorRing = [NSMutableArray array];
|
||||||
for (NSUInteger index = 0; index < self.pointCount; index++) {
|
for (NSUInteger index = 0; index < self.pointCount; index++) {
|
||||||
CLLocationCoordinate2D coordinate = self.coordinates[index];
|
CLLocationCoordinate2D coordinate = self.coordinates[index];
|
||||||
[exteriorRing addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
|
[exteriorRing addObject:@[ @(coordinate.longitude), @(coordinate.latitude) ]];
|
||||||
}
|
}
|
||||||
[coordinates addObject:exteriorRing];
|
[coordinates addObject:exteriorRing];
|
||||||
|
|
||||||
@@ -124,7 +129,7 @@
|
|||||||
NSMutableArray *interiorRing = [NSMutableArray array];
|
NSMutableArray *interiorRing = [NSMutableArray array];
|
||||||
for (NSUInteger index = 0; index < interiorPolygon.pointCount; index++) {
|
for (NSUInteger index = 0; index < interiorPolygon.pointCount; index++) {
|
||||||
CLLocationCoordinate2D coordinate = interiorPolygon.coordinates[index];
|
CLLocationCoordinate2D coordinate = interiorPolygon.coordinates[index];
|
||||||
[interiorRing addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
|
[interiorRing addObject:@[ @(coordinate.longitude), @(coordinate.latitude) ]];
|
||||||
}
|
}
|
||||||
[coordinates addObject:interiorRing];
|
[coordinates addObject:interiorRing];
|
||||||
}
|
}
|
||||||
@@ -172,7 +177,8 @@
|
|||||||
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||||
MLNLogInfo(@"Initializing with coder.");
|
MLNLogInfo(@"Initializing with coder.");
|
||||||
if (self = [super initWithCoder:decoder]) {
|
if (self = [super initWithCoder:decoder]) {
|
||||||
NSSet<Class> *polygonsClasses = [NSSet setWithArray:@[[NSDictionary class], [NSArray class], [MLNPolygon class]]];
|
NSSet<Class> *polygonsClasses =
|
||||||
|
[NSSet setWithArray:@[ [NSDictionary class], [NSArray class], [MLNPolygon class] ]];
|
||||||
_polygons = [decoder decodeObjectOfClasses:polygonsClasses forKey:@"polygons"];
|
_polygons = [decoder decodeObjectOfClasses:polygonsClasses forKey:@"polygons"];
|
||||||
|
|
||||||
mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
|
mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
|
||||||
@@ -195,8 +201,7 @@
|
|||||||
if (![other isKindOfClass:[MLNMultiPolygon class]]) return NO;
|
if (![other isKindOfClass:[MLNMultiPolygon class]]) return NO;
|
||||||
|
|
||||||
MLNMultiPolygon *otherMultiPolygon = other;
|
MLNMultiPolygon *otherMultiPolygon = other;
|
||||||
return [super isEqual:other]
|
return [super isEqual:other] && [self.polygons isEqualToArray:otherMultiPolygon.polygons];
|
||||||
&& [self.polygons isEqualToArray:otherMultiPolygon.polygons];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)hash {
|
- (NSUInteger)hash {
|
||||||
@@ -238,18 +243,18 @@
|
|||||||
- (NSDictionary *)geoJSONDictionary {
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.polygons.count];
|
NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.polygons.count];
|
||||||
for (MLNPolygonFeature *feature in self.polygons) {
|
for (MLNPolygonFeature *feature in self.polygons) {
|
||||||
[coordinates addObject: feature.mgl_coordinates];
|
[coordinates addObject:feature.mgl_coordinates];
|
||||||
}
|
}
|
||||||
return @{@"type": @"MultiPolygon",
|
return @{@"type" : @"MultiPolygon", @"coordinates" : coordinates};
|
||||||
@"coordinates": coordinates};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
return [NSString
|
||||||
return [NSString stringWithFormat:@"<%@: %p; title = %@, subtitle: = %@, count = %lu; bounds = %@>",
|
stringWithFormat:@"<%@: %p; title = %@, subtitle: = %@, count = %lu; bounds = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self,
|
NSStringFromClass([self class]), (void *)self,
|
||||||
self.title ? [NSString stringWithFormat:@"\"%@\"", self.title] : self.title,
|
self.title ? [NSString stringWithFormat:@"\"%@\"", self.title] : self.title,
|
||||||
self.subtitle ? [NSString stringWithFormat:@"\"%@\"", self.subtitle] : self.subtitle,
|
self.subtitle ? [NSString stringWithFormat:@"\"%@\"", self.subtitle]
|
||||||
|
: self.subtitle,
|
||||||
(unsigned long)self.polygons.count,
|
(unsigned long)self.polygons.count,
|
||||||
MLNStringFromCoordinateBounds(self.overlayBounds)];
|
MLNStringFromCoordinateBounds(self.overlayBounds)];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,24 @@
|
|||||||
#import "MLNPolyline_Private.h"
|
#import "MLNPolyline_Private.h"
|
||||||
|
|
||||||
#import "MLNMultiPoint_Private.h"
|
|
||||||
#import "MLNGeometry_Private.h"
|
#import "MLNGeometry_Private.h"
|
||||||
|
#import "MLNMultiPoint_Private.h"
|
||||||
|
|
||||||
#import "MLNFeature.h"
|
#import "MLNFeature.h"
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
|
||||||
#import <mbgl/util/geojson.hpp>
|
|
||||||
#import <mapbox/polylabel.hpp>
|
#import <mapbox/polylabel.hpp>
|
||||||
|
#import <mbgl/util/geojson.hpp>
|
||||||
|
|
||||||
@implementation MLNPolyline
|
@implementation MLNPolyline
|
||||||
|
|
||||||
@dynamic overlayBounds;
|
@dynamic overlayBounds;
|
||||||
|
|
||||||
+ (BOOL)supportsSecureCoding
|
+ (BOOL)supportsSecureCoding {
|
||||||
{
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (instancetype)polylineWithCoordinates:(const CLLocationCoordinate2D *)coords
|
+ (instancetype)polylineWithCoordinates:(const CLLocationCoordinate2D *)coords
|
||||||
count:(NSUInteger)count
|
count:(NSUInteger)count {
|
||||||
{
|
|
||||||
return [[self alloc] initWithCoordinates:coords count:count];
|
return [[self alloc] initWithCoordinates:coords count:count];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,11 +35,11 @@
|
|||||||
return geometry;
|
return geometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::Annotation)annotationObjectWithDelegate:(id <MLNMultiPointDelegate>)delegate {
|
- (mbgl::Annotation)annotationObjectWithDelegate:(id<MLNMultiPointDelegate>)delegate {
|
||||||
mbgl::LineAnnotation annotation { [self lineString] };
|
mbgl::LineAnnotation annotation{[self lineString]};
|
||||||
annotation.opacity = { static_cast<float>([delegate alphaForShapeAnnotation:self]) };
|
annotation.opacity = {static_cast<float>([delegate alphaForShapeAnnotation:self])};
|
||||||
annotation.color = { [delegate strokeColorForShapeAnnotation:self] };
|
annotation.color = {[delegate strokeColorForShapeAnnotation:self]};
|
||||||
annotation.width = { static_cast<float>([delegate lineWidthForPolylineAnnotation:self]) };
|
annotation.width = {static_cast<float>([delegate lineWidthForPolylineAnnotation:self])};
|
||||||
|
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
@@ -51,15 +49,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)geoJSONDictionary {
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
return @{@"type": @"LineString",
|
return @{@"type" : @"LineString", @"coordinates" : self.mgl_coordinates};
|
||||||
@"coordinates": self.mgl_coordinates};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray<id> *)mgl_coordinates {
|
- (NSArray<id> *)mgl_coordinates {
|
||||||
NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.pointCount];
|
NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.pointCount];
|
||||||
for (NSUInteger index = 0; index < self.pointCount; index++) {
|
for (NSUInteger index = 0; index < self.pointCount; index++) {
|
||||||
CLLocationCoordinate2D coordinate = self.coordinates[index];
|
CLLocationCoordinate2D coordinate = self.coordinates[index];
|
||||||
[coordinates addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
|
[coordinates addObject:@[ @(coordinate.longitude), @(coordinate.latitude) ]];
|
||||||
}
|
}
|
||||||
return [coordinates copy];
|
return [coordinates copy];
|
||||||
}
|
}
|
||||||
@@ -78,12 +75,12 @@
|
|||||||
|
|
||||||
if (count > 1 || middle > traveled) {
|
if (count > 1 || middle > traveled) {
|
||||||
for (NSUInteger i = 0; i < count; i++) {
|
for (NSUInteger i = 0; i < count; i++) {
|
||||||
|
|
||||||
// Avoid a heap buffer overflow when there are only two coordinates.
|
// Avoid a heap buffer overflow when there are only two coordinates.
|
||||||
NSUInteger nextIndex = (i + 1 == count) ? 0 : 1;
|
NSUInteger nextIndex = (i + 1 == count) ? 0 : 1;
|
||||||
|
|
||||||
MLNRadianCoordinate2D from = MLNRadianCoordinateFromLocationCoordinate(coordinates[i]);
|
MLNRadianCoordinate2D from = MLNRadianCoordinateFromLocationCoordinate(coordinates[i]);
|
||||||
MLNRadianCoordinate2D to = MLNRadianCoordinateFromLocationCoordinate(coordinates[i + nextIndex]);
|
MLNRadianCoordinate2D to =
|
||||||
|
MLNRadianCoordinateFromLocationCoordinate(coordinates[i + nextIndex]);
|
||||||
|
|
||||||
if (traveled >= middle) {
|
if (traveled >= middle) {
|
||||||
double overshoot = middle - traveled;
|
double overshoot = middle - traveled;
|
||||||
@@ -92,9 +89,8 @@
|
|||||||
}
|
}
|
||||||
to = MLNRadianCoordinateFromLocationCoordinate(coordinates[i - 1]);
|
to = MLNRadianCoordinateFromLocationCoordinate(coordinates[i - 1]);
|
||||||
CLLocationDirection direction = [self direction:from to:to] - 180;
|
CLLocationDirection direction = [self direction:from to:to] - 180;
|
||||||
MLNRadianCoordinate2D otherCoordinate = MLNRadianCoordinateAtDistanceFacingDirection(from,
|
MLNRadianCoordinate2D otherCoordinate = MLNRadianCoordinateAtDistanceFacingDirection(
|
||||||
overshoot/mbgl::util::EARTH_RADIUS_M,
|
from, overshoot / mbgl::util::EARTH_RADIUS_M, MLNRadiansFromDegrees(direction));
|
||||||
MLNRadiansFromDegrees(direction));
|
|
||||||
return CLLocationCoordinate2DMake(MLNDegreesFromRadians(otherCoordinate.latitude),
|
return CLLocationCoordinate2DMake(MLNDegreesFromRadians(otherCoordinate.latitude),
|
||||||
MLNDegreesFromRadians(otherCoordinate.longitude));
|
MLNDegreesFromRadians(otherCoordinate.longitude));
|
||||||
}
|
}
|
||||||
@@ -106,22 +102,23 @@
|
|||||||
return coordinates[count - 1];
|
return coordinates[count - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CLLocationDistance)length
|
- (CLLocationDistance)length {
|
||||||
{
|
|
||||||
CLLocationDistance length = 0.0;
|
CLLocationDistance length = 0.0;
|
||||||
|
|
||||||
NSUInteger count = self.pointCount;
|
NSUInteger count = self.pointCount;
|
||||||
CLLocationCoordinate2D *coordinates = self.coordinates;
|
CLLocationCoordinate2D *coordinates = self.coordinates;
|
||||||
|
|
||||||
for (NSUInteger i = 0; i < count - 1; i++) {
|
for (NSUInteger i = 0; i < count - 1; i++) {
|
||||||
length += (MLNDistanceBetweenRadianCoordinates(MLNRadianCoordinateFromLocationCoordinate(coordinates[i]), MLNRadianCoordinateFromLocationCoordinate(coordinates[i + 1])) * mbgl::util::EARTH_RADIUS_M);
|
length += (MLNDistanceBetweenRadianCoordinates(
|
||||||
|
MLNRadianCoordinateFromLocationCoordinate(coordinates[i]),
|
||||||
|
MLNRadianCoordinateFromLocationCoordinate(coordinates[i + 1])) *
|
||||||
|
mbgl::util::EARTH_RADIUS_M);
|
||||||
}
|
}
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CLLocationDirection)direction:(MLNRadianCoordinate2D)from to:(MLNRadianCoordinate2D)to
|
- (CLLocationDirection)direction:(MLNRadianCoordinate2D)from to:(MLNRadianCoordinate2D)to {
|
||||||
{
|
|
||||||
return MLNDegreesFromRadians(MLNRadianCoordinatesDirection(from, to));
|
return MLNDegreesFromRadians(MLNRadianCoordinatesDirection(from, to));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +162,8 @@
|
|||||||
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||||
MLNLogInfo(@"Initializing with coder.");
|
MLNLogInfo(@"Initializing with coder.");
|
||||||
if (self = [super initWithCoder:decoder]) {
|
if (self = [super initWithCoder:decoder]) {
|
||||||
NSSet<Class> *polylinesClasses = [NSSet setWithArray:@[[NSDictionary class], [NSArray class], [MLNPolyline class]]];
|
NSSet<Class> *polylinesClasses =
|
||||||
|
[NSSet setWithArray:@[ [NSDictionary class], [NSArray class], [MLNPolyline class] ]];
|
||||||
_polylines = [decoder decodeObjectOfClasses:polylinesClasses forKey:@"polylines"];
|
_polylines = [decoder decodeObjectOfClasses:polylinesClasses forKey:@"polylines"];
|
||||||
|
|
||||||
mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
|
mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
|
||||||
@@ -183,14 +181,13 @@
|
|||||||
[coder encodeObject:_polylines forKey:@"polylines"];
|
[coder encodeObject:_polylines forKey:@"polylines"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)other
|
- (BOOL)isEqual:(id)other {
|
||||||
{
|
|
||||||
if (self == other) return YES;
|
if (self == other) return YES;
|
||||||
if (![other isKindOfClass:[MLNMultiPolyline class]]) return NO;
|
if (![other isKindOfClass:[MLNMultiPolyline class]]) return NO;
|
||||||
|
|
||||||
MLNMultiPolyline *otherMultipoline = other;
|
MLNMultiPolyline *otherMultipoline = other;
|
||||||
return ([super isEqual:otherMultipoline]
|
return ([super isEqual:otherMultipoline] &&
|
||||||
&& [self.polylines isEqualToArray:otherMultipoline.polylines]);
|
[self.polylines isEqualToArray:otherMultipoline.polylines]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)hash {
|
- (NSUInteger)hash {
|
||||||
@@ -226,18 +223,18 @@
|
|||||||
- (NSDictionary *)geoJSONDictionary {
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
NSMutableArray *coordinates = [NSMutableArray array];
|
NSMutableArray *coordinates = [NSMutableArray array];
|
||||||
for (MLNPolylineFeature *feature in self.polylines) {
|
for (MLNPolylineFeature *feature in self.polylines) {
|
||||||
[coordinates addObject: feature.mgl_coordinates];
|
[coordinates addObject:feature.mgl_coordinates];
|
||||||
}
|
}
|
||||||
return @{@"type": @"MultiLineString",
|
return @{@"type" : @"MultiLineString", @"coordinates" : coordinates};
|
||||||
@"coordinates": coordinates};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
return [NSString
|
||||||
return [NSString stringWithFormat:@"<%@: %p; title = %@, subtitle: = %@, count = %lu; bounds = %@>",
|
stringWithFormat:@"<%@: %p; title = %@, subtitle: = %@, count = %lu; bounds = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self,
|
NSStringFromClass([self class]), (void *)self,
|
||||||
self.title ? [NSString stringWithFormat:@"\"%@\"", self.title] : self.title,
|
self.title ? [NSString stringWithFormat:@"\"%@\"", self.title] : self.title,
|
||||||
self.subtitle ? [NSString stringWithFormat:@"\"%@\"", self.subtitle] : self.subtitle,
|
self.subtitle ? [NSString stringWithFormat:@"\"%@\"", self.subtitle]
|
||||||
|
: self.subtitle,
|
||||||
(unsigned long)self.polylines.count,
|
(unsigned long)self.polylines.count,
|
||||||
MLNStringFromCoordinateBounds(self.overlayBounds)];
|
MLNStringFromCoordinateBounds(self.overlayBounds)];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ const MLNTileSourceOption MLNTileSourceOptionDEMEncoding = @"MLNTileSourceOption
|
|||||||
|
|
||||||
@implementation MLNRasterDEMSource
|
@implementation MLNRasterDEMSource
|
||||||
|
|
||||||
- (std::unique_ptr<mbgl::style::RasterSource>)pendingSourceWithIdentifier:(NSString *)identifier urlOrTileset:(mbgl::variant<std::string, mbgl::Tileset>)urlOrTileset tileSize:(uint16_t)tileSize {
|
- (std::unique_ptr<mbgl::style::RasterSource>)
|
||||||
auto source = std::make_unique<mbgl::style::RasterDEMSource>(identifier.UTF8String,
|
pendingSourceWithIdentifier:(NSString *)identifier
|
||||||
urlOrTileset,
|
urlOrTileset:(mbgl::variant<std::string, mbgl::Tileset>)urlOrTileset
|
||||||
tileSize);
|
tileSize:(uint16_t)tileSize {
|
||||||
|
auto source =
|
||||||
|
std::make_unique<mbgl::style::RasterDEMSource>(identifier.UTF8String, urlOrTileset, tileSize);
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -22,31 +22,42 @@ static const CGFloat MLNRasterTileSourceRetinaTileSize = 512;
|
|||||||
|
|
||||||
@implementation MLNRasterTileSource
|
@implementation MLNRasterTileSource
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL {
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
|
configurationURL:(NSURL *)configurationURL {
|
||||||
// The style specification default is 512, but 256 is the expected value for
|
// The style specification default is 512, but 256 is the expected value for
|
||||||
// any tile set that would be accessed through a mapbox: URL and therefore
|
// any tile set that would be accessed through a mapbox: URL and therefore
|
||||||
// any tile URL that this option currently affects.
|
// any tile URL that this option currently affects.
|
||||||
BOOL isMapboxURL = ([configurationURL.scheme isEqualToString:@"mapbox"]
|
BOOL isMapboxURL =
|
||||||
&& [configurationURL.host containsString:@"."]
|
([configurationURL.scheme isEqualToString:@"mapbox"] &&
|
||||||
&& (!configurationURL.path.length || [configurationURL.path isEqualToString:@"/"]));
|
[configurationURL.host containsString:@"."] &&
|
||||||
CGFloat tileSize = isMapboxURL ? MLNRasterTileSourceClassicTileSize : MLNRasterTileSourceRetinaTileSize;
|
(!configurationURL.path.length || [configurationURL.path isEqualToString:@"/"]));
|
||||||
|
CGFloat tileSize =
|
||||||
|
isMapboxURL ? MLNRasterTileSourceClassicTileSize : MLNRasterTileSourceRetinaTileSize;
|
||||||
return [self initWithIdentifier:identifier configurationURL:configurationURL tileSize:tileSize];
|
return [self initWithIdentifier:identifier configurationURL:configurationURL tileSize:tileSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL tileSize:(CGFloat)tileSize {
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
|
configurationURL:(NSURL *)configurationURL
|
||||||
|
tileSize:(CGFloat)tileSize {
|
||||||
NSString *configurationURLString = configurationURL.mgl_URLByStandardizingScheme.absoluteString;
|
NSString *configurationURLString = configurationURL.mgl_URLByStandardizingScheme.absoluteString;
|
||||||
auto source = [self pendingSourceWithIdentifier:identifier urlOrTileset:configurationURLString.UTF8String tileSize:uint16_t(round(tileSize))];
|
auto source = [self pendingSourceWithIdentifier:identifier
|
||||||
|
urlOrTileset:configurationURLString.UTF8String
|
||||||
|
tileSize:uint16_t(round(tileSize))];
|
||||||
return self = [super initWithPendingSource:std::move(source)];
|
return self = [super initWithPendingSource:std::move(source)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (std::unique_ptr<mbgl::style::RasterSource>)pendingSourceWithIdentifier:(NSString *)identifier urlOrTileset:(mbgl::variant<std::string, mbgl::Tileset>)urlOrTileset tileSize:(uint16_t)tileSize {
|
- (std::unique_ptr<mbgl::style::RasterSource>)
|
||||||
auto source = std::make_unique<mbgl::style::RasterSource>(identifier.UTF8String,
|
pendingSourceWithIdentifier:(NSString *)identifier
|
||||||
urlOrTileset,
|
urlOrTileset:(mbgl::variant<std::string, mbgl::Tileset>)urlOrTileset
|
||||||
tileSize);
|
tileSize:(uint16_t)tileSize {
|
||||||
|
auto source =
|
||||||
|
std::make_unique<mbgl::style::RasterSource>(identifier.UTF8String, urlOrTileset, tileSize);
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray<NSString *> *)tileURLTemplates options:(nullable NSDictionary<MLNTileSourceOption, id> *)options {
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
|
tileURLTemplates:(NSArray<NSString *> *)tileURLTemplates
|
||||||
|
options:(nullable NSDictionary<MLNTileSourceOption, id> *)options {
|
||||||
mbgl::Tileset tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, options);
|
mbgl::Tileset tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, options);
|
||||||
|
|
||||||
uint16_t tileSize = MLNRasterTileSourceRetinaTileSize;
|
uint16_t tileSize = MLNRasterTileSourceRetinaTileSize;
|
||||||
@@ -58,7 +69,9 @@ static const CGFloat MLNRasterTileSourceRetinaTileSize = 512;
|
|||||||
tileSize = static_cast<uint16_t>(round(tileSizeNumber.doubleValue));
|
tileSize = static_cast<uint16_t>(round(tileSizeNumber.doubleValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto source = [self pendingSourceWithIdentifier:identifier urlOrTileset:tileSet tileSize:tileSize];
|
auto source = [self pendingSourceWithIdentifier:identifier
|
||||||
|
urlOrTileset:tileSet
|
||||||
|
tileSize:tileSize];
|
||||||
return self = [super initWithPendingSource:std::move(source)];
|
return self = [super initWithPendingSource:std::move(source)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +87,8 @@ static const CGFloat MLNRasterTileSourceRetinaTileSize = 512;
|
|||||||
|
|
||||||
- (NSString *)attributionHTMLString {
|
- (NSString *)attributionHTMLString {
|
||||||
if (!self.rawSource) {
|
if (!self.rawSource) {
|
||||||
MLNAssert(0, @"Source with identifier `%@` was invalidated after a style change", self.identifier);
|
MLNAssert(0, @"Source with identifier `%@` was invalidated after a style change",
|
||||||
|
self.identifier);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
dependent way.
|
dependent way.
|
||||||
*/
|
*/
|
||||||
class MLNRenderFrontend : public mbgl::RendererFrontend {
|
class MLNRenderFrontend : public mbgl::RendererFrontend {
|
||||||
public:
|
public:
|
||||||
MLNRenderFrontend(std::unique_ptr<mbgl::Renderer> renderer_, MLNMapView* nativeView_,
|
MLNRenderFrontend(std::unique_ptr<mbgl::Renderer> renderer_, MLNMapView* nativeView_,
|
||||||
mbgl::gfx::RendererBackend& mbglBackend_, bool async = false)
|
mbgl::gfx::RendererBackend& mbglBackend_, bool async = false)
|
||||||
: renderer(std::move(renderer_)), nativeView(nativeView_), mbglBackend(mbglBackend_) {
|
: renderer(std::move(renderer_)), nativeView(nativeView_), mbglBackend(mbglBackend_) {
|
||||||
@@ -73,7 +73,7 @@ class MLNRenderFrontend : public mbgl::RendererFrontend {
|
|||||||
renderer->reduceMemoryUse();
|
renderer->reduceMemoryUse();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<mbgl::Renderer> renderer;
|
std::unique_ptr<mbgl::Renderer> renderer;
|
||||||
__weak MLNMapView* nativeView = nullptr;
|
__weak MLNMapView* nativeView = nullptr;
|
||||||
mbgl::gfx::RendererBackend& mbglBackend;
|
mbgl::gfx::RendererBackend& mbglBackend;
|
||||||
|
|||||||
@@ -34,5 +34,4 @@
|
|||||||
_stencilUpdates = stats.stencilUpdates;
|
_stencilUpdates = stats.stencilUpdates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
|
|
||||||
- (instancetype)init {
|
- (instancetype)init {
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
self.tileServerOptionsInternal = new mbgl::TileServerOptions(mbgl::TileServerOptions::DefaultConfiguration());
|
self.tileServerOptionsInternal =
|
||||||
|
new mbgl::TileServerOptions(mbgl::TileServerOptions::DefaultConfiguration());
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -65,8 +66,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
+ (void)setApiKey:(NSString *)apiKey {
|
+ (void)setApiKey:(NSString *)apiKey {
|
||||||
apiKey = [apiKey stringByTrimmingCharactersInSet:
|
apiKey =
|
||||||
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
[apiKey stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||||
if (!apiKey.length) {
|
if (!apiKey.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -88,7 +89,7 @@
|
|||||||
|
|
||||||
+ (NSURL *)apiBaseURL {
|
+ (NSURL *)apiBaseURL {
|
||||||
auto baseUrl = [MLNSettings sharedSettings].tileServerOptionsInternal->baseURL();
|
auto baseUrl = [MLNSettings sharedSettings].tileServerOptionsInternal->baseURL();
|
||||||
NSString* baseUrlNSStr = [NSString stringWithUTF8String:baseUrl.c_str()];
|
NSString *baseUrlNSStr = [NSString stringWithUTF8String:baseUrl.c_str()];
|
||||||
NSURL *url = [NSURL URLWithString:baseUrlNSStr];
|
NSURL *url = [NSURL URLWithString:baseUrlNSStr];
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
@@ -109,7 +110,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
+ (void)useWellKnownTileServer:(MLNWellKnownTileServer)tileServer {
|
+ (void)useWellKnownTileServer:(MLNWellKnownTileServer)tileServer {
|
||||||
switch(tileServer){
|
switch (tileServer) {
|
||||||
case MLNMapTiler:
|
case MLNMapTiler:
|
||||||
[MLNSettings setTileServerOptionsInternal:mbgl::TileServerOptions::MapTilerConfiguration()];
|
[MLNSettings setTileServerOptionsInternal:mbgl::TileServerOptions::MapTilerConfiguration()];
|
||||||
break;
|
break;
|
||||||
@@ -124,46 +125,48 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)setTileServerOptions:(MLNTileServerOptions*)options {
|
+ (void)setTileServerOptions:(MLNTileServerOptions *)options {
|
||||||
|
|
||||||
auto opts = mbgl::TileServerOptions()
|
auto opts = mbgl::TileServerOptions()
|
||||||
.withBaseURL(std::string([options.baseURL UTF8String]))
|
.withBaseURL(std::string([options.baseURL UTF8String]))
|
||||||
.withUriSchemeAlias(std::string([options.uriSchemeAlias UTF8String]))
|
.withUriSchemeAlias(std::string([options.uriSchemeAlias UTF8String]))
|
||||||
.withApiKeyParameterName(std::string([options.apiKeyParameterName UTF8String]));
|
.withApiKeyParameterName(std::string([options.apiKeyParameterName UTF8String]));
|
||||||
|
|
||||||
opts.withSourceTemplate(
|
opts.withSourceTemplate(std::string([options.sourceTemplate UTF8String]),
|
||||||
std::string([options.sourceTemplate UTF8String]),
|
|
||||||
std::string([options.sourceDomainName UTF8String]),
|
std::string([options.sourceDomainName UTF8String]),
|
||||||
options.sourceVersionPrefix ? std::string([options.sourceVersionPrefix UTF8String]) : std::optional<std::string>{});
|
options.sourceVersionPrefix
|
||||||
|
? std::string([options.sourceVersionPrefix UTF8String])
|
||||||
|
: std::optional<std::string>{});
|
||||||
|
|
||||||
opts.withStyleTemplate(
|
opts.withStyleTemplate(std::string([options.styleTemplate UTF8String]),
|
||||||
std::string([options.styleTemplate UTF8String]),
|
|
||||||
std::string([options.styleDomainName UTF8String]),
|
std::string([options.styleDomainName UTF8String]),
|
||||||
options.styleVersionPrefix ? std::string([options.styleVersionPrefix UTF8String]) : std::optional<std::string>{});
|
options.styleVersionPrefix
|
||||||
|
? std::string([options.styleVersionPrefix UTF8String])
|
||||||
|
: std::optional<std::string>{});
|
||||||
|
|
||||||
opts.withSpritesTemplate(
|
opts.withSpritesTemplate(std::string([options.spritesTemplate UTF8String]),
|
||||||
std::string([options.spritesTemplate UTF8String]),
|
|
||||||
std::string([options.spritesDomainName UTF8String]),
|
std::string([options.spritesDomainName UTF8String]),
|
||||||
options.spritesVersionPrefix ? std::string([options.spritesVersionPrefix UTF8String]) : std::optional<std::string>{});
|
options.spritesVersionPrefix
|
||||||
|
? std::string([options.spritesVersionPrefix UTF8String])
|
||||||
|
: std::optional<std::string>{});
|
||||||
|
|
||||||
opts.withGlyphsTemplate(
|
opts.withGlyphsTemplate(std::string([options.glyphsTemplate UTF8String]),
|
||||||
std::string([options.glyphsTemplate UTF8String]),
|
|
||||||
std::string([options.glyphsDomainName UTF8String]),
|
std::string([options.glyphsDomainName UTF8String]),
|
||||||
options.glyphsVersionPrefix ? std::string([options.glyphsVersionPrefix UTF8String]) : std::optional<std::string>{});
|
options.glyphsVersionPrefix
|
||||||
|
? std::string([options.glyphsVersionPrefix UTF8String])
|
||||||
|
: std::optional<std::string>{});
|
||||||
|
|
||||||
opts.withTileTemplate(
|
opts.withTileTemplate(std::string([options.tileTemplate UTF8String]),
|
||||||
std::string([options.tileTemplate UTF8String]),
|
|
||||||
std::string([options.tileDomainName UTF8String]),
|
std::string([options.tileDomainName UTF8String]),
|
||||||
options.tileVersionPrefix ? std::string([options.tileVersionPrefix UTF8String]) : std::optional<std::string>{});
|
options.tileVersionPrefix
|
||||||
|
? std::string([options.tileVersionPrefix UTF8String])
|
||||||
|
: std::optional<std::string>{});
|
||||||
|
|
||||||
std::vector<mbgl::util::DefaultStyle> defaultStyles;
|
std::vector<mbgl::util::DefaultStyle> defaultStyles;
|
||||||
if (options.defaultStyles) {
|
if (options.defaultStyles) {
|
||||||
for (MLNDefaultStyle* objCStyle in options.defaultStyles) {
|
for (MLNDefaultStyle *objCStyle in options.defaultStyles) {
|
||||||
mbgl::util::DefaultStyle cppStyle(
|
mbgl::util::DefaultStyle cppStyle(std::string([objCStyle.url.absoluteString UTF8String]),
|
||||||
std::string([objCStyle.url.absoluteString UTF8String]),
|
|
||||||
std::string([objCStyle.name UTF8String]),
|
std::string([objCStyle.name UTF8String]),
|
||||||
objCStyle.version
|
objCStyle.version);
|
||||||
);
|
|
||||||
defaultStyles.push_back(cppStyle);
|
defaultStyles.push_back(cppStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,43 +176,57 @@
|
|||||||
self.tileServerOptionsInternal = opts;
|
self.tileServerOptionsInternal = opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (MLNTileServerOptions*)tileServerOptions {
|
+ (MLNTileServerOptions *)tileServerOptions {
|
||||||
auto cppOpts = [MLNSettings sharedSettings].tileServerOptionsInternal;
|
auto cppOpts = [MLNSettings sharedSettings].tileServerOptionsInternal;
|
||||||
if (cppOpts) {
|
if (cppOpts) {
|
||||||
|
MLNTileServerOptions *retVal = [[MLNTileServerOptions alloc] init];
|
||||||
MLNTileServerOptions* retVal = [[MLNTileServerOptions alloc] init];
|
|
||||||
retVal.baseURL = [NSString stringWithUTF8String:cppOpts->baseURL().c_str()];
|
retVal.baseURL = [NSString stringWithUTF8String:cppOpts->baseURL().c_str()];
|
||||||
retVal.uriSchemeAlias = [NSString stringWithUTF8String:cppOpts->uriSchemeAlias().c_str()];
|
retVal.uriSchemeAlias = [NSString stringWithUTF8String:cppOpts->uriSchemeAlias().c_str()];
|
||||||
retVal.apiKeyParameterName = [NSString stringWithUTF8String:cppOpts->apiKeyParameterName().c_str()];
|
retVal.apiKeyParameterName =
|
||||||
|
[NSString stringWithUTF8String:cppOpts->apiKeyParameterName().c_str()];
|
||||||
|
|
||||||
retVal.sourceTemplate = [NSString stringWithUTF8String:cppOpts->sourceTemplate().c_str()];
|
retVal.sourceTemplate = [NSString stringWithUTF8String:cppOpts->sourceTemplate().c_str()];
|
||||||
retVal.sourceDomainName = [NSString stringWithUTF8String:cppOpts->sourceDomainName().c_str()];
|
retVal.sourceDomainName = [NSString stringWithUTF8String:cppOpts->sourceDomainName().c_str()];
|
||||||
retVal.sourceVersionPrefix = cppOpts->sourceVersionPrefix().has_value() ? [NSString stringWithUTF8String:cppOpts->sourceVersionPrefix().value().c_str()] : nil;
|
retVal.sourceVersionPrefix =
|
||||||
|
cppOpts->sourceVersionPrefix().has_value()
|
||||||
|
? [NSString stringWithUTF8String:cppOpts->sourceVersionPrefix().value().c_str()]
|
||||||
|
: nil;
|
||||||
|
|
||||||
retVal.styleTemplate = [NSString stringWithUTF8String:cppOpts->styleTemplate().c_str()];
|
retVal.styleTemplate = [NSString stringWithUTF8String:cppOpts->styleTemplate().c_str()];
|
||||||
retVal.styleDomainName = [NSString stringWithUTF8String:cppOpts->styleDomainName().c_str()];
|
retVal.styleDomainName = [NSString stringWithUTF8String:cppOpts->styleDomainName().c_str()];
|
||||||
retVal.styleVersionPrefix = cppOpts->styleVersionPrefix().has_value() ? [NSString stringWithUTF8String:cppOpts->styleVersionPrefix().value().c_str()] : nil;
|
retVal.styleVersionPrefix =
|
||||||
|
cppOpts->styleVersionPrefix().has_value()
|
||||||
|
? [NSString stringWithUTF8String:cppOpts->styleVersionPrefix().value().c_str()]
|
||||||
|
: nil;
|
||||||
|
|
||||||
retVal.spritesTemplate = [NSString stringWithUTF8String:cppOpts->spritesTemplate().c_str()];
|
retVal.spritesTemplate = [NSString stringWithUTF8String:cppOpts->spritesTemplate().c_str()];
|
||||||
retVal.spritesDomainName = [NSString stringWithUTF8String:cppOpts->spritesDomainName().c_str()];
|
retVal.spritesDomainName = [NSString stringWithUTF8String:cppOpts->spritesDomainName().c_str()];
|
||||||
retVal.spritesVersionPrefix = cppOpts->styleVersionPrefix().has_value() ? [NSString stringWithUTF8String:cppOpts->spritesVersionPrefix().value().c_str()] : nil;
|
retVal.spritesVersionPrefix =
|
||||||
|
cppOpts->styleVersionPrefix().has_value()
|
||||||
|
? [NSString stringWithUTF8String:cppOpts->spritesVersionPrefix().value().c_str()]
|
||||||
|
: nil;
|
||||||
|
|
||||||
retVal.glyphsTemplate = [NSString stringWithUTF8String:cppOpts->glyphsTemplate().c_str()];
|
retVal.glyphsTemplate = [NSString stringWithUTF8String:cppOpts->glyphsTemplate().c_str()];
|
||||||
retVal.glyphsDomainName = [NSString stringWithUTF8String:cppOpts->glyphsDomainName().c_str()];
|
retVal.glyphsDomainName = [NSString stringWithUTF8String:cppOpts->glyphsDomainName().c_str()];
|
||||||
retVal.glyphsVersionPrefix = cppOpts->styleVersionPrefix().has_value() ? [NSString stringWithUTF8String:cppOpts->glyphsVersionPrefix().value().c_str()] : nil;
|
retVal.glyphsVersionPrefix =
|
||||||
|
cppOpts->styleVersionPrefix().has_value()
|
||||||
|
? [NSString stringWithUTF8String:cppOpts->glyphsVersionPrefix().value().c_str()]
|
||||||
|
: nil;
|
||||||
|
|
||||||
retVal.tileTemplate = [NSString stringWithUTF8String:cppOpts->tileTemplate().c_str()];
|
retVal.tileTemplate = [NSString stringWithUTF8String:cppOpts->tileTemplate().c_str()];
|
||||||
retVal.tileDomainName = [NSString stringWithUTF8String:cppOpts->tileDomainName().c_str()];
|
retVal.tileDomainName = [NSString stringWithUTF8String:cppOpts->tileDomainName().c_str()];
|
||||||
retVal.tileVersionPrefix = cppOpts->styleVersionPrefix().has_value() ? [NSString stringWithUTF8String:cppOpts->tileVersionPrefix().value().c_str()] : nil;
|
retVal.tileVersionPrefix =
|
||||||
|
cppOpts->styleVersionPrefix().has_value()
|
||||||
|
? [NSString stringWithUTF8String:cppOpts->tileVersionPrefix().value().c_str()]
|
||||||
|
: nil;
|
||||||
|
|
||||||
std::vector<mbgl::util::DefaultStyle> cppDefaultStyles = cppOpts->defaultStyles();
|
std::vector<mbgl::util::DefaultStyle> cppDefaultStyles = cppOpts->defaultStyles();
|
||||||
|
|
||||||
|
NSMutableArray<MLNDefaultStyle *> *mglStyles = [[NSMutableArray<MLNDefaultStyle *> alloc] init];
|
||||||
NSMutableArray<MLNDefaultStyle*>* mglStyles = [[NSMutableArray<MLNDefaultStyle*> alloc] init];
|
|
||||||
for (auto it = begin(cppDefaultStyles); it != end(cppDefaultStyles); ++it) {
|
for (auto it = begin(cppDefaultStyles); it != end(cppDefaultStyles); ++it) {
|
||||||
NSString* url = [NSString stringWithUTF8String:it->getUrl().c_str()];
|
NSString *url = [NSString stringWithUTF8String:it->getUrl().c_str()];
|
||||||
|
|
||||||
auto* mglDefaultStyle = [[MLNDefaultStyle alloc] init];
|
auto *mglDefaultStyle = [[MLNDefaultStyle alloc] init];
|
||||||
mglDefaultStyle.url = [NSURL URLWithString:url];
|
mglDefaultStyle.url = [NSURL URLWithString:url];
|
||||||
mglDefaultStyle.name = [NSString stringWithUTF8String:it->getName().c_str()];
|
mglDefaultStyle.name = [NSString stringWithUTF8String:it->getName().c_str()];
|
||||||
mglDefaultStyle.version = it->getCurrentVersion();
|
mglDefaultStyle.version = it->getCurrentVersion();
|
||||||
@@ -226,7 +243,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
#import "MLNFeature_Private.h"
|
#import "MLNFeature_Private.h"
|
||||||
|
|
||||||
#import "NSString+MLNAdditions.h"
|
|
||||||
#import "MLNTypes.h"
|
#import "MLNTypes.h"
|
||||||
|
#import "NSString+MLNAdditions.h"
|
||||||
|
|
||||||
#import <mbgl/util/geo.hpp>
|
#import <mbgl/util/geo.hpp>
|
||||||
|
|
||||||
@@ -13,7 +13,9 @@ bool operator==(const CLLocationCoordinate2D lhs, const CLLocationCoordinate2D r
|
|||||||
|
|
||||||
@implementation MLNShape
|
@implementation MLNShape
|
||||||
|
|
||||||
+ (nullable MLNShape *)shapeWithData:(NSData *)data encoding:(NSStringEncoding)encoding error:(NSError * _Nullable *)outError {
|
+ (nullable MLNShape *)shapeWithData:(NSData *)data
|
||||||
|
encoding:(NSStringEncoding)encoding
|
||||||
|
error:(NSError *_Nullable *)outError {
|
||||||
NSString *string = [[NSString alloc] initWithData:data encoding:encoding];
|
NSString *string = [[NSString alloc] initWithData:data encoding:encoding];
|
||||||
if (!string) {
|
if (!string) {
|
||||||
if (outError) {
|
if (outError) {
|
||||||
@@ -27,8 +29,10 @@ bool operator==(const CLLocationCoordinate2D lhs, const CLLocationCoordinate2D r
|
|||||||
return MLNShapeFromGeoJSON(geojson);
|
return MLNShapeFromGeoJSON(geojson);
|
||||||
} catch (std::runtime_error &err) {
|
} catch (std::runtime_error &err) {
|
||||||
if (outError) {
|
if (outError) {
|
||||||
*outError = [NSError errorWithDomain:MLNErrorDomain code:MLNErrorCodeUnknown userInfo:@{
|
*outError = [NSError errorWithDomain:MLNErrorDomain
|
||||||
NSLocalizedFailureReasonErrorKey: @(err.what()),
|
code:MLNErrorCodeUnknown
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedFailureReasonErrorKey : @(err.what()),
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
return nil;
|
return nil;
|
||||||
@@ -40,8 +44,7 @@ bool operator==(const CLLocationCoordinate2D lhs, const CLLocationCoordinate2D r
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::Geometry<double>)geometryObject {
|
- (mbgl::Geometry<double>)geometryObject {
|
||||||
[NSException raise:MLNAbstractClassException
|
[NSException raise:MLNAbstractClassException format:@"MLNShape is an abstract class"];
|
||||||
format:@"MLNShape is an abstract class"];
|
|
||||||
return mbgl::Point<double>();
|
return mbgl::Point<double>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,13 +54,11 @@ bool operator==(const CLLocationCoordinate2D lhs, const CLLocationCoordinate2D r
|
|||||||
return [string dataUsingEncoding:NSUTF8StringEncoding];
|
return [string dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (BOOL)supportsSecureCoding
|
+ (BOOL)supportsSecureCoding {
|
||||||
{
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithCoder:(NSCoder *)coder
|
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||||
{
|
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
_title = [coder decodeObjectOfClass:[NSString class] forKey:@"title"];
|
_title = [coder decodeObjectOfClass:[NSString class] forKey:@"title"];
|
||||||
_subtitle = [coder decodeObjectOfClass:[NSString class] forKey:@"subtitle"];
|
_subtitle = [coder decodeObjectOfClass:[NSString class] forKey:@"subtitle"];
|
||||||
@@ -68,8 +69,7 @@ bool operator==(const CLLocationCoordinate2D lhs, const CLLocationCoordinate2D r
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)encodeWithCoder:(NSCoder *)coder
|
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||||
{
|
|
||||||
[coder encodeObject:_title forKey:@"title"];
|
[coder encodeObject:_title forKey:@"title"];
|
||||||
[coder encodeObject:_subtitle forKey:@"subtitle"];
|
[coder encodeObject:_subtitle forKey:@"subtitle"];
|
||||||
#if !TARGET_OS_IPHONE
|
#if !TARGET_OS_IPHONE
|
||||||
@@ -77,23 +77,25 @@ bool operator==(const CLLocationCoordinate2D lhs, const CLLocationCoordinate2D r
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)other
|
- (BOOL)isEqual:(id)other {
|
||||||
{
|
if (other == self) {
|
||||||
if (other == self) { return YES; }
|
return YES;
|
||||||
id <MLNAnnotation> annotation = other;
|
}
|
||||||
|
id<MLNAnnotation> annotation = other;
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
return ((!_title && ![annotation title]) || [_title isEqualToString:[annotation title]])
|
return ((!_title && ![annotation title]) || [_title isEqualToString:[annotation title]]) &&
|
||||||
&& ((!_subtitle && ![annotation subtitle]) || [_subtitle isEqualToString:[annotation subtitle]]);
|
((!_subtitle && ![annotation subtitle]) ||
|
||||||
|
[_subtitle isEqualToString:[annotation subtitle]]);
|
||||||
#else
|
#else
|
||||||
return ((!_title && ![annotation title]) || [_title isEqualToString:[annotation title]])
|
return ((!_title && ![annotation title]) || [_title isEqualToString:[annotation title]]) &&
|
||||||
&& ((!_subtitle && ![annotation subtitle]) || [_subtitle isEqualToString:[annotation subtitle]])
|
((!_subtitle && ![annotation subtitle]) ||
|
||||||
&& ((!_toolTip && ![annotation toolTip]) || [_toolTip isEqualToString:[annotation toolTip]]);
|
[_subtitle isEqualToString:[annotation subtitle]]) &&
|
||||||
|
((!_toolTip && ![annotation toolTip]) || [_toolTip isEqualToString:[annotation toolTip]]);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)hash
|
- (NSUInteger)hash {
|
||||||
{
|
|
||||||
NSUInteger hash = _title.hash + _subtitle.hash;
|
NSUInteger hash = _title.hash + _subtitle.hash;
|
||||||
#if !TARGET_OS_IPHONE
|
#if !TARGET_OS_IPHONE
|
||||||
hash += _toolTip.hash;
|
hash += _toolTip.hash;
|
||||||
@@ -101,10 +103,8 @@ bool operator==(const CLLocationCoordinate2D lhs, const CLLocationCoordinate2D r
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CLLocationCoordinate2D)coordinate
|
- (CLLocationCoordinate2D)coordinate {
|
||||||
{
|
[NSException raise:MLNAbstractClassException format:@"MLNShape is an abstract class"];
|
||||||
[NSException raise:MLNAbstractClassException
|
|
||||||
format:@"MLNShape is an abstract class"];
|
|
||||||
return kCLLocationCoordinate2DInvalid;
|
return kCLLocationCoordinate2DInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#import "MLNShapeCollection.h"
|
#import "MLNShapeCollection.h"
|
||||||
|
|
||||||
#import "MLNShape_Private.h"
|
|
||||||
#import "MLNFeature.h"
|
#import "MLNFeature.h"
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
#import "MLNShape_Private.h"
|
||||||
|
|
||||||
#import <mbgl/style/conversion/geojson.hpp>
|
#import <mbgl/style/conversion/geojson.hpp>
|
||||||
|
|
||||||
@@ -27,7 +27,8 @@
|
|||||||
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||||
MLNLogInfo(@"Initializing with coder.");
|
MLNLogInfo(@"Initializing with coder.");
|
||||||
if (self = [super initWithCoder:decoder]) {
|
if (self = [super initWithCoder:decoder]) {
|
||||||
NSSet<Class> *shapeClasses = [NSSet setWithArray:@[[NSDictionary class], [NSArray class], [MLNShape class]]];
|
NSSet<Class> *shapeClasses =
|
||||||
|
[NSSet setWithArray:@[ [NSDictionary class], [NSArray class], [MLNShape class] ]];
|
||||||
_shapes = [decoder decodeObjectOfClasses:shapeClasses forKey:@"shapes"];
|
_shapes = [decoder decodeObjectOfClasses:shapeClasses forKey:@"shapes"];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@@ -43,8 +44,8 @@
|
|||||||
if (![other isKindOfClass:[MLNShapeCollection class]]) return NO;
|
if (![other isKindOfClass:[MLNShapeCollection class]]) return NO;
|
||||||
|
|
||||||
MLNShapeCollection *otherShapeCollection = other;
|
MLNShapeCollection *otherShapeCollection = other;
|
||||||
return [super isEqual:otherShapeCollection]
|
return
|
||||||
&& [_shapes isEqualToArray:otherShapeCollection.shapes];
|
[super isEqual:otherShapeCollection] && [_shapes isEqualToArray:otherShapeCollection.shapes];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)hash {
|
- (NSUInteger)hash {
|
||||||
@@ -60,8 +61,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)geoJSONDictionary {
|
- (NSDictionary *)geoJSONDictionary {
|
||||||
return @{@"type": @"GeometryCollection",
|
return @{@"type" : @"GeometryCollection", @"geometries" : [self geometryCollection]};
|
||||||
@"geometries": [self geometryCollection]};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *)geometryCollection {
|
- (NSArray *)geometryCollection {
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
#import "MLNShapeOfflineRegion.h"
|
#import "MLNShapeOfflineRegion.h"
|
||||||
|
|
||||||
#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
|
#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#else
|
#else
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#import "MLNFeature_Private.h"
|
||||||
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
#import "MLNOfflineRegion_Private.h"
|
#import "MLNOfflineRegion_Private.h"
|
||||||
#import "MLNShapeOfflineRegion_Private.h"
|
#import "MLNShapeOfflineRegion_Private.h"
|
||||||
#import "MLNFeature_Private.h"
|
|
||||||
#import "MLNShape_Private.h"
|
#import "MLNShape_Private.h"
|
||||||
#import "MLNStyle.h"
|
#import "MLNStyle.h"
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
|
||||||
|
|
||||||
@interface MLNShapeOfflineRegion () <MLNOfflineRegion_Private, MLNShapeOfflineRegion_Private>
|
@interface MLNShapeOfflineRegion () <MLNOfflineRegion_Private, MLNShapeOfflineRegion_Private>
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
@synthesize styleURL = _styleURL;
|
@synthesize styleURL = _styleURL;
|
||||||
@synthesize includesIdeographicGlyphs = _includesIdeographicGlyphs;
|
@synthesize includesIdeographicGlyphs = _includesIdeographicGlyphs;
|
||||||
|
|
||||||
-(NSDictionary *)offlineStartEventAttributes {
|
- (NSDictionary *)offlineStartEventAttributes {
|
||||||
return @{};
|
return @{};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,22 +35,25 @@
|
|||||||
- (instancetype)init {
|
- (instancetype)init {
|
||||||
MLNLogInfo(@"Calling this initializer is not allowed.");
|
MLNLogInfo(@"Calling this initializer is not allowed.");
|
||||||
[NSException raise:@"Method unavailable"
|
[NSException raise:@"Method unavailable"
|
||||||
format:
|
format:@"-[MLNShapeOfflineRegion init] is unavailable. "
|
||||||
@"-[MLNShapeOfflineRegion init] is unavailable. "
|
|
||||||
@"Use -initWithStyleURL:shape:fromZoomLevel:toZoomLevel: instead."];
|
@"Use -initWithStyleURL:shape:fromZoomLevel:toZoomLevel: instead."];
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithStyleURL:(NSURL *)styleURL shape:(MLNShape *)shape fromZoomLevel:(double)minimumZoomLevel toZoomLevel:(double)maximumZoomLevel {
|
- (instancetype)initWithStyleURL:(NSURL *)styleURL
|
||||||
MLNLogDebug(@"Initializing styleURL: %@ shape: %@ fromZoomLevel: %f toZoomLevel: %f", styleURL, shape, minimumZoomLevel, maximumZoomLevel);
|
shape:(MLNShape *)shape
|
||||||
|
fromZoomLevel:(double)minimumZoomLevel
|
||||||
|
toZoomLevel:(double)maximumZoomLevel {
|
||||||
|
MLNLogDebug(@"Initializing styleURL: %@ shape: %@ fromZoomLevel: %f toZoomLevel: %f", styleURL,
|
||||||
|
shape, minimumZoomLevel, maximumZoomLevel);
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
if (!styleURL) {
|
if (!styleURL) {
|
||||||
styleURL = [MLNStyle defaultStyleURL];
|
styleURL = [MLNStyle defaultStyleURL];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!styleURL.scheme) {
|
if (!styleURL.scheme) {
|
||||||
[NSException raise:@"Invalid style URL" format:
|
[NSException raise:@"Invalid style URL"
|
||||||
@"%@ does not support setting a relative file URL as the style URL. "
|
format:@"%@ does not support setting a relative file URL as the style URL. "
|
||||||
@"To download the online resources required by this style, "
|
@"To download the online resources required by this style, "
|
||||||
@"specify a URL to an online copy of this style. "
|
@"specify a URL to an online copy of this style. "
|
||||||
@"For Mapbox-hosted styles, use the mapbox: scheme.",
|
@"For Mapbox-hosted styles, use the mapbox: scheme.",
|
||||||
@@ -66,40 +69,47 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithOfflineRegionDefinition:(const mbgl::OfflineGeometryRegionDefinition &)definition {
|
- (instancetype)initWithOfflineRegionDefinition:
|
||||||
|
(const mbgl::OfflineGeometryRegionDefinition &)definition {
|
||||||
NSURL *styleURL = [NSURL URLWithString:@(definition.styleURL.c_str())];
|
NSURL *styleURL = [NSURL URLWithString:@(definition.styleURL.c_str())];
|
||||||
MLNShape *shape = MLNShapeFromGeoJSON(definition.geometry);
|
MLNShape *shape = MLNShapeFromGeoJSON(definition.geometry);
|
||||||
MLNShapeOfflineRegion* result = [self initWithStyleURL:styleURL shape:shape fromZoomLevel:definition.minZoom toZoomLevel:definition.maxZoom];
|
MLNShapeOfflineRegion *result = [self initWithStyleURL:styleURL
|
||||||
|
shape:shape
|
||||||
|
fromZoomLevel:definition.minZoom
|
||||||
|
toZoomLevel:definition.maxZoom];
|
||||||
result.includesIdeographicGlyphs = definition.includeIdeographs;
|
result.includesIdeographicGlyphs = definition.includeIdeographs;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (const mbgl::OfflineRegionDefinition)offlineRegionDefinition {
|
- (const mbgl::OfflineRegionDefinition)offlineRegionDefinition {
|
||||||
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
||||||
const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale];
|
const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)]
|
||||||
|
? [[UIScreen mainScreen] nativeScale]
|
||||||
|
: [[UIScreen mainScreen] scale];
|
||||||
#elif TARGET_OS_MAC
|
#elif TARGET_OS_MAC
|
||||||
const float scaleFactor = [NSScreen mainScreen].backingScaleFactor;
|
const float scaleFactor = [NSScreen mainScreen].backingScaleFactor;
|
||||||
#endif
|
#endif
|
||||||
return mbgl::OfflineGeometryRegionDefinition(_styleURL.absoluteString.UTF8String,
|
return mbgl::OfflineGeometryRegionDefinition(
|
||||||
_shape.geometryObject,
|
_styleURL.absoluteString.UTF8String, _shape.geometryObject, _minimumZoomLevel,
|
||||||
_minimumZoomLevel, _maximumZoomLevel,
|
_maximumZoomLevel, scaleFactor, _includesIdeographicGlyphs);
|
||||||
scaleFactor, _includesIdeographicGlyphs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (nullable instancetype)initWithCoder:(NSCoder *)coder {
|
- (nullable instancetype)initWithCoder:(NSCoder *)coder {
|
||||||
MLNLogInfo(@"Initializing with coder.");
|
MLNLogInfo(@"Initializing with coder.");
|
||||||
NSURL *styleURL = [coder decodeObjectForKey:@"styleURL"];
|
NSURL *styleURL = [coder decodeObjectForKey:@"styleURL"];
|
||||||
MLNShape * shape = [coder decodeObjectForKey:@"shape"];
|
MLNShape *shape = [coder decodeObjectForKey:@"shape"];
|
||||||
double minimumZoomLevel = [coder decodeDoubleForKey:@"minimumZoomLevel"];
|
double minimumZoomLevel = [coder decodeDoubleForKey:@"minimumZoomLevel"];
|
||||||
double maximumZoomLevel = [coder decodeDoubleForKey:@"maximumZoomLevel"];
|
double maximumZoomLevel = [coder decodeDoubleForKey:@"maximumZoomLevel"];
|
||||||
|
|
||||||
MLNShapeOfflineRegion* result = [self initWithStyleURL:styleURL shape:shape fromZoomLevel:minimumZoomLevel toZoomLevel:maximumZoomLevel];
|
MLNShapeOfflineRegion *result = [self initWithStyleURL:styleURL
|
||||||
|
shape:shape
|
||||||
|
fromZoomLevel:minimumZoomLevel
|
||||||
|
toZoomLevel:maximumZoomLevel];
|
||||||
result.includesIdeographicGlyphs = [coder decodeBoolForKey:@"includesIdeographicGlyphs"];
|
result.includesIdeographicGlyphs = [coder decodeBoolForKey:@"includesIdeographicGlyphs"];
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)encodeWithCoder:(NSCoder *)coder
|
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||||
{
|
|
||||||
[coder encodeObject:_styleURL forKey:@"styleURL"];
|
[coder encodeObject:_styleURL forKey:@"styleURL"];
|
||||||
[coder encodeObject:_shape forKey:@"shape"];
|
[coder encodeObject:_shape forKey:@"shape"];
|
||||||
[coder encodeDouble:_maximumZoomLevel forKey:@"maximumZoomLevel"];
|
[coder encodeDouble:_maximumZoomLevel forKey:@"maximumZoomLevel"];
|
||||||
@@ -108,7 +118,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (id)copyWithZone:(nullable NSZone *)zone {
|
- (id)copyWithZone:(nullable NSZone *)zone {
|
||||||
MLNShapeOfflineRegion* result = [[[self class] allocWithZone:zone] initWithStyleURL:_styleURL shape:_shape fromZoomLevel:_minimumZoomLevel toZoomLevel:_maximumZoomLevel];
|
MLNShapeOfflineRegion *result =
|
||||||
|
[[[self class] allocWithZone:zone] initWithStyleURL:_styleURL
|
||||||
|
shape:_shape
|
||||||
|
fromZoomLevel:_minimumZoomLevel
|
||||||
|
toZoomLevel:_maximumZoomLevel];
|
||||||
result.includesIdeographicGlyphs = _includesIdeographicGlyphs;
|
result.includesIdeographicGlyphs = _includesIdeographicGlyphs;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -122,18 +136,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
MLNShapeOfflineRegion *otherRegion = other;
|
MLNShapeOfflineRegion *otherRegion = other;
|
||||||
return (_minimumZoomLevel == otherRegion->_minimumZoomLevel
|
return (_minimumZoomLevel == otherRegion->_minimumZoomLevel &&
|
||||||
&& _maximumZoomLevel == otherRegion->_maximumZoomLevel
|
_maximumZoomLevel == otherRegion->_maximumZoomLevel &&
|
||||||
&& _shape.geometryObject == otherRegion->_shape.geometryObject
|
_shape.geometryObject == otherRegion->_shape.geometryObject &&
|
||||||
&& [_styleURL isEqual:otherRegion->_styleURL]
|
[_styleURL isEqual:otherRegion->_styleURL] &&
|
||||||
&& _includesIdeographicGlyphs == otherRegion->_includesIdeographicGlyphs);
|
_includesIdeographicGlyphs == otherRegion->_includesIdeographicGlyphs);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)hash {
|
- (NSUInteger)hash {
|
||||||
return (_styleURL.hash
|
return (_styleURL.hash + _shape.hash + @(_minimumZoomLevel).hash + @(_maximumZoomLevel).hash +
|
||||||
+ _shape.hash
|
@(_includesIdeographicGlyphs).hash);
|
||||||
+ @(_minimumZoomLevel).hash + @(_maximumZoomLevel).hash
|
|
||||||
+ @(_includesIdeographicGlyphs).hash);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,35 +1,44 @@
|
|||||||
#import "MLNFoundation_Private.h"
|
#import "MLNFoundation_Private.h"
|
||||||
#import "MLNShapeSource_Private.h"
|
#import "MLNShapeSource_Private.h"
|
||||||
|
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
|
||||||
#import "MLNStyle_Private.h"
|
|
||||||
#import "MLNStyleValue_Private.h"
|
|
||||||
#import "MLNMapView_Private.h"
|
|
||||||
#import "MLNSource_Private.h"
|
|
||||||
#import "MLNFeature_Private.h"
|
|
||||||
#import "MLNShape_Private.h"
|
|
||||||
#import "MLNCluster.h"
|
#import "MLNCluster.h"
|
||||||
|
#import "MLNFeature_Private.h"
|
||||||
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
#import "MLNMapView_Private.h"
|
||||||
|
#import "MLNShape_Private.h"
|
||||||
|
#import "MLNSource_Private.h"
|
||||||
|
#import "MLNStyleValue_Private.h"
|
||||||
|
#import "MLNStyle_Private.h"
|
||||||
|
|
||||||
#import "NSPredicate+MLNPrivateAdditions.h"
|
#import "NSPredicate+MLNPrivateAdditions.h"
|
||||||
#import "NSURL+MLNAdditions.h"
|
#import "NSURL+MLNAdditions.h"
|
||||||
|
|
||||||
#include <mbgl/map/map.hpp>
|
#include <mbgl/map/map.hpp>
|
||||||
#include <mbgl/style/sources/geojson_source.hpp>
|
|
||||||
#include <mbgl/renderer/renderer.hpp>
|
#include <mbgl/renderer/renderer.hpp>
|
||||||
|
#include <mbgl/style/sources/geojson_source.hpp>
|
||||||
|
|
||||||
const MLNShapeSourceOption MLNShapeSourceOptionBuffer = @"MLNShapeSourceOptionBuffer";
|
const MLNShapeSourceOption MLNShapeSourceOptionBuffer = @"MLNShapeSourceOptionBuffer";
|
||||||
const MLNShapeSourceOption MLNShapeSourceOptionClusterRadius = @"MLNShapeSourceOptionClusterRadius";
|
const MLNShapeSourceOption MLNShapeSourceOptionClusterRadius = @"MLNShapeSourceOptionClusterRadius";
|
||||||
const MLNShapeSourceOption MLNShapeSourceOptionClusterMinPoints = @"MLNShapeSourceOptionClusterMinPoints";
|
const MLNShapeSourceOption MLNShapeSourceOptionClusterMinPoints =
|
||||||
|
@"MLNShapeSourceOptionClusterMinPoints";
|
||||||
const MLNShapeSourceOption MLNShapeSourceOptionClustered = @"MLNShapeSourceOptionClustered";
|
const MLNShapeSourceOption MLNShapeSourceOptionClustered = @"MLNShapeSourceOptionClustered";
|
||||||
const MLNShapeSourceOption MLNShapeSourceOptionClusterProperties = @"MLNShapeSourceOptionClusterProperties";
|
const MLNShapeSourceOption MLNShapeSourceOptionClusterProperties =
|
||||||
const MLNShapeSourceOption MLNShapeSourceOptionMaximumZoomLevel = @"MLNShapeSourceOptionMaximumZoomLevel";
|
@"MLNShapeSourceOptionClusterProperties";
|
||||||
const MLNShapeSourceOption MLNShapeSourceOptionMaximumZoomLevelForClustering = @"MLNShapeSourceOptionMaximumZoomLevelForClustering";
|
const MLNShapeSourceOption MLNShapeSourceOptionMaximumZoomLevel =
|
||||||
const MLNShapeSourceOption MLNShapeSourceOptionMinimumZoomLevel = @"MLNShapeSourceOptionMinimumZoomLevel";
|
@"MLNShapeSourceOptionMaximumZoomLevel";
|
||||||
const MLNShapeSourceOption MLNShapeSourceOptionSimplificationTolerance = @"MLNShapeSourceOptionSimplificationTolerance";
|
const MLNShapeSourceOption MLNShapeSourceOptionMaximumZoomLevelForClustering =
|
||||||
const MLNShapeSourceOption MLNShapeSourceOptionLineDistanceMetrics = @"MLNShapeSourceOptionLineDistanceMetrics";
|
@"MLNShapeSourceOptionMaximumZoomLevelForClustering";
|
||||||
const MLNShapeSourceOption MLNShapeSourceOptionSynchronousUpdate = @"MLNShapeSourceOptionSynchronousUpdate";
|
const MLNShapeSourceOption MLNShapeSourceOptionMinimumZoomLevel =
|
||||||
|
@"MLNShapeSourceOptionMinimumZoomLevel";
|
||||||
|
const MLNShapeSourceOption MLNShapeSourceOptionSimplificationTolerance =
|
||||||
|
@"MLNShapeSourceOptionSimplificationTolerance";
|
||||||
|
const MLNShapeSourceOption MLNShapeSourceOptionLineDistanceMetrics =
|
||||||
|
@"MLNShapeSourceOptionLineDistanceMetrics";
|
||||||
|
const MLNShapeSourceOption MLNShapeSourceOptionSynchronousUpdate =
|
||||||
|
@"MLNShapeSourceOptionSynchronousUpdate";
|
||||||
|
|
||||||
mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(NSDictionary<MLNShapeSourceOption, id> *options) {
|
mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(
|
||||||
|
NSDictionary<MLNShapeSourceOption, id> *options) {
|
||||||
auto geoJSONOptions = mbgl::makeMutable<mbgl::style::GeoJSONOptions>();
|
auto geoJSONOptions = mbgl::makeMutable<mbgl::style::GeoJSONOptions>();
|
||||||
|
|
||||||
if (NSNumber *value = options[MLNShapeSourceOptionMinimumZoomLevel]) {
|
if (NSNumber *value = options[MLNShapeSourceOptionMinimumZoomLevel]) {
|
||||||
@@ -98,8 +107,10 @@ mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(NSD
|
|||||||
|
|
||||||
if (NSDictionary *value = options[MLNShapeSourceOptionClusterProperties]) {
|
if (NSDictionary *value = options[MLNShapeSourceOptionClusterProperties]) {
|
||||||
if (![value isKindOfClass:[NSDictionary<NSString *, NSArray *> class]]) {
|
if (![value isKindOfClass:[NSDictionary<NSString *, NSArray *> class]]) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException
|
||||||
format:@"MLNShapeSourceOptionClusterProperties must be an NSDictionary with an NSString as a key and an array containing two NSExpression objects as a value."];
|
raise:NSInvalidArgumentException
|
||||||
|
format:@"MLNShapeSourceOptionClusterProperties must be an NSDictionary with an NSString "
|
||||||
|
@"as a key and an array containing two NSExpression objects as a value."];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSEnumerator *stringEnumerator = [value keyEnumerator];
|
NSEnumerator *stringEnumerator = [value keyEnumerator];
|
||||||
@@ -109,23 +120,28 @@ mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(NSD
|
|||||||
NSArray *expressionsArray = value[key];
|
NSArray *expressionsArray = value[key];
|
||||||
if (![expressionsArray isKindOfClass:[NSArray class]]) {
|
if (![expressionsArray isKindOfClass:[NSArray class]]) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"MLNShapeSourceOptionClusterProperties dictionary member value must be an array containing two objects."];
|
format:@"MLNShapeSourceOptionClusterProperties dictionary member value must be "
|
||||||
|
@"an array containing two objects."];
|
||||||
}
|
}
|
||||||
// Check that the array has 2 values. One should be a the reduce expression and one should be the map expression.
|
// Check that the array has 2 values. One should be a the reduce expression and one should be
|
||||||
|
// the map expression.
|
||||||
if ([expressionsArray count] != 2) {
|
if ([expressionsArray count] != 2) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"MLNShapeSourceOptionClusterProperties member value requires array of two objects."];
|
format:@"MLNShapeSourceOptionClusterProperties member value requires array of "
|
||||||
|
@"two objects."];
|
||||||
}
|
}
|
||||||
|
|
||||||
// reduceExpression should be a valid NSExpression
|
// reduceExpression should be a valid NSExpression
|
||||||
NSExpression *reduceExpression = expressionsArray[0];
|
NSExpression *reduceExpression = expressionsArray[0];
|
||||||
if (![reduceExpression isKindOfClass:[NSExpression class]]) {
|
if (![reduceExpression isKindOfClass:[NSExpression class]]) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"MLNShapeSourceOptionClusterProperties array value requires two expression objects."];
|
format:@"MLNShapeSourceOptionClusterProperties array value requires two "
|
||||||
|
@"expression objects."];
|
||||||
}
|
}
|
||||||
auto reduce = MLNClusterPropertyFromNSExpression(reduceExpression);
|
auto reduce = MLNClusterPropertyFromNSExpression(reduceExpression);
|
||||||
if (!reduce) {
|
if (!reduce) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException
|
||||||
|
raise:NSInvalidArgumentException
|
||||||
format:@"Failed to convert MLNShapeSourceOptionClusterProperties reduce expression."];
|
format:@"Failed to convert MLNShapeSourceOptionClusterProperties reduce expression."];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,17 +149,20 @@ mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(NSD
|
|||||||
NSExpression *mapExpression = expressionsArray[1];
|
NSExpression *mapExpression = expressionsArray[1];
|
||||||
if (![mapExpression isKindOfClass:[NSExpression class]]) {
|
if (![mapExpression isKindOfClass:[NSExpression class]]) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"MLNShapeSourceOptionClusterProperties member value must contain a valid NSExpression."];
|
format:@"MLNShapeSourceOptionClusterProperties member value must contain a "
|
||||||
|
@"valid NSExpression."];
|
||||||
}
|
}
|
||||||
auto map = MLNClusterPropertyFromNSExpression(mapExpression);
|
auto map = MLNClusterPropertyFromNSExpression(mapExpression);
|
||||||
if (!map) {
|
if (!map) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException
|
||||||
|
raise:NSInvalidArgumentException
|
||||||
format:@"Failed to convert MLNShapeSourceOptionClusterProperties map expression."];
|
format:@"Failed to convert MLNShapeSourceOptionClusterProperties map expression."];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string keyString = std::string([key UTF8String]);
|
std::string keyString = std::string([key UTF8String]);
|
||||||
|
|
||||||
geoJSONOptions->clusterProperties.emplace(keyString, std::make_pair(std::move(map), std::move(reduce)));
|
geoJSONOptions->clusterProperties.emplace(keyString,
|
||||||
|
std::make_pair(std::move(map), std::move(reduce)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,18 +194,24 @@ mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(NSD
|
|||||||
|
|
||||||
@implementation MLNShapeSource
|
@implementation MLNShapeSource
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(NSDictionary<NSString *, id> *)options {
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
|
URL:(NSURL *)url
|
||||||
|
options:(NSDictionary<NSString *, id> *)options {
|
||||||
auto geoJSONOptions = MLNGeoJSONOptionsFromDictionary(options);
|
auto geoJSONOptions = MLNGeoJSONOptionsFromDictionary(options);
|
||||||
auto source = std::make_unique<mbgl::style::GeoJSONSource>(identifier.UTF8String, std::move(geoJSONOptions));
|
auto source = std::make_unique<mbgl::style::GeoJSONSource>(identifier.UTF8String,
|
||||||
|
std::move(geoJSONOptions));
|
||||||
if (self = [super initWithPendingSource:std::move(source)]) {
|
if (self = [super initWithPendingSource:std::move(source)]) {
|
||||||
self.URL = url;
|
self.URL = url;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MLNShape *)shape options:(NSDictionary<MLNShapeSourceOption, id> *)options {
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
|
shape:(nullable MLNShape *)shape
|
||||||
|
options:(NSDictionary<MLNShapeSourceOption, id> *)options {
|
||||||
auto geoJSONOptions = MLNGeoJSONOptionsFromDictionary(options);
|
auto geoJSONOptions = MLNGeoJSONOptionsFromDictionary(options);
|
||||||
auto source = std::make_unique<mbgl::style::GeoJSONSource>(identifier.UTF8String, std::move(geoJSONOptions));
|
auto source = std::make_unique<mbgl::style::GeoJSONSource>(identifier.UTF8String,
|
||||||
|
std::move(geoJSONOptions));
|
||||||
if (self = [super initWithPendingSource:std::move(source)]) {
|
if (self = [super initWithPendingSource:std::move(source)]) {
|
||||||
if ([shape isMemberOfClass:[MLNShapeCollection class]]) {
|
if ([shape isMemberOfClass:[MLNShapeCollection class]]) {
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
@@ -201,17 +226,25 @@ mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(NSD
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier features:(NSArray<MLNShape<MLNFeature> *> *)features options:(nullable NSDictionary<MLNShapeSourceOption, id> *)options {
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
for (id <MLNFeature> feature in features) {
|
features:(NSArray<MLNShape<MLNFeature> *> *)features
|
||||||
|
options:(nullable NSDictionary<MLNShapeSourceOption, id> *)options {
|
||||||
|
for (id<MLNFeature> feature in features) {
|
||||||
if (![feature conformsToProtocol:@protocol(MLNFeature)]) {
|
if (![feature conformsToProtocol:@protocol(MLNFeature)]) {
|
||||||
[NSException raise:NSInvalidArgumentException format:@"The object %@ included in the features argument does not conform to the MLNFeature protocol.", feature];
|
[NSException raise:NSInvalidArgumentException
|
||||||
|
format:@"The object %@ included in the features argument does not conform to the "
|
||||||
|
@"MLNFeature protocol.",
|
||||||
|
feature];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MLNShapeCollectionFeature *shapeCollectionFeature = [MLNShapeCollectionFeature shapeCollectionWithShapes:features];
|
MLNShapeCollectionFeature *shapeCollectionFeature =
|
||||||
|
[MLNShapeCollectionFeature shapeCollectionWithShapes:features];
|
||||||
return [self initWithIdentifier:identifier shape:shapeCollectionFeature options:options];
|
return [self initWithIdentifier:identifier shape:shapeCollectionFeature options:options];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NSArray<MLNShape *> *)shapes options:(nullable NSDictionary<MLNShapeSourceOption, id> *)options {
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
|
shapes:(NSArray<MLNShape *> *)shapes
|
||||||
|
options:(nullable NSDictionary<MLNShapeSourceOption, id> *)options {
|
||||||
MLNShapeCollection *shapeCollection = [MLNShapeCollection shapeCollectionWithShapes:shapes];
|
MLNShapeCollection *shapeCollection = [MLNShapeCollection shapeCollectionWithShapes:shapes];
|
||||||
return [self initWithIdentifier:identifier shape:shapeCollection options:options];
|
return [self initWithIdentifier:identifier shape:shapeCollection options:options];
|
||||||
}
|
}
|
||||||
@@ -238,22 +271,23 @@ mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(NSD
|
|||||||
|
|
||||||
- (void)setShape:(MLNShape *)shape {
|
- (void)setShape:(MLNShape *)shape {
|
||||||
MLNAssertStyleSourceIsValid();
|
MLNAssertStyleSourceIsValid();
|
||||||
self.rawSource->setGeoJSON({ shape.geoJSONObject });
|
self.rawSource->setGeoJSON({shape.geoJSONObject});
|
||||||
_shape = shape;
|
_shape = shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description {
|
- (NSString *)description {
|
||||||
if (self.rawSource) {
|
if (self.rawSource) {
|
||||||
return [NSString stringWithFormat:@"<%@: %p; identifier = %@; URL = %@; shape = %@>",
|
return [NSString stringWithFormat:@"<%@: %p; identifier = %@; URL = %@; shape = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self, self.identifier, self.URL, self.shape];
|
NSStringFromClass([self class]), (void *)self,
|
||||||
}
|
self.identifier, self.URL, self.shape];
|
||||||
else {
|
} else {
|
||||||
return [NSString stringWithFormat:@"<%@: %p; identifier = %@; URL = <unknown>; shape = %@>",
|
return [NSString stringWithFormat:@"<%@: %p; identifier = %@; URL = <unknown>; shape = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self, self.identifier, self.shape];
|
NSStringFromClass([self class]), (void *)self,
|
||||||
|
self.identifier, self.shape];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray<id <MLNFeature>> *)featuresMatchingPredicate:(nullable NSPredicate *)predicate {
|
- (NSArray<id<MLNFeature>> *)featuresMatchingPredicate:(nullable NSPredicate *)predicate {
|
||||||
MLNAssertStyleSourceIsValid();
|
MLNAssertStyleSourceIsValid();
|
||||||
std::optional<mbgl::style::Filter> optionalFilter;
|
std::optional<mbgl::style::Filter> optionalFilter;
|
||||||
if (predicate) {
|
if (predicate) {
|
||||||
@@ -263,14 +297,17 @@ mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(NSD
|
|||||||
std::vector<mbgl::Feature> features;
|
std::vector<mbgl::Feature> features;
|
||||||
if ([self.stylable isKindOfClass:[MLNMapView class]]) {
|
if ([self.stylable isKindOfClass:[MLNMapView class]]) {
|
||||||
MLNMapView *mapView = (MLNMapView *)self.stylable;
|
MLNMapView *mapView = (MLNMapView *)self.stylable;
|
||||||
features = mapView.renderer->querySourceFeatures(self.rawSource->getID(), { {}, optionalFilter });
|
features = mapView.renderer->querySourceFeatures(self.rawSource->getID(), {{}, optionalFilter});
|
||||||
}
|
}
|
||||||
return MLNFeaturesFromMBGLFeatures(features);
|
return MLNFeaturesFromMBGLFeatures(features);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MLNCluster management
|
// MARK: - MLNCluster management
|
||||||
|
|
||||||
- (std::optional<mbgl::FeatureExtensionValue>)featureExtensionValueOfCluster:(MLNShape<MLNCluster> *)cluster extension:(std::string)extension options:(const std::map<std::string, mbgl::Value>)options {
|
- (std::optional<mbgl::FeatureExtensionValue>)
|
||||||
|
featureExtensionValueOfCluster:(MLNShape<MLNCluster> *)cluster
|
||||||
|
extension:(std::string)extension
|
||||||
|
options:(const std::map<std::string, mbgl::Value>)options {
|
||||||
MLNAssertStyleSourceIsValid();
|
MLNAssertStyleSourceIsValid();
|
||||||
std::optional<mbgl::FeatureExtensionValue> extensionValue;
|
std::optional<mbgl::FeatureExtensionValue> extensionValue;
|
||||||
|
|
||||||
@@ -290,22 +327,21 @@ mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(NSD
|
|||||||
|
|
||||||
if ([self.stylable isKindOfClass:[MLNMapView class]]) {
|
if ([self.stylable isKindOfClass:[MLNMapView class]]) {
|
||||||
MLNMapView *mapView = (MLNMapView *)self.stylable;
|
MLNMapView *mapView = (MLNMapView *)self.stylable;
|
||||||
extensionValue = mapView.renderer->queryFeatureExtensions(self.rawSource->getID(),
|
extensionValue = mapView.renderer->queryFeatureExtensions(
|
||||||
clusterFeature,
|
self.rawSource->getID(), clusterFeature, "supercluster", extension, options);
|
||||||
"supercluster",
|
|
||||||
extension,
|
|
||||||
options);
|
|
||||||
}
|
}
|
||||||
return extensionValue;
|
return extensionValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray<id <MLNFeature>> *)leavesOfCluster:(MLNPointFeatureCluster *)cluster offset:(NSUInteger)offset limit:(NSUInteger)limit {
|
- (NSArray<id<MLNFeature>> *)leavesOfCluster:(MLNPointFeatureCluster *)cluster
|
||||||
const std::map<std::string, mbgl::Value> options = {
|
offset:(NSUInteger)offset
|
||||||
{ "limit", static_cast<uint64_t>(limit) },
|
limit:(NSUInteger)limit {
|
||||||
{ "offset", static_cast<uint64_t>(offset) }
|
const std::map<std::string, mbgl::Value> options = {{"limit", static_cast<uint64_t>(limit)},
|
||||||
};
|
{"offset", static_cast<uint64_t>(offset)}};
|
||||||
|
|
||||||
auto featureExtension = [self featureExtensionValueOfCluster:cluster extension:"leaves" options:options];
|
auto featureExtension = [self featureExtensionValueOfCluster:cluster
|
||||||
|
extension:"leaves"
|
||||||
|
options:options];
|
||||||
|
|
||||||
if (!featureExtension) {
|
if (!featureExtension) {
|
||||||
return @[];
|
return @[];
|
||||||
@@ -319,8 +355,10 @@ mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(NSD
|
|||||||
return MLNFeaturesFromMBGLFeatures(leaves);
|
return MLNFeaturesFromMBGLFeatures(leaves);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray<id <MLNFeature>> *)childrenOfCluster:(MLNPointFeatureCluster *)cluster {
|
- (NSArray<id<MLNFeature>> *)childrenOfCluster:(MLNPointFeatureCluster *)cluster {
|
||||||
auto featureExtension = [self featureExtensionValueOfCluster:cluster extension:"children" options:{}];
|
auto featureExtension = [self featureExtensionValueOfCluster:cluster
|
||||||
|
extension:"children"
|
||||||
|
options:{}];
|
||||||
|
|
||||||
if (!featureExtension) {
|
if (!featureExtension) {
|
||||||
return @[];
|
return @[];
|
||||||
@@ -335,7 +373,9 @@ mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(NSD
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (double)zoomLevelForExpandingCluster:(MLNPointFeatureCluster *)cluster {
|
- (double)zoomLevelForExpandingCluster:(MLNPointFeatureCluster *)cluster {
|
||||||
auto featureExtension = [self featureExtensionValueOfCluster:cluster extension:"expansion-zoom" options:{}];
|
auto featureExtension = [self featureExtensionValueOfCluster:cluster
|
||||||
|
extension:"expansion-zoom"
|
||||||
|
options:{}];
|
||||||
|
|
||||||
if (!featureExtension) {
|
if (!featureExtension) {
|
||||||
return -1.0;
|
return -1.0;
|
||||||
@@ -354,11 +394,12 @@ mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(NSD
|
|||||||
return -1.0;
|
return -1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)debugRecursiveLogForFeature:(id <MLNFeature>)feature indent:(NSUInteger)indent {
|
- (void)debugRecursiveLogForFeature:(id<MLNFeature>)feature indent:(NSUInteger)indent {
|
||||||
NSString *description = feature.description;
|
NSString *description = feature.description;
|
||||||
|
|
||||||
// Want our recursive log on a single line
|
// Want our recursive log on a single line
|
||||||
NSString *log = [description stringByReplacingOccurrencesOfString:@"\\s+"
|
NSString *log =
|
||||||
|
[description stringByReplacingOccurrencesOfString:@"\\s+"
|
||||||
withString:@" "
|
withString:@" "
|
||||||
options:NSRegularExpressionSearch
|
options:NSRegularExpressionSearch
|
||||||
range:NSMakeRange(0, description.length)];
|
range:NSMakeRange(0, description.length)];
|
||||||
@@ -368,7 +409,7 @@ mbgl::Immutable<mbgl::style::GeoJSONOptions> MLNGeoJSONOptionsFromDictionary(NSD
|
|||||||
MLNPointFeatureCluster *cluster = MLN_OBJC_DYNAMIC_CAST(feature, MLNPointFeatureCluster);
|
MLNPointFeatureCluster *cluster = MLN_OBJC_DYNAMIC_CAST(feature, MLNPointFeatureCluster);
|
||||||
|
|
||||||
if (cluster) {
|
if (cluster) {
|
||||||
for (id <MLNFeature> child in [self childrenOfCluster:cluster]) {
|
for (id<MLNFeature> child in [self childrenOfCluster:cluster]) {
|
||||||
[self debugRecursiveLogForFeature:child indent:indent + 4];
|
[self debugRecursiveLogForFeature:child indent:indent + 4];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
|
#import "MLNMapView_Private.h"
|
||||||
#import "MLNSource_Private.h"
|
#import "MLNSource_Private.h"
|
||||||
#import "MLNStyle_Private.h"
|
#import "MLNStyle_Private.h"
|
||||||
#import "MLNMapView_Private.h"
|
|
||||||
#import "NSBundle+MLNAdditions.h"
|
#import "NSBundle+MLNAdditions.h"
|
||||||
|
|
||||||
#include <mbgl/style/style.hpp>
|
|
||||||
#include <mbgl/map/map.hpp>
|
#include <mbgl/map/map.hpp>
|
||||||
#include <mbgl/style/source.hpp>
|
#include <mbgl/style/source.hpp>
|
||||||
|
#include <mbgl/style/style.hpp>
|
||||||
|
|
||||||
const MLNExceptionName MLNInvalidStyleSourceException = @"MLNInvalidStyleSourceException";
|
const MLNExceptionName MLNInvalidStyleSourceException = @"MLNInvalidStyleSourceException";
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ const MLNExceptionName MLNInvalidStyleSourceException = @"MLNInvalidStyleSourceE
|
|||||||
// special internal source types like mbgl::AnnotationSource.
|
// special internal source types like mbgl::AnnotationSource.
|
||||||
@property (nonatomic, readonly) mbgl::style::Source *rawSource;
|
@property (nonatomic, readonly) mbgl::style::Source *rawSource;
|
||||||
|
|
||||||
@property (nonatomic, readonly, weak) id <MLNStylable> stylable;
|
@property (nonatomic, readonly, weak) id<MLNStylable> stylable;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -24,27 +24,25 @@ const MLNExceptionName MLNInvalidStyleSourceException = @"MLNInvalidStyleSourceE
|
|||||||
mapbox::base::WeakPtr<mbgl::style::Source> _weakSource;
|
mapbox::base::WeakPtr<mbgl::style::Source> _weakSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier
|
|
||||||
{
|
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
_identifier = [identifier copy];
|
_identifier = [identifier copy];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource stylable:(id <MLNStylable>)stylable {
|
- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource
|
||||||
|
stylable:(id<MLNStylable>)stylable {
|
||||||
NSString *identifier = @(rawSource->getID().c_str());
|
NSString *identifier = @(rawSource->getID().c_str());
|
||||||
if (self = [self initWithIdentifier:identifier]) {
|
if (self = [self initWithIdentifier:identifier]) {
|
||||||
_weakSource = rawSource->makeWeakPtr();
|
_weakSource = rawSource->makeWeakPtr();
|
||||||
rawSource->peer = SourceWrapper { self };
|
rawSource->peer = SourceWrapper{self};
|
||||||
_stylable = stylable;
|
_stylable = stylable;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::style::Source *)rawSource
|
- (mbgl::style::Source *)rawSource {
|
||||||
{
|
|
||||||
return _weakSource.get();
|
return _weakSource.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,23 +53,24 @@ const MLNExceptionName MLNInvalidStyleSourceException = @"MLNInvalidStyleSourceE
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addToStylable:(id <MLNStylable>)stylable {
|
- (void)addToStylable:(id<MLNStylable>)stylable {
|
||||||
if (_pendingSource == nullptr) {
|
if (_pendingSource == nullptr) {
|
||||||
[NSException raise:MLNRedundantSourceException
|
[NSException raise:MLNRedundantSourceException
|
||||||
format:@"This instance %@ was already added to %@. Adding the same source instance " \
|
format:@"This instance %@ was already added to %@. Adding the same source instance "
|
||||||
"to the style more than once is invalid.", self, stylable.style];
|
"to the style more than once is invalid.",
|
||||||
|
self, stylable.style];
|
||||||
}
|
}
|
||||||
|
|
||||||
_stylable = stylable;
|
_stylable = stylable;
|
||||||
_stylable.style.rawStyle->addSource(std::move(_pendingSource));
|
_stylable.style.rawStyle->addSource(std::move(_pendingSource));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)removeFromStylable:(id <MLNStylable>)mapView error:(NSError * __nullable * __nullable)outError {
|
- (BOOL)removeFromStylable:(id<MLNStylable>)mapView
|
||||||
|
error:(NSError *__nullable *__nullable)outError {
|
||||||
MLNAssertStyleSourceIsValid();
|
MLNAssertStyleSourceIsValid();
|
||||||
BOOL removed = NO;
|
BOOL removed = NO;
|
||||||
|
|
||||||
if (self.rawSource == mapView.style.rawStyle->getSource(self.identifier.UTF8String)) {
|
if (self.rawSource == mapView.style.rawStyle->getSource(self.identifier.UTF8String)) {
|
||||||
|
|
||||||
auto removedSource = mapView.style.rawStyle->removeSource(self.identifier.UTF8String);
|
auto removedSource = mapView.style.rawStyle->removeSource(self.identifier.UTF8String);
|
||||||
|
|
||||||
if (removedSource) {
|
if (removedSource) {
|
||||||
@@ -79,31 +78,39 @@ const MLNExceptionName MLNInvalidStyleSourceException = @"MLNInvalidStyleSourceE
|
|||||||
_pendingSource = std::move(removedSource);
|
_pendingSource = std::move(removedSource);
|
||||||
_stylable = nil;
|
_stylable = nil;
|
||||||
} else if (outError) {
|
} else if (outError) {
|
||||||
NSString *localizedDescription = [NSString stringWithFormat:
|
NSString *localizedDescription = [NSString
|
||||||
NSLocalizedStringWithDefaultValue(@"REMOVE_SRC_FAIL_IN_USE_FMT", @"Foundation", nil, @"The source “%@” can’t be removed while it is in use.", @"User-friendly error description; first placeholder is the source’s identifier"),
|
stringWithFormat:
|
||||||
|
NSLocalizedStringWithDefaultValue(
|
||||||
|
@"REMOVE_SRC_FAIL_IN_USE_FMT", @"Foundation", nil,
|
||||||
|
@"The source “%@” can’t be removed while it is in use.",
|
||||||
|
@"User-friendly error description; first placeholder is the source’s identifier"),
|
||||||
self.identifier];
|
self.identifier];
|
||||||
|
|
||||||
*outError = [NSError errorWithDomain:MLNErrorDomain
|
*outError = [NSError errorWithDomain:MLNErrorDomain
|
||||||
code:MLNErrorCodeSourceIsInUseCannotRemove
|
code:MLNErrorCodeSourceIsInUseCannotRemove
|
||||||
userInfo:@{ NSLocalizedDescriptionKey : localizedDescription }];
|
userInfo:@{NSLocalizedDescriptionKey : localizedDescription}];
|
||||||
}
|
}
|
||||||
} else if (outError) {
|
} else if (outError) {
|
||||||
// TODO: Consider raising an exception here
|
// TODO: Consider raising an exception here
|
||||||
NSString *localizedDescription = [NSString stringWithFormat:
|
NSString *localizedDescription =
|
||||||
NSLocalizedStringWithDefaultValue(@"REMOVE_SRC_FAIL_MISMATCH_FMT", @"Foundation", nil, @"The source can’t be removed because its identifier, “%@”, belongs to a different source in this style.", @"User-friendly error description"),
|
[NSString stringWithFormat:NSLocalizedStringWithDefaultValue(
|
||||||
|
@"REMOVE_SRC_FAIL_MISMATCH_FMT", @"Foundation", nil,
|
||||||
|
@"The source can’t be removed because its identifier, “%@”, "
|
||||||
|
@"belongs to a different source in this style.",
|
||||||
|
@"User-friendly error description"),
|
||||||
self.identifier];
|
self.identifier];
|
||||||
|
|
||||||
*outError = [NSError errorWithDomain:MLNErrorDomain
|
*outError = [NSError errorWithDomain:MLNErrorDomain
|
||||||
code:MLNErrorCodeSourceIdentifierMismatch
|
code:MLNErrorCodeSourceIdentifierMismatch
|
||||||
userInfo:@{ NSLocalizedDescriptionKey : localizedDescription }];
|
userInfo:@{NSLocalizedDescriptionKey : localizedDescription}];
|
||||||
}
|
}
|
||||||
|
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description {
|
- (NSString *)description {
|
||||||
return [NSString stringWithFormat:@"<%@: %p; identifier = %@>",
|
return [NSString stringWithFormat:@"<%@: %p; identifier = %@>", NSStringFromClass([self class]),
|
||||||
NSStringFromClass([self class]), (void *)self, self.identifier];
|
(void *)self, self.identifier];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,72 +1,75 @@
|
|||||||
#import "MLNStyle_Private.h"
|
#import "MLNStyle_Private.h"
|
||||||
|
|
||||||
#import "MLNMapView_Private.h"
|
#import "MLNBackgroundStyleLayer.h"
|
||||||
#import "MLNStyleLayer.h"
|
|
||||||
#import "MLNStyleLayer_Private.h"
|
|
||||||
#import "MLNFillStyleLayer.h"
|
|
||||||
#import "MLNFillExtrusionStyleLayer.h"
|
|
||||||
#import "MLNLineStyleLayer.h"
|
|
||||||
#import "MLNCircleStyleLayer.h"
|
#import "MLNCircleStyleLayer.h"
|
||||||
#import "MLNSymbolStyleLayer.h"
|
#import "MLNFillExtrusionStyleLayer.h"
|
||||||
|
#import "MLNFillStyleLayer.h"
|
||||||
#import "MLNHeatmapStyleLayer.h"
|
#import "MLNHeatmapStyleLayer.h"
|
||||||
#import "MLNHillshadeStyleLayer.h"
|
#import "MLNHillshadeStyleLayer.h"
|
||||||
|
#import "MLNLineStyleLayer.h"
|
||||||
|
#import "MLNMapView_Private.h"
|
||||||
#import "MLNRasterStyleLayer.h"
|
#import "MLNRasterStyleLayer.h"
|
||||||
#import "MLNBackgroundStyleLayer.h"
|
#import "MLNStyleLayer.h"
|
||||||
#import "MLNStyleLayerManager.h"
|
#import "MLNStyleLayerManager.h"
|
||||||
|
#import "MLNStyleLayer_Private.h"
|
||||||
|
#import "MLNSymbolStyleLayer.h"
|
||||||
|
|
||||||
|
#import "MLNImageSource.h"
|
||||||
|
#import "MLNLight_Private.h"
|
||||||
|
#import "MLNRasterDEMSource.h"
|
||||||
|
#import "MLNRasterTileSource.h"
|
||||||
|
#import "MLNShapeSource.h"
|
||||||
#import "MLNSource.h"
|
#import "MLNSource.h"
|
||||||
#import "MLNSource_Private.h"
|
#import "MLNSource_Private.h"
|
||||||
#import "MLNLight_Private.h"
|
|
||||||
#import "MLNTileSource_Private.h"
|
#import "MLNTileSource_Private.h"
|
||||||
#import "MLNVectorTileSource_Private.h"
|
#import "MLNVectorTileSource_Private.h"
|
||||||
#import "MLNRasterTileSource.h"
|
|
||||||
#import "MLNRasterDEMSource.h"
|
|
||||||
#import "MLNShapeSource.h"
|
|
||||||
#import "MLNImageSource.h"
|
|
||||||
|
|
||||||
#import "MLNAttributionInfo_Private.h"
|
#import "MLNAttributionInfo_Private.h"
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
|
||||||
#include <mbgl/map/map.hpp>
|
#include <mbgl/map/map.hpp>
|
||||||
#include <mbgl/style/style.hpp>
|
|
||||||
#include <mbgl/style/image.hpp>
|
#include <mbgl/style/image.hpp>
|
||||||
#include <mbgl/style/light.hpp>
|
#include <mbgl/style/light.hpp>
|
||||||
#include <mbgl/style/sources/geojson_source.hpp>
|
#include <mbgl/style/sources/geojson_source.hpp>
|
||||||
#include <mbgl/style/sources/vector_source.hpp>
|
|
||||||
#include <mbgl/style/sources/raster_source.hpp>
|
|
||||||
#include <mbgl/style/sources/raster_dem_source.hpp>
|
|
||||||
#include <mbgl/style/sources/image_source.hpp>
|
#include <mbgl/style/sources/image_source.hpp>
|
||||||
|
#include <mbgl/style/sources/raster_dem_source.hpp>
|
||||||
|
#include <mbgl/style/sources/raster_source.hpp>
|
||||||
|
#include <mbgl/style/sources/vector_source.hpp>
|
||||||
|
#include <mbgl/style/style.hpp>
|
||||||
|
|
||||||
#import "NSDate+MLNAdditions.h"
|
#import "NSDate+MLNAdditions.h"
|
||||||
|
|
||||||
#import "MLNCustomStyleLayer.h"
|
#import "MLNCustomStyleLayer.h"
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
#import "UIImage+MLNAdditions.h"
|
#import "UIImage+MLNAdditions.h"
|
||||||
#else
|
#else
|
||||||
#import "NSImage+MLNAdditions.h"
|
#import "NSImage+MLNAdditions.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const MLNExceptionName MLNInvalidStyleURLException = @"MLNInvalidStyleURLException";
|
const MLNExceptionName MLNInvalidStyleURLException = @"MLNInvalidStyleURLException";
|
||||||
const MLNExceptionName MLNRedundantLayerException = @"MLNRedundantLayerException";
|
const MLNExceptionName MLNRedundantLayerException = @"MLNRedundantLayerException";
|
||||||
const MLNExceptionName MLNRedundantLayerIdentifierException = @"MLNRedundantLayerIdentifierException";
|
const MLNExceptionName MLNRedundantLayerIdentifierException =
|
||||||
|
@"MLNRedundantLayerIdentifierException";
|
||||||
const MLNExceptionName MLNRedundantSourceException = @"MLNRedundantSourceException";
|
const MLNExceptionName MLNRedundantSourceException = @"MLNRedundantSourceException";
|
||||||
const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSourceIdentifierException";
|
const MLNExceptionName MLNRedundantSourceIdentifierException =
|
||||||
|
@"MLNRedundantSourceIdentifierException";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Model class for localization changes.
|
Model class for localization changes.
|
||||||
*/
|
*/
|
||||||
@interface MLNTextLanguage: NSObject
|
@interface MLNTextLanguage : NSObject
|
||||||
@property (strong, nonatomic) NSString *originalTextField;
|
@property (strong, nonatomic) NSString *originalTextField;
|
||||||
@property (strong, nonatomic) NSString *updatedTextField;
|
@property (strong, nonatomic) NSString *updatedTextField;
|
||||||
|
|
||||||
- (instancetype)initWithTextLanguage:(NSString *)originalTextField updatedTextField:(NSString *)updatedTextField;
|
- (instancetype)initWithTextLanguage:(NSString *)originalTextField
|
||||||
|
updatedTextField:(NSString *)updatedTextField;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MLNTextLanguage
|
@implementation MLNTextLanguage
|
||||||
- (instancetype)initWithTextLanguage:(NSString *)originalTextField updatedTextField:(NSString *)updatedTextField
|
- (instancetype)initWithTextLanguage:(NSString *)originalTextField
|
||||||
{
|
updatedTextField:(NSString *)updatedTextField {
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
_originalTextField = originalTextField;
|
_originalTextField = originalTextField;
|
||||||
_updatedTextField = updatedTextField;
|
_updatedTextField = updatedTextField;
|
||||||
@@ -75,13 +78,15 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MLNStyle()
|
@interface MLNStyle ()
|
||||||
|
|
||||||
@property (nonatomic, readonly, weak) id <MLNStylable> stylable;
|
@property (nonatomic, readonly, weak) id<MLNStylable> stylable;
|
||||||
@property (nonatomic, readonly) mbgl::style::Style *rawStyle;
|
@property (nonatomic, readonly) mbgl::style::Style *rawStyle;
|
||||||
@property (readonly, copy, nullable) NSURL *URL;
|
@property (readonly, copy, nullable) NSURL *URL;
|
||||||
@property (nonatomic, readwrite, strong) NSMutableDictionary<NSString *, MLNCustomStyleLayer *> *customLayers;
|
@property (nonatomic, readwrite, strong)
|
||||||
@property (nonatomic) NSMutableDictionary<NSString *, NSDictionary<NSObject *, MLNTextLanguage *> *> *localizedLayersByIdentifier;
|
NSMutableDictionary<NSString *, MLNCustomStyleLayer *> *customLayers;
|
||||||
|
@property (nonatomic) NSMutableDictionary<NSString *, NSDictionary<NSObject *, MLNTextLanguage *> *>
|
||||||
|
*localizedLayersByIdentifier;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -89,26 +94,26 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
|
|
||||||
// MARK: Predefined style URLs
|
// MARK: Predefined style URLs
|
||||||
|
|
||||||
+ (NSArray<MLNDefaultStyle*>*) predefinedStyles {
|
+ (NSArray<MLNDefaultStyle *> *)predefinedStyles {
|
||||||
return MLNSettings.tileServerOptions.defaultStyles;
|
return MLNSettings.tileServerOptions.defaultStyles;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (MLNDefaultStyle*) defaultStyle {
|
+ (MLNDefaultStyle *)defaultStyle {
|
||||||
MLNTileServerOptions* opts = MLNSettings.tileServerOptions;
|
MLNTileServerOptions *opts = MLNSettings.tileServerOptions;
|
||||||
return opts.defaultStyle;
|
return opts.defaultStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSURL*) defaultStyleURL {
|
+ (NSURL *)defaultStyleURL {
|
||||||
MLNDefaultStyle* styleDefinition = [MLNStyle defaultStyle];
|
MLNDefaultStyle *styleDefinition = [MLNStyle defaultStyle];
|
||||||
if (styleDefinition != nil){
|
if (styleDefinition != nil) {
|
||||||
return styleDefinition.url;
|
return styleDefinition.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (MLNDefaultStyle*) predefinedStyle:(NSString*)withStyleName {
|
+ (MLNDefaultStyle *)predefinedStyle:(NSString *)withStyleName {
|
||||||
for (MLNDefaultStyle* style in MLNSettings.tileServerOptions.defaultStyles) {
|
for (MLNDefaultStyle *style in MLNSettings.tileServerOptions.defaultStyles) {
|
||||||
if ([style.name isEqualToString:withStyleName]) {
|
if ([style.name isEqualToString:withStyleName]) {
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
@@ -118,7 +123,7 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
|
|
||||||
// MARK: -
|
// MARK: -
|
||||||
|
|
||||||
- (instancetype)initWithRawStyle:(mbgl::style::Style *)rawStyle stylable:(id <MLNStylable>)stylable {
|
- (instancetype)initWithRawStyle:(mbgl::style::Style *)rawStyle stylable:(id<MLNStylable>)stylable {
|
||||||
MLNLogInfo(@"Initializing %@ with stylable: %@", NSStringFromClass([self class]), stylable);
|
MLNLogInfo(@"Initializing %@ with stylable: %@", NSStringFromClass([self class]), stylable);
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
_stylable = stylable;
|
_stylable = stylable;
|
||||||
@@ -179,8 +184,7 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
return [self sourceWithIdentifier:object.identifier];
|
return [self sourceWithIdentifier:object.identifier];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MLNSource *)sourceWithIdentifier:(NSString *)identifier
|
- (MLNSource *)sourceWithIdentifier:(NSString *)identifier {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Querying source with identifier: %@", identifier);
|
MLNLogDebug(@"Querying source with identifier: %@", identifier);
|
||||||
auto rawSource = self.rawStyle->getSource(identifier.UTF8String);
|
auto rawSource = self.rawStyle->getSource(identifier.UTF8String);
|
||||||
|
|
||||||
@@ -188,7 +192,8 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (MLNSource *)sourceFromMBGLSource:(mbgl::style::Source *)rawSource {
|
- (MLNSource *)sourceFromMBGLSource:(mbgl::style::Source *)rawSource {
|
||||||
if (MLNSource *source = rawSource->peer.has_value() ? rawSource->peer.get<SourceWrapper>().source : nil) {
|
if (MLNSource *source =
|
||||||
|
rawSource->peer.has_value() ? rawSource->peer.get<SourceWrapper>().source : nil) {
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,33 +214,33 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addSource:(MLNSource *)source
|
- (void)addSource:(MLNSource *)source {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Adding source: %@", source);
|
MLNLogDebug(@"Adding source: %@", source);
|
||||||
if (!source.rawSource) {
|
if (!source.rawSource) {
|
||||||
[NSException raise:NSInvalidArgumentException format:
|
[NSException
|
||||||
@"The source %@ cannot be added to the style. "
|
raise:NSInvalidArgumentException
|
||||||
|
format:@"The source %@ cannot be added to the style. "
|
||||||
@"Make sure the source was created as a member of a concrete subclass of MLNSource.",
|
@"Make sure the source was created as a member of a concrete subclass of MLNSource.",
|
||||||
source];
|
source];
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
[source addToStylable:self.stylable];
|
[source addToStylable:self.stylable];
|
||||||
} catch (std::runtime_error & err) {
|
} catch (std::runtime_error &err) {
|
||||||
[NSException raise:MLNRedundantSourceIdentifierException format:@"%s", err.what()];
|
[NSException raise:MLNRedundantSourceIdentifierException format:@"%s", err.what()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeSource:(MLNSource *)source
|
- (void)removeSource:(MLNSource *)source {
|
||||||
{
|
|
||||||
[self removeSource:source error:nil];
|
[self removeSource:source error:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)removeSource:(MLNSource *)source error:(NSError * __nullable * __nullable)outError {
|
- (BOOL)removeSource:(MLNSource *)source error:(NSError *__nullable *__nullable)outError {
|
||||||
MLNLogDebug(@"Removing source: %@", source);
|
MLNLogDebug(@"Removing source: %@", source);
|
||||||
|
|
||||||
if (!source.rawSource) {
|
if (!source.rawSource) {
|
||||||
NSString *errorMessage = [NSString stringWithFormat:
|
NSString *errorMessage = [NSString
|
||||||
|
stringWithFormat:
|
||||||
@"The source %@ cannot be removed from the style. "
|
@"The source %@ cannot be removed from the style. "
|
||||||
@"Make sure the source was created as a member of a concrete subclass of MLNSource."
|
@"Make sure the source was created as a member of a concrete subclass of MLNSource."
|
||||||
@"Automatic re-addition of sources after style changes is not currently supported.",
|
@"Automatic re-addition of sources after style changes is not currently supported.",
|
||||||
@@ -244,10 +249,9 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
if (outError) {
|
if (outError) {
|
||||||
*outError = [NSError errorWithDomain:MLNErrorDomain
|
*outError = [NSError errorWithDomain:MLNErrorDomain
|
||||||
code:MLNErrorCodeSourceCannotBeRemovedFromStyle
|
code:MLNErrorCodeSourceCannotBeRemovedFromStyle
|
||||||
userInfo:@{ NSLocalizedDescriptionKey : errorMessage }];
|
userInfo:@{NSLocalizedDescriptionKey : errorMessage}];
|
||||||
return NO;
|
return NO;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
[NSException raise:NSInvalidArgumentException format:@"%@", errorMessage];
|
[NSException raise:NSInvalidArgumentException format:@"%@", errorMessage];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,8 +259,9 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
return [source removeFromStylable:self.stylable error:outError];
|
return [source removeFromStylable:self.stylable error:outError];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (nullable NSArray<MLNAttributionInfo *> *)attributionInfosWithFontSize:(CGFloat)fontSize
|
||||||
- (nullable NSArray<MLNAttributionInfo *> *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MLNColor *)linkColor {
|
linkColor:
|
||||||
|
(nullable MLNColor *)linkColor {
|
||||||
// It’d be incredibly convenient to use -sources here, but this operation
|
// It’d be incredibly convenient to use -sources here, but this operation
|
||||||
// depends on the sources being sorted in ascending order by creation, as
|
// depends on the sources being sorted in ascending order by creation, as
|
||||||
// with the std::vector used in mbgl.
|
// with the std::vector used in mbgl.
|
||||||
@@ -276,10 +281,10 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
|
|
||||||
// MARK: Style layers
|
// MARK: Style layers
|
||||||
|
|
||||||
- (NSArray<__kindof MLNStyleLayer *> *)layers
|
- (NSArray<__kindof MLNStyleLayer *> *)layers {
|
||||||
{
|
|
||||||
auto layers = self.rawStyle->getLayers();
|
auto layers = self.rawStyle->getLayers();
|
||||||
NSMutableArray<__kindof MLNStyleLayer *> *styleLayers = [NSMutableArray arrayWithCapacity:layers.size()];
|
NSMutableArray<__kindof MLNStyleLayer *> *styleLayers =
|
||||||
|
[NSMutableArray arrayWithCapacity:layers.size()];
|
||||||
for (auto layer : layers) {
|
for (auto layer : layers) {
|
||||||
MLNStyleLayer *styleLayer = [self layerFromMBGLLayer:layer];
|
MLNStyleLayer *styleLayer = [self layerFromMBGLLayer:layer];
|
||||||
[styleLayers addObject:styleLayer];
|
[styleLayers addObject:styleLayer];
|
||||||
@@ -297,13 +302,11 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)countOfLayers
|
- (NSUInteger)countOfLayers {
|
||||||
{
|
|
||||||
return self.rawStyle->getLayers().size();
|
return self.rawStyle->getLayers().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MLNStyleLayer *)objectInLayersAtIndex:(NSUInteger)index
|
- (MLNStyleLayer *)objectInLayersAtIndex:(NSUInteger)index {
|
||||||
{
|
|
||||||
auto layers = self.rawStyle->getLayers();
|
auto layers = self.rawStyle->getLayers();
|
||||||
if (index >= layers.size()) {
|
if (index >= layers.size()) {
|
||||||
[NSException raise:NSRangeException
|
[NSException raise:NSRangeException
|
||||||
@@ -314,8 +317,7 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
return [self layerFromMBGLLayer:layer];
|
return [self layerFromMBGLLayer:layer];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)getLayers:(MLNStyleLayer **)buffer range:(NSRange)inRange
|
- (void)getLayers:(MLNStyleLayer **)buffer range:(NSRange)inRange {
|
||||||
{
|
|
||||||
auto layers = self.rawStyle->getLayers();
|
auto layers = self.rawStyle->getLayers();
|
||||||
if (NSMaxRange(inRange) > layers.size()) {
|
if (NSMaxRange(inRange) > layers.size()) {
|
||||||
[NSException raise:NSRangeException
|
[NSException raise:NSRangeException
|
||||||
@@ -328,40 +330,41 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)insertObject:(MLNStyleLayer *)styleLayer inLayersAtIndex:(NSUInteger)index
|
- (void)insertObject:(MLNStyleLayer *)styleLayer inLayersAtIndex:(NSUInteger)index {
|
||||||
{
|
|
||||||
if (!styleLayer.rawLayer) {
|
if (!styleLayer.rawLayer) {
|
||||||
[NSException raise:NSInvalidArgumentException format:
|
[NSException raise:NSInvalidArgumentException
|
||||||
@"The style layer %@ cannot be inserted into the style. "
|
format:@"The style layer %@ cannot be inserted into the style. "
|
||||||
@"Make sure the style layer was created as a member of a concrete subclass of MLNStyleLayer.",
|
@"Make sure the style layer was created as a member of a concrete subclass "
|
||||||
|
@"of MLNStyleLayer.",
|
||||||
styleLayer];
|
styleLayer];
|
||||||
}
|
}
|
||||||
auto layers = self.rawStyle->getLayers();
|
auto layers = self.rawStyle->getLayers();
|
||||||
if (index > layers.size()) {
|
if (index > layers.size()) {
|
||||||
[NSException raise:NSRangeException
|
[NSException
|
||||||
|
raise:NSRangeException
|
||||||
format:@"Cannot insert style layer at out-of-bounds index %lu.", (unsigned long)index];
|
format:@"Cannot insert style layer at out-of-bounds index %lu.", (unsigned long)index];
|
||||||
} else if (index == 0) {
|
} else if (index == 0) {
|
||||||
try {
|
try {
|
||||||
MLNStyleLayer *sibling = layers.size() ? [self layerFromMBGLLayer:layers.at(0)] : nil;
|
MLNStyleLayer *sibling = layers.size() ? [self layerFromMBGLLayer:layers.at(0)] : nil;
|
||||||
[styleLayer addToStyle:self belowLayer:sibling];
|
[styleLayer addToStyle:self belowLayer:sibling];
|
||||||
} catch (const std::runtime_error & err) {
|
} catch (const std::runtime_error &err) {
|
||||||
[NSException raise:MLNRedundantLayerIdentifierException format:@"%s", err.what()];
|
[NSException raise:MLNRedundantLayerIdentifierException format:@"%s", err.what()];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
MLNStyleLayer *sibling = [self layerFromMBGLLayer:layers.at(index)];
|
MLNStyleLayer *sibling = [self layerFromMBGLLayer:layers.at(index)];
|
||||||
[styleLayer addToStyle:self belowLayer:sibling];
|
[styleLayer addToStyle:self belowLayer:sibling];
|
||||||
} catch (std::runtime_error & err) {
|
} catch (std::runtime_error &err) {
|
||||||
[NSException raise:MLNRedundantLayerIdentifierException format:@"%s", err.what()];
|
[NSException raise:MLNRedundantLayerIdentifierException format:@"%s", err.what()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeObjectFromLayersAtIndex:(NSUInteger)index
|
- (void)removeObjectFromLayersAtIndex:(NSUInteger)index {
|
||||||
{
|
|
||||||
auto layers = self.rawStyle->getLayers();
|
auto layers = self.rawStyle->getLayers();
|
||||||
if (index >= layers.size()) {
|
if (index >= layers.size()) {
|
||||||
[NSException raise:NSRangeException
|
[NSException
|
||||||
|
raise:NSRangeException
|
||||||
format:@"Cannot remove style layer at out-of-bounds index %lu.", (unsigned long)index];
|
format:@"Cannot remove style layer at out-of-bounds index %lu.", (unsigned long)index];
|
||||||
}
|
}
|
||||||
auto layer = layers.at(index);
|
auto layer = layers.at(index);
|
||||||
@@ -369,31 +372,30 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
[styleLayer removeFromStyle:self];
|
[styleLayer removeFromStyle:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MLNStyleLayer *)layerFromMBGLLayer:(mbgl::style::Layer *)rawLayer
|
- (MLNStyleLayer *)layerFromMBGLLayer:(mbgl::style::Layer *)rawLayer {
|
||||||
{
|
|
||||||
NSParameterAssert(rawLayer);
|
NSParameterAssert(rawLayer);
|
||||||
|
|
||||||
if (MLNStyleLayer *layer = rawLayer->peer.has_value() ? rawLayer->peer.get<LayerWrapper>().layer : nil) {
|
if (MLNStyleLayer *layer =
|
||||||
|
rawLayer->peer.has_value() ? rawLayer->peer.get<LayerWrapper>().layer : nil) {
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mbgl::LayerManagerDarwin::get()->createPeer(rawLayer);
|
return mbgl::LayerManagerDarwin::get()->createPeer(rawLayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MLNStyleLayer *)layerWithIdentifier:(NSString *)identifier
|
- (MLNStyleLayer *)layerWithIdentifier:(NSString *)identifier {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Querying layerWithIdentifier: %@", identifier);
|
MLNLogDebug(@"Querying layerWithIdentifier: %@", identifier);
|
||||||
auto mbglLayer = self.rawStyle->getLayer(identifier.UTF8String);
|
auto mbglLayer = self.rawStyle->getLayer(identifier.UTF8String);
|
||||||
return mbglLayer ? [self layerFromMBGLLayer:mbglLayer] : nil;
|
return mbglLayer ? [self layerFromMBGLLayer:mbglLayer] : nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeLayer:(MLNStyleLayer *)layer
|
- (void)removeLayer:(MLNStyleLayer *)layer {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Removing layer: %@", layer);
|
MLNLogDebug(@"Removing layer: %@", layer);
|
||||||
if (!layer.rawLayer) {
|
if (!layer.rawLayer) {
|
||||||
[NSException raise:NSInvalidArgumentException format:
|
[NSException raise:NSInvalidArgumentException
|
||||||
@"The style layer %@ cannot be removed from the style. "
|
format:@"The style layer %@ cannot be removed from the style. "
|
||||||
@"Make sure the style layer was created as a member of a concrete subclass of MLNStyleLayer.",
|
@"Make sure the style layer was created as a member of a concrete subclass "
|
||||||
|
@"of MLNStyleLayer.",
|
||||||
layer];
|
layer];
|
||||||
}
|
}
|
||||||
[self willChangeValueForKey:@"layers"];
|
[self willChangeValueForKey:@"layers"];
|
||||||
@@ -401,19 +403,19 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
[self didChangeValueForKey:@"layers"];
|
[self didChangeValueForKey:@"layers"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addLayer:(MLNStyleLayer *)layer
|
- (void)addLayer:(MLNStyleLayer *)layer {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Adding layer: %@", layer);
|
MLNLogDebug(@"Adding layer: %@", layer);
|
||||||
if (!layer.rawLayer) {
|
if (!layer.rawLayer) {
|
||||||
[NSException raise:NSInvalidArgumentException format:
|
[NSException raise:NSInvalidArgumentException
|
||||||
@"The style layer %@ cannot be added to the style. "
|
format:@"The style layer %@ cannot be added to the style. "
|
||||||
@"Make sure the style layer was created as a member of a concrete subclass of MLNStyleLayer.",
|
@"Make sure the style layer was created as a member of a concrete subclass "
|
||||||
|
@"of MLNStyleLayer.",
|
||||||
layer];
|
layer];
|
||||||
}
|
}
|
||||||
[self willChangeValueForKey:@"layers"];
|
[self willChangeValueForKey:@"layers"];
|
||||||
try {
|
try {
|
||||||
[layer addToStyle:self belowLayer:nil];
|
[layer addToStyle:self belowLayer:nil];
|
||||||
} catch (std::runtime_error & err) {
|
} catch (std::runtime_error &err) {
|
||||||
[NSException raise:MLNRedundantLayerIdentifierException format:@"%s", err.what()];
|
[NSException raise:MLNRedundantLayerIdentifierException format:@"%s", err.what()];
|
||||||
}
|
}
|
||||||
[self didChangeValueForKey:@"layers"];
|
[self didChangeValueForKey:@"layers"];
|
||||||
@@ -423,27 +425,25 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
[self insertObject:layer inLayersAtIndex:index];
|
[self insertObject:layer inLayersAtIndex:index];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)insertLayer:(MLNStyleLayer *)layer belowLayer:(MLNStyleLayer *)sibling
|
- (void)insertLayer:(MLNStyleLayer *)layer belowLayer:(MLNStyleLayer *)sibling {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Inseting layer: %@ belowLayer: %@", layer, sibling);
|
MLNLogDebug(@"Inseting layer: %@ belowLayer: %@", layer, sibling);
|
||||||
if (!layer.rawLayer) {
|
if (!layer.rawLayer) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:
|
format:@"The style layer %@ cannot be added to the style. "
|
||||||
@"The style layer %@ cannot be added to the style. "
|
@"Make sure the style layer was created as a member of a concrete subclass "
|
||||||
@"Make sure the style layer was created as a member of a concrete subclass of MLNStyleLayer.",
|
@"of MLNStyleLayer.",
|
||||||
layer];
|
layer];
|
||||||
}
|
}
|
||||||
if (!sibling.rawLayer) {
|
if (!sibling.rawLayer) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:
|
format:@"A style layer cannot be placed below %@ in the style. "
|
||||||
@"A style layer cannot be placed below %@ in the style. "
|
|
||||||
@"Make sure sibling was obtained using -[MLNStyle layerWithIdentifier:].",
|
@"Make sure sibling was obtained using -[MLNStyle layerWithIdentifier:].",
|
||||||
sibling];
|
sibling];
|
||||||
}
|
}
|
||||||
[self willChangeValueForKey:@"layers"];
|
[self willChangeValueForKey:@"layers"];
|
||||||
try {
|
try {
|
||||||
[layer addToStyle:self belowLayer:sibling];
|
[layer addToStyle:self belowLayer:sibling];
|
||||||
} catch (std::runtime_error & err) {
|
} catch (std::runtime_error &err) {
|
||||||
[NSException raise:MLNRedundantLayerIdentifierException format:@"%s", err.what()];
|
[NSException raise:MLNRedundantLayerIdentifierException format:@"%s", err.what()];
|
||||||
}
|
}
|
||||||
[self didChangeValueForKey:@"layers"];
|
[self didChangeValueForKey:@"layers"];
|
||||||
@@ -453,15 +453,14 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
MLNLogDebug(@"Inseting layer: %@ aboveLayer: %@", layer, sibling);
|
MLNLogDebug(@"Inseting layer: %@ aboveLayer: %@", layer, sibling);
|
||||||
if (!layer.rawLayer) {
|
if (!layer.rawLayer) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:
|
format:@"The style layer %@ cannot be added to the style. "
|
||||||
@"The style layer %@ cannot be added to the style. "
|
@"Make sure the style layer was created as a member of a concrete subclass "
|
||||||
@"Make sure the style layer was created as a member of a concrete subclass of MLNStyleLayer.",
|
@"of MLNStyleLayer.",
|
||||||
layer];
|
layer];
|
||||||
}
|
}
|
||||||
if (!sibling.rawLayer) {
|
if (!sibling.rawLayer) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:
|
format:@"A style layer cannot be placed above %@ in the style. "
|
||||||
@"A style layer cannot be placed above %@ in the style. "
|
|
||||||
@"Make sure sibling was obtained using -[MLNStyle layerWithIdentifier:].",
|
@"Make sure sibling was obtained using -[MLNStyle layerWithIdentifier:].",
|
||||||
sibling];
|
sibling];
|
||||||
}
|
}
|
||||||
@@ -479,21 +478,20 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
[self willChangeValueForKey:@"layers"];
|
[self willChangeValueForKey:@"layers"];
|
||||||
if (index + 1 > layers.size()) {
|
if (index + 1 > layers.size()) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:
|
format:@"A style layer cannot be placed above %@ in the style. "
|
||||||
@"A style layer cannot be placed above %@ in the style. "
|
|
||||||
@"Make sure sibling was obtained using -[MLNStyle layerWithIdentifier:].",
|
@"Make sure sibling was obtained using -[MLNStyle layerWithIdentifier:].",
|
||||||
sibling];
|
sibling];
|
||||||
} else if (index + 1 == layers.size()) {
|
} else if (index + 1 == layers.size()) {
|
||||||
try {
|
try {
|
||||||
[layer addToStyle:self belowLayer:nil];
|
[layer addToStyle:self belowLayer:nil];
|
||||||
} catch (std::runtime_error & err) {
|
} catch (std::runtime_error &err) {
|
||||||
[NSException raise:MLNRedundantLayerIdentifierException format:@"%s", err.what()];
|
[NSException raise:MLNRedundantLayerIdentifierException format:@"%s", err.what()];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
MLNStyleLayer *nextSibling = [self layerFromMBGLLayer:layers.at(index + 1)];
|
MLNStyleLayer *nextSibling = [self layerFromMBGLLayer:layers.at(index + 1)];
|
||||||
try {
|
try {
|
||||||
[layer addToStyle:self belowLayer:nextSibling];
|
[layer addToStyle:self belowLayer:nextSibling];
|
||||||
} catch (std::runtime_error & err) {
|
} catch (std::runtime_error &err) {
|
||||||
[NSException raise:MLNRedundantLayerIdentifierException format:@"%s", err.what()];
|
[NSException raise:MLNRedundantLayerIdentifierException format:@"%s", err.what()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -502,12 +500,10 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
|
|
||||||
// MARK: Style images
|
// MARK: Style images
|
||||||
|
|
||||||
- (void)setImage:(MLNImage *)image forName:(NSString *)name
|
- (void)setImage:(MLNImage *)image forName:(NSString *)name {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Setting image: %@ forName: %@", image, name);
|
MLNLogDebug(@"Setting image: %@ forName: %@", image, name);
|
||||||
if (!image) {
|
if (!image) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException format:@"Cannot assign nil image to “%@”.", name];
|
||||||
format:@"Cannot assign nil image to “%@”.", name];
|
|
||||||
}
|
}
|
||||||
if (!name) {
|
if (!name) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
@@ -517,23 +513,19 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
self.rawStyle->addImage([image mgl_styleImageWithIdentifier:name]);
|
self.rawStyle->addImage([image mgl_styleImageWithIdentifier:name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeImageForName:(NSString *)name
|
- (void)removeImageForName:(NSString *)name {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Removing imageForName: %@", name);
|
MLNLogDebug(@"Removing imageForName: %@", name);
|
||||||
if (!name) {
|
if (!name) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException format:@"Cannot remove image with nil name."];
|
||||||
format:@"Cannot remove image with nil name."];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.rawStyle->removeImage([name UTF8String]);
|
self.rawStyle->removeImage([name UTF8String]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MLNImage *)imageForName:(NSString *)name
|
- (MLNImage *)imageForName:(NSString *)name {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Querying imageForName: %@", name);
|
MLNLogDebug(@"Querying imageForName: %@", name);
|
||||||
if (!name) {
|
if (!name) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException format:@"Cannot get image with nil name."];
|
||||||
format:@"Cannot get image with nil name."];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto styleImage = self.rawStyle->getImage([name UTF8String]);
|
auto styleImage = self.rawStyle->getImage([name UTF8String]);
|
||||||
@@ -542,50 +534,45 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
|
|
||||||
// MARK: Style transitions
|
// MARK: Style transitions
|
||||||
|
|
||||||
- (void)setTransition:(MLNTransition)transition
|
- (void)setTransition:(MLNTransition)transition {
|
||||||
{
|
|
||||||
self.rawStyle->setTransitionOptions(MLNOptionsFromTransition(transition));
|
self.rawStyle->setTransitionOptions(MLNOptionsFromTransition(transition));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MLNTransition)transition
|
- (MLNTransition)transition {
|
||||||
{
|
|
||||||
const mbgl::style::TransitionOptions transitionOptions = self.rawStyle->getTransitionOptions();
|
const mbgl::style::TransitionOptions transitionOptions = self.rawStyle->getTransitionOptions();
|
||||||
|
|
||||||
return MLNTransitionFromOptions(transitionOptions);
|
return MLNTransitionFromOptions(transitionOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setPerformsPlacementTransitions:(BOOL)performsPlacementTransitions
|
- (void)setPerformsPlacementTransitions:(BOOL)performsPlacementTransitions {
|
||||||
{
|
|
||||||
mbgl::style::TransitionOptions transitionOptions = self.rawStyle->getTransitionOptions();
|
mbgl::style::TransitionOptions transitionOptions = self.rawStyle->getTransitionOptions();
|
||||||
transitionOptions.enablePlacementTransitions = static_cast<bool>(performsPlacementTransitions);
|
transitionOptions.enablePlacementTransitions = static_cast<bool>(performsPlacementTransitions);
|
||||||
self.rawStyle->setTransitionOptions(transitionOptions);
|
self.rawStyle->setTransitionOptions(transitionOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)performsPlacementTransitions
|
- (BOOL)performsPlacementTransitions {
|
||||||
{
|
|
||||||
mbgl::style::TransitionOptions transitionOptions = self.rawStyle->getTransitionOptions();
|
mbgl::style::TransitionOptions transitionOptions = self.rawStyle->getTransitionOptions();
|
||||||
return transitionOptions.enablePlacementTransitions;
|
return transitionOptions.enablePlacementTransitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Style light
|
// MARK: Style light
|
||||||
|
|
||||||
- (void)setLight:(MLNLight *)light
|
- (void)setLight:(MLNLight *)light {
|
||||||
{
|
std::unique_ptr<mbgl::style::Light> mbglLight =
|
||||||
std::unique_ptr<mbgl::style::Light> mbglLight = std::make_unique<mbgl::style::Light>([light mbglLight]);
|
std::make_unique<mbgl::style::Light>([light mbglLight]);
|
||||||
self.rawStyle->setLight(std::move(mbglLight));
|
self.rawStyle->setLight(std::move(mbglLight));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MLNLight *)light
|
- (MLNLight *)light {
|
||||||
{
|
|
||||||
auto mbglLight = self.rawStyle->getLight();
|
auto mbglLight = self.rawStyle->getLight();
|
||||||
MLNLight *light = [[MLNLight alloc] initWithMBGLLight:mbglLight];
|
MLNLight *light = [[MLNLight alloc] initWithMBGLLight:mbglLight];
|
||||||
return light;
|
return light;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
return [NSString
|
||||||
return [NSString stringWithFormat:@"<%@: %p; name = %@, URL = %@>",
|
stringWithFormat:@"<%@: %p; name = %@, URL = %@>", NSStringFromClass([self class]),
|
||||||
NSStringFromClass([self class]), (void *)self,
|
(void *)self,
|
||||||
self.name ? [NSString stringWithFormat:@"\"%@\"", self.name] : self.name,
|
self.name ? [NSString stringWithFormat:@"\"%@\"", self.name] : self.name,
|
||||||
self.URL ? [NSString stringWithFormat:@"\"%@\"", self.URL] : self.URL];
|
self.URL ? [NSString stringWithFormat:@"\"%@\"", self.URL] : self.URL];
|
||||||
}
|
}
|
||||||
@@ -593,9 +580,10 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
// MARK: Mapbox Streets source introspection
|
// MARK: Mapbox Streets source introspection
|
||||||
|
|
||||||
- (void)localizeLabelsIntoLocale:(nullable NSLocale *)locale {
|
- (void)localizeLabelsIntoLocale:(nullable NSLocale *)locale {
|
||||||
NSSet<MLNVectorTileSource *> *streetsSources =
|
NSSet<MLNVectorTileSource *> *streetsSources = [self.sources
|
||||||
[self.sources filteredSetUsingPredicate:
|
filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
|
||||||
[NSPredicate predicateWithBlock:^BOOL(MLNVectorTileSource * _Nullable source, NSDictionary<NSString *, id> * _Nullable bindings) {
|
MLNVectorTileSource *_Nullable source,
|
||||||
|
NSDictionary<NSString *, id> *_Nullable bindings) {
|
||||||
return [source isKindOfClass:[MLNVectorTileSource class]] && [source isMapboxStreets];
|
return [source isKindOfClass:[MLNVectorTileSource class]] && [source isMapboxStreets];
|
||||||
}]];
|
}]];
|
||||||
NSSet<NSString *> *streetsSourceIdentifiers = [streetsSources valueForKey:@"identifier"];
|
NSSet<NSString *> *streetsSourceIdentifiers = [streetsSources valueForKey:@"identifier"];
|
||||||
@@ -617,7 +605,8 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSSet<MLNVectorTileSource *> *)mapboxStreetsSources {
|
- (NSSet<MLNVectorTileSource *> *)mapboxStreetsSources {
|
||||||
return [self.sources objectsPassingTest:^BOOL (__kindof MLNVectorTileSource * _Nonnull source, BOOL * _Nonnull stop) {
|
return [self.sources
|
||||||
|
objectsPassingTest:^BOOL(__kindof MLNVectorTileSource *_Nonnull source, BOOL *_Nonnull stop) {
|
||||||
return [source isKindOfClass:[MLNVectorTileSource class]] && source.mapboxStreets;
|
return [source isKindOfClass:[MLNVectorTileSource class]] && source.mapboxStreets;
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -625,9 +614,16 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
- (NSArray<MLNStyleLayer *> *)placeStyleLayers {
|
- (NSArray<MLNStyleLayer *> *)placeStyleLayers {
|
||||||
NSSet *streetsSourceIdentifiers = [self.mapboxStreetsSources valueForKey:@"identifier"];
|
NSSet *streetsSourceIdentifiers = [self.mapboxStreetsSources valueForKey:@"identifier"];
|
||||||
|
|
||||||
NSSet *placeSourceLayerIdentifiers = [NSSet setWithObjects:@"marine_label", @"country_label", @"state_label", @"place_label", @"water_label", @"poi_label", @"rail_station_label", @"mountain_peak_label", @"natural_label", @"transit_stop_label", nil];
|
NSSet *placeSourceLayerIdentifiers =
|
||||||
NSPredicate *isPlacePredicate = [NSPredicate predicateWithBlock:^BOOL (MLNVectorStyleLayer * _Nullable layer, NSDictionary<NSString *, id> * _Nullable bindings) {
|
[NSSet setWithObjects:@"marine_label", @"country_label", @"state_label", @"place_label",
|
||||||
return [layer isKindOfClass:[MLNVectorStyleLayer class]] && [streetsSourceIdentifiers containsObject:layer.sourceIdentifier] && [placeSourceLayerIdentifiers containsObject:layer.sourceLayerIdentifier];
|
@"water_label", @"poi_label", @"rail_station_label",
|
||||||
|
@"mountain_peak_label", @"natural_label", @"transit_stop_label", nil];
|
||||||
|
NSPredicate *isPlacePredicate =
|
||||||
|
[NSPredicate predicateWithBlock:^BOOL(MLNVectorStyleLayer *_Nullable layer,
|
||||||
|
NSDictionary<NSString *, id> *_Nullable bindings) {
|
||||||
|
return [layer isKindOfClass:[MLNVectorStyleLayer class]] &&
|
||||||
|
[streetsSourceIdentifiers containsObject:layer.sourceIdentifier] &&
|
||||||
|
[placeSourceLayerIdentifiers containsObject:layer.sourceLayerIdentifier];
|
||||||
}];
|
}];
|
||||||
return [self.layers filteredArrayUsingPredicate:isPlacePredicate];
|
return [self.layers filteredArrayUsingPredicate:isPlacePredicate];
|
||||||
}
|
}
|
||||||
@@ -636,8 +632,12 @@ const MLNExceptionName MLNRedundantSourceIdentifierException = @"MLNRedundantSou
|
|||||||
NSSet *streetsSourceIdentifiers = [self.mapboxStreetsSources valueForKey:@"identifier"];
|
NSSet *streetsSourceIdentifiers = [self.mapboxStreetsSources valueForKey:@"identifier"];
|
||||||
|
|
||||||
NSSet *roadStyleLayerIdentifiers = [NSSet setWithObjects:@"road_label", @"road", nil];
|
NSSet *roadStyleLayerIdentifiers = [NSSet setWithObjects:@"road_label", @"road", nil];
|
||||||
NSPredicate *isPlacePredicate = [NSPredicate predicateWithBlock:^BOOL (MLNVectorStyleLayer * _Nullable layer, NSDictionary<NSString *, id> * _Nullable bindings) {
|
NSPredicate *isPlacePredicate =
|
||||||
return [layer isKindOfClass:[MLNVectorStyleLayer class]] && [streetsSourceIdentifiers containsObject:layer.sourceIdentifier] && [roadStyleLayerIdentifiers containsObject:layer.sourceLayerIdentifier];
|
[NSPredicate predicateWithBlock:^BOOL(MLNVectorStyleLayer *_Nullable layer,
|
||||||
|
NSDictionary<NSString *, id> *_Nullable bindings) {
|
||||||
|
return [layer isKindOfClass:[MLNVectorStyleLayer class]] &&
|
||||||
|
[streetsSourceIdentifiers containsObject:layer.sourceIdentifier] &&
|
||||||
|
[roadStyleLayerIdentifiers containsObject:layer.sourceLayerIdentifier];
|
||||||
}];
|
}];
|
||||||
return [self.layers filteredArrayUsingPredicate:isPlacePredicate];
|
return [self.layers filteredArrayUsingPredicate:isPlacePredicate];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#import "MLNStyleLayer_Private.h"
|
#import "MLNStyleLayer_Private.h"
|
||||||
#import "MLNStyle_Private.h"
|
#import "MLNStyle_Private.h"
|
||||||
|
|
||||||
#include <mbgl/style/style.hpp>
|
|
||||||
#include <mbgl/style/layer.hpp>
|
#include <mbgl/style/layer.hpp>
|
||||||
|
#include <mbgl/style/style.hpp>
|
||||||
|
|
||||||
const MLNExceptionName MLNInvalidStyleLayerException = @"MLNInvalidStyleLayerException";
|
const MLNExceptionName MLNInvalidStyleLayerException = @"MLNInvalidStyleLayerException";
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ const MLNExceptionName MLNInvalidStyleLayerException = @"MLNInvalidStyleLayerExc
|
|||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
_identifier = @(rawLayer->getID().c_str());
|
_identifier = @(rawLayer->getID().c_str());
|
||||||
_weakLayer = rawLayer->makeWeakPtr();
|
_weakLayer = rawLayer->makeWeakPtr();
|
||||||
rawLayer->peer = LayerWrapper { self };
|
rawLayer->peer = LayerWrapper{self};
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -33,17 +33,16 @@ const MLNExceptionName MLNInvalidStyleLayerException = @"MLNInvalidStyleLayerExc
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (mbgl::style::Layer *)rawLayer
|
- (mbgl::style::Layer *)rawLayer {
|
||||||
{
|
|
||||||
return _weakLayer.get();
|
return _weakLayer.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addToStyle:(MLNStyle *)style belowLayer:(MLNStyleLayer *)otherLayer
|
- (void)addToStyle:(MLNStyle *)style belowLayer:(MLNStyleLayer *)otherLayer {
|
||||||
{
|
|
||||||
if (_pendingLayer == nullptr) {
|
if (_pendingLayer == nullptr) {
|
||||||
[NSException raise:MLNRedundantLayerException
|
[NSException raise:MLNRedundantLayerException
|
||||||
format:@"This instance %@ was already added to %@. Adding the same layer instance " \
|
format:@"This instance %@ was already added to %@. Adding the same layer instance "
|
||||||
"to the style more than once is invalid.", self, style];
|
"to the style more than once is invalid.",
|
||||||
|
self, style];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (otherLayer) {
|
if (otherLayer) {
|
||||||
@@ -54,68 +53,59 @@ const MLNExceptionName MLNInvalidStyleLayerException = @"MLNInvalidStyleLayerExc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeFromStyle:(MLNStyle *)style
|
- (void)removeFromStyle:(MLNStyle *)style {
|
||||||
{
|
|
||||||
if (self.rawLayer == style.rawStyle->getLayer(self.identifier.UTF8String)) {
|
if (self.rawLayer == style.rawStyle->getLayer(self.identifier.UTF8String)) {
|
||||||
_pendingLayer = style.rawStyle->removeLayer(self.identifier.UTF8String);
|
_pendingLayer = style.rawStyle->removeLayer(self.identifier.UTF8String);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setVisible:(BOOL)visible
|
- (void)setVisible:(BOOL)visible {
|
||||||
{
|
|
||||||
MLNAssertStyleLayerIsValid();
|
MLNAssertStyleLayerIsValid();
|
||||||
|
|
||||||
mbgl::style::VisibilityType v = visible
|
mbgl::style::VisibilityType v =
|
||||||
? mbgl::style::VisibilityType::Visible
|
visible ? mbgl::style::VisibilityType::Visible : mbgl::style::VisibilityType::None;
|
||||||
: mbgl::style::VisibilityType::None;
|
|
||||||
self.rawLayer->setVisibility(v);
|
self.rawLayer->setVisibility(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isVisible
|
- (BOOL)isVisible {
|
||||||
{
|
|
||||||
MLNAssertStyleLayerIsValid();
|
MLNAssertStyleLayerIsValid();
|
||||||
|
|
||||||
mbgl::style::VisibilityType v = self.rawLayer->getVisibility();
|
mbgl::style::VisibilityType v = self.rawLayer->getVisibility();
|
||||||
return (v == mbgl::style::VisibilityType::Visible);
|
return (v == mbgl::style::VisibilityType::Visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setMaximumZoomLevel:(float)maximumZoomLevel
|
- (void)setMaximumZoomLevel:(float)maximumZoomLevel {
|
||||||
{
|
|
||||||
MLNAssertStyleLayerIsValid();
|
MLNAssertStyleLayerIsValid();
|
||||||
|
|
||||||
self.rawLayer->setMaxZoom(maximumZoomLevel);
|
self.rawLayer->setMaxZoom(maximumZoomLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)maximumZoomLevel
|
- (float)maximumZoomLevel {
|
||||||
{
|
|
||||||
MLNAssertStyleLayerIsValid();
|
MLNAssertStyleLayerIsValid();
|
||||||
|
|
||||||
return self.rawLayer->getMaxZoom();
|
return self.rawLayer->getMaxZoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setMinimumZoomLevel:(float)minimumZoomLevel
|
- (void)setMinimumZoomLevel:(float)minimumZoomLevel {
|
||||||
{
|
|
||||||
MLNAssertStyleLayerIsValid();
|
MLNAssertStyleLayerIsValid();
|
||||||
|
|
||||||
self.rawLayer->setMinZoom(minimumZoomLevel);
|
self.rawLayer->setMinZoom(minimumZoomLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)minimumZoomLevel
|
- (float)minimumZoomLevel {
|
||||||
{
|
|
||||||
MLNAssertStyleLayerIsValid();
|
MLNAssertStyleLayerIsValid();
|
||||||
|
|
||||||
return self.rawLayer->getMinZoom();
|
return self.rawLayer->getMinZoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description {
|
||||||
{
|
|
||||||
if (self.rawLayer) {
|
if (self.rawLayer) {
|
||||||
return [NSString stringWithFormat:@"<%@: %p; identifier = %@; visible = %@>",
|
return [NSString stringWithFormat:@"<%@: %p; identifier = %@; visible = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self, self.identifier,
|
NSStringFromClass([self class]), (void *)self,
|
||||||
self.visible ? @"YES" : @"NO"];
|
self.identifier, self.visible ? @"YES" : @"NO"];
|
||||||
}
|
} else {
|
||||||
else {
|
return
|
||||||
return [NSString stringWithFormat:@"<%@: %p; identifier = %@; visible = NO>",
|
[NSString stringWithFormat:@"<%@: %p; identifier = %@; visible = NO>",
|
||||||
NSStringFromClass([self class]), (void *)self, self.identifier];
|
NSStringFromClass([self class]), (void *)self, self.identifier];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#import "MLNBackgroundStyleLayer_Private.h"
|
#import "MLNBackgroundStyleLayer_Private.h"
|
||||||
#import "MLNCircleStyleLayer_Private.h"
|
#import "MLNCircleStyleLayer_Private.h"
|
||||||
|
#import "MLNCustomStyleLayer_Private.h"
|
||||||
#import "MLNFillExtrusionStyleLayer_Private.h"
|
#import "MLNFillExtrusionStyleLayer_Private.h"
|
||||||
#import "MLNFillStyleLayer_Private.h"
|
#import "MLNFillStyleLayer_Private.h"
|
||||||
#import "MLNHeatmapStyleLayer_Private.h"
|
#import "MLNHeatmapStyleLayer_Private.h"
|
||||||
@@ -9,7 +10,6 @@
|
|||||||
#import "MLNLineStyleLayer_Private.h"
|
#import "MLNLineStyleLayer_Private.h"
|
||||||
#import "MLNRasterStyleLayer_Private.h"
|
#import "MLNRasterStyleLayer_Private.h"
|
||||||
#import "MLNSymbolStyleLayer_Private.h"
|
#import "MLNSymbolStyleLayer_Private.h"
|
||||||
#import "MLNCustomStyleLayer_Private.h"
|
|
||||||
|
|
||||||
#import "MLNCustomDrawableStyleLayer_Private.h"
|
#import "MLNCustomDrawableStyleLayer_Private.h"
|
||||||
|
|
||||||
@@ -102,13 +102,14 @@ void LayerManagerDarwin::addLayerTypeCoreOnly(std::unique_ptr<LayerFactory> fact
|
|||||||
void LayerManagerDarwin::registerCoreFactory(LayerFactory* factory) {
|
void LayerManagerDarwin::registerCoreFactory(LayerFactory* factory) {
|
||||||
std::string type{factory->getTypeInfo()->type};
|
std::string type{factory->getTypeInfo()->type};
|
||||||
if (!type.empty()) {
|
if (!type.empty()) {
|
||||||
NSCAssert(typeToFactory.find(type) == typeToFactory.end(), @"A layer type can be registered only once.");
|
NSCAssert(typeToFactory.find(type) == typeToFactory.end(),
|
||||||
|
@"A layer type can be registered only once.");
|
||||||
typeToFactory.emplace(std::make_pair(std::move(type), factory));
|
typeToFactory.emplace(std::make_pair(std::move(type), factory));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LayerPeerFactory* LayerManagerDarwin::getPeerFactory(const mbgl::style::LayerTypeInfo* typeInfo) {
|
LayerPeerFactory* LayerManagerDarwin::getPeerFactory(const mbgl::style::LayerTypeInfo* typeInfo) {
|
||||||
for (const auto& factory: peerFactories) {
|
for (const auto& factory : peerFactories) {
|
||||||
if (layerTypeInfoEquals(factory->getCoreLayerFactory()->getTypeInfo(), typeInfo)) {
|
if (layerTypeInfoEquals(factory->getCoreLayerFactory()->getTypeInfo(), typeInfo)) {
|
||||||
return factory.get();
|
return factory.get();
|
||||||
}
|
}
|
||||||
@@ -126,7 +127,7 @@ LayerFactory* LayerManagerDarwin::getFactory(const mbgl::style::LayerTypeInfo* i
|
|||||||
return peerFactory->getCoreLayerFactory();
|
return peerFactory->getCoreLayerFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& factory: coreFactories) {
|
for (const auto& factory : coreFactories) {
|
||||||
if (layerTypeInfoEquals(factory->getTypeInfo(), info)) {
|
if (layerTypeInfoEquals(factory->getTypeInfo(), info)) {
|
||||||
// if (factory->getTypeInfo() == info) {
|
// if (factory->getTypeInfo() == info) {
|
||||||
return factory.get();
|
return factory.get();
|
||||||
@@ -143,11 +144,10 @@ LayerManagerDarwin* LayerManagerDarwin::get() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
LayerManager* LayerManager::get() noexcept {
|
LayerManager* LayerManager::get() noexcept { return LayerManagerDarwin::get(); }
|
||||||
return LayerManagerDarwin::get();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(MBGL_LAYER_LINE_DISABLE_ALL) || defined(MBGL_LAYER_SYMBOL_DISABLE_ALL) || defined(MBGL_LAYER_FILL_DISABLE_ALL)
|
#if defined(MBGL_LAYER_LINE_DISABLE_ALL) || defined(MBGL_LAYER_SYMBOL_DISABLE_ALL) || \
|
||||||
|
defined(MBGL_LAYER_FILL_DISABLE_ALL)
|
||||||
const bool LayerManager::annotationsEnabled = false;
|
const bool LayerManager::annotationsEnabled = false;
|
||||||
#else
|
#else
|
||||||
const bool LayerManager::annotationsEnabled = true;
|
const bool LayerManager::annotationsEnabled = true;
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ struct LayerWrapper {
|
|||||||
namespace mbgl {
|
namespace mbgl {
|
||||||
|
|
||||||
class LayerPeerFactory {
|
class LayerPeerFactory {
|
||||||
public:
|
public:
|
||||||
virtual ~LayerPeerFactory() = default;
|
virtual ~LayerPeerFactory() = default;
|
||||||
/**
|
/**
|
||||||
Get the corresponding core layer factory.
|
Get the corresponding core layer factory.
|
||||||
|
|||||||
@@ -2,60 +2,56 @@
|
|||||||
|
|
||||||
#include <mbgl/style/expression/expression.hpp>
|
#include <mbgl/style/expression/expression.hpp>
|
||||||
|
|
||||||
const MLNStyleFunctionOption MLNStyleFunctionOptionInterpolationBase = @"MLNStyleFunctionOptionInterpolationBase";
|
const MLNStyleFunctionOption MLNStyleFunctionOptionInterpolationBase =
|
||||||
const MLNStyleFunctionOption MLNStyleFunctionOptionDefaultValue = @"MLNStyleFunctionOptionDefaultValue";
|
@"MLNStyleFunctionOptionInterpolationBase";
|
||||||
|
const MLNStyleFunctionOption MLNStyleFunctionOptionDefaultValue =
|
||||||
|
@"MLNStyleFunctionOptionDefaultValue";
|
||||||
|
|
||||||
id MLNJSONObjectFromMBGLValue(const mbgl::Value &value) {
|
id MLNJSONObjectFromMBGLValue(const mbgl::Value &value) {
|
||||||
return value.match([](const mbgl::NullValue) -> id {
|
return value.match(
|
||||||
return [NSNull null];
|
[](const mbgl::NullValue) -> id { return [NSNull null]; },
|
||||||
}, [](const bool value) {
|
[](const bool value) { return @(value); }, [](const float value) { return @(value); },
|
||||||
return @(value);
|
[](const int64_t value) { return @(value); }, [](const uint64_t value) { return @(value); },
|
||||||
}, [](const float value) {
|
[](const double value) { return @(value); },
|
||||||
return @(value);
|
[](const std::string &value) { return @(value.c_str()); },
|
||||||
}, [](const int64_t value) {
|
[](const mbgl::Color &value) { return [MLNColor mgl_colorWithColor:value]; },
|
||||||
return @(value);
|
[](const mbgl::style::Position &value) {
|
||||||
}, [](const uint64_t value) {
|
|
||||||
return @(value);
|
|
||||||
}, [](const double value) {
|
|
||||||
return @(value);
|
|
||||||
}, [](const std::string &value) {
|
|
||||||
return @(value.c_str());
|
|
||||||
}, [](const mbgl::Color &value) {
|
|
||||||
return [MLNColor mgl_colorWithColor:value];
|
|
||||||
}, [](const mbgl::style::Position &value) {
|
|
||||||
std::array<float, 3> spherical = value.getSpherical();
|
std::array<float, 3> spherical = value.getSpherical();
|
||||||
MLNSphericalPosition position = MLNSphericalPositionMake(spherical[0], spherical[1], spherical[2]);
|
MLNSphericalPosition position =
|
||||||
|
MLNSphericalPositionMake(spherical[0], spherical[1], spherical[2]);
|
||||||
return [NSValue valueWithMLNSphericalPosition:position];
|
return [NSValue valueWithMLNSphericalPosition:position];
|
||||||
}, [&](const std::vector<mbgl::Value> &vector) {
|
},
|
||||||
|
[&](const std::vector<mbgl::Value> &vector) {
|
||||||
NSMutableArray *array = [NSMutableArray arrayWithCapacity:vector.size()];
|
NSMutableArray *array = [NSMutableArray arrayWithCapacity:vector.size()];
|
||||||
for (auto value : vector) {
|
for (auto value : vector) {
|
||||||
[array addObject:MLNJSONObjectFromMBGLValue(value)];
|
[array addObject:MLNJSONObjectFromMBGLValue(value)];
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
}, [&](const std::unordered_map<std::string, mbgl::Value> &map) {
|
},
|
||||||
|
[&](const std::unordered_map<std::string, mbgl::Value> &map) {
|
||||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:map.size()];
|
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:map.size()];
|
||||||
for (auto &item : map) {
|
for (auto &item : map) {
|
||||||
dictionary[@(item.first.c_str())] = MLNJSONObjectFromMBGLValue(item.second);
|
dictionary[@(item.first.c_str())] = MLNJSONObjectFromMBGLValue(item.second);
|
||||||
}
|
}
|
||||||
return dictionary;
|
return dictionary;
|
||||||
}, [](const auto &) -> id {
|
},
|
||||||
return nil;
|
[](const auto &) -> id { return nil; });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
id MLNJSONObjectFromMBGLExpression(const mbgl::style::expression::Expression &mbglExpression) {
|
id MLNJSONObjectFromMBGLExpression(const mbgl::style::expression::Expression &mbglExpression) {
|
||||||
return MLNJSONObjectFromMBGLValue(mbglExpression.serialize());
|
return MLNJSONObjectFromMBGLValue(mbglExpression.serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<mbgl::style::expression::Expression> MLNClusterPropertyFromNSExpression(
|
||||||
std::unique_ptr<mbgl::style::expression::Expression> MLNClusterPropertyFromNSExpression(NSExpression *expression) {
|
NSExpression *expression) {
|
||||||
if (!expression) {
|
if (!expression) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSArray *jsonExpression = expression.mgl_jsonExpressionObject;
|
NSArray *jsonExpression = expression.mgl_jsonExpressionObject;
|
||||||
|
|
||||||
auto expr = mbgl::style::expression::dsl::createExpression(mbgl::style::conversion::makeConvertible(jsonExpression));
|
auto expr = mbgl::style::expression::dsl::createExpression(
|
||||||
|
mbgl::style::conversion::makeConvertible(jsonExpression));
|
||||||
|
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ id MLNJSONObjectFromMBGLExpression(const mbgl::style::expression::Expression &mb
|
|||||||
template <typename MBGLType, typename ObjCType, typename MBGLElement = MBGLType,
|
template <typename MBGLType, typename ObjCType, typename MBGLElement = MBGLType,
|
||||||
typename ObjCEnum = ObjCType>
|
typename ObjCEnum = ObjCType>
|
||||||
class MLNStyleValueTransformer {
|
class MLNStyleValueTransformer {
|
||||||
public:
|
public:
|
||||||
/// Convert an mbgl property value into an mgl style value
|
/// Convert an mbgl property value into an mgl style value
|
||||||
NSExpression *toExpression(const mbgl::style::PropertyValue<MBGLType> &mbglValue) {
|
NSExpression *toExpression(const mbgl::style::PropertyValue<MBGLType> &mbglValue) {
|
||||||
PropertyExpressionEvaluator evaluator;
|
PropertyExpressionEvaluator evaluator;
|
||||||
@@ -139,7 +139,7 @@ class MLNStyleValueTransformer {
|
|||||||
return *value;
|
return *value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private: // Private utilities for converting from mgl to mbgl values
|
private: // Private utilities for converting from mgl to mbgl values
|
||||||
/**
|
/**
|
||||||
As hack to allow converting enum => string values, we accept a second, dummy parameter in
|
As hack to allow converting enum => string values, we accept a second, dummy parameter in
|
||||||
the toRawStyleSpecValue() methods for converting 'atomic' (non-style-function) values.
|
the toRawStyleSpecValue() methods for converting 'atomic' (non-style-function) values.
|
||||||
@@ -307,7 +307,7 @@ class MLNStyleValueTransformer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private: // Private utilities for converting from mbgl to mgl values
|
private: // Private utilities for converting from mbgl to mgl values
|
||||||
// Bool
|
// Bool
|
||||||
static NSNumber *toMLNRawStyleValue(const bool mbglStopValue) { return @(mbglStopValue); }
|
static NSNumber *toMLNRawStyleValue(const bool mbglStopValue) { return @(mbglStopValue); }
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
#import "MLNTilePyramidOfflineRegion.h"
|
#import "MLNTilePyramidOfflineRegion.h"
|
||||||
|
|
||||||
#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
|
#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#import "MLNOfflineRegion_Private.h"
|
|
||||||
#import "MLNTilePyramidOfflineRegion_Private.h"
|
|
||||||
#import "MLNGeometry_Private.h"
|
#import "MLNGeometry_Private.h"
|
||||||
#import "MLNStyle.h"
|
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
#import "MLNOfflineRegion_Private.h"
|
||||||
|
#import "MLNStyle.h"
|
||||||
|
#import "MLNTilePyramidOfflineRegion_Private.h"
|
||||||
|
|
||||||
@interface MLNTilePyramidOfflineRegion () <MLNOfflineRegion_Private, MLNTilePyramidOfflineRegion_Private>
|
@interface MLNTilePyramidOfflineRegion () <MLNOfflineRegion_Private,
|
||||||
|
MLNTilePyramidOfflineRegion_Private>
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@
|
|||||||
@synthesize styleURL = _styleURL;
|
@synthesize styleURL = _styleURL;
|
||||||
@synthesize includesIdeographicGlyphs = _includesIdeographicGlyphs;
|
@synthesize includesIdeographicGlyphs = _includesIdeographicGlyphs;
|
||||||
|
|
||||||
-(NSDictionary *)offlineStartEventAttributes {
|
- (NSDictionary *)offlineStartEventAttributes {
|
||||||
return @{};
|
return @{};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,22 +32,26 @@
|
|||||||
|
|
||||||
- (instancetype)init {
|
- (instancetype)init {
|
||||||
MLNLogInfo(@"Calling this initializer is not allowed.");
|
MLNLogInfo(@"Calling this initializer is not allowed.");
|
||||||
[NSException raise:NSGenericException format:
|
[NSException raise:NSGenericException
|
||||||
@"-[MLNTilePyramidOfflineRegion init] is unavailable. "
|
format:@"-[MLNTilePyramidOfflineRegion init] is unavailable. "
|
||||||
@"Use -initWithStyleURL:bounds:fromZoomLevel:toZoomLevel: instead."];
|
@"Use -initWithStyleURL:bounds:fromZoomLevel:toZoomLevel: instead."];
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithStyleURL:(NSURL *)styleURL bounds:(MLNCoordinateBounds)bounds fromZoomLevel:(double)minimumZoomLevel toZoomLevel:(double)maximumZoomLevel {
|
- (instancetype)initWithStyleURL:(NSURL *)styleURL
|
||||||
MLNLogDebug(@"Initializing styleURL: %@ bounds: %@ fromZoomLevel: %f toZoomLevel: %f", styleURL, MLNStringFromCoordinateBounds(bounds), minimumZoomLevel, maximumZoomLevel);
|
bounds:(MLNCoordinateBounds)bounds
|
||||||
|
fromZoomLevel:(double)minimumZoomLevel
|
||||||
|
toZoomLevel:(double)maximumZoomLevel {
|
||||||
|
MLNLogDebug(@"Initializing styleURL: %@ bounds: %@ fromZoomLevel: %f toZoomLevel: %f", styleURL,
|
||||||
|
MLNStringFromCoordinateBounds(bounds), minimumZoomLevel, maximumZoomLevel);
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
if (!styleURL) {
|
if (!styleURL) {
|
||||||
styleURL = [MLNStyle defaultStyleURL];
|
styleURL = [MLNStyle defaultStyleURL];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!styleURL.scheme) {
|
if (!styleURL.scheme) {
|
||||||
[NSException raise:MLNInvalidStyleURLException format:
|
[NSException raise:MLNInvalidStyleURLException
|
||||||
@"%@ does not support setting a relative file URL as the style URL. "
|
format:@"%@ does not support setting a relative file URL as the style URL. "
|
||||||
@"To download the online resources required by this style, "
|
@"To download the online resources required by this style, "
|
||||||
@"specify a URL to an online copy of this style. "
|
@"specify a URL to an online copy of this style. "
|
||||||
@"For Mapbox-hosted styles, use the mapbox: scheme.",
|
@"For Mapbox-hosted styles, use the mapbox: scheme.",
|
||||||
@@ -62,44 +67,53 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithOfflineRegionDefinition:(const mbgl::OfflineTilePyramidRegionDefinition &)definition {
|
- (instancetype)initWithOfflineRegionDefinition:
|
||||||
|
(const mbgl::OfflineTilePyramidRegionDefinition &)definition {
|
||||||
NSURL *styleURL = [NSURL URLWithString:@(definition.styleURL.c_str())];
|
NSURL *styleURL = [NSURL URLWithString:@(definition.styleURL.c_str())];
|
||||||
MLNCoordinateBounds bounds = MLNCoordinateBoundsFromLatLngBounds(definition.bounds);
|
MLNCoordinateBounds bounds = MLNCoordinateBoundsFromLatLngBounds(definition.bounds);
|
||||||
MLNTilePyramidOfflineRegion* result = [self initWithStyleURL:styleURL bounds:bounds fromZoomLevel:definition.minZoom toZoomLevel:definition.maxZoom];
|
MLNTilePyramidOfflineRegion *result = [self initWithStyleURL:styleURL
|
||||||
|
bounds:bounds
|
||||||
|
fromZoomLevel:definition.minZoom
|
||||||
|
toZoomLevel:definition.maxZoom];
|
||||||
result.includesIdeographicGlyphs = definition.includeIdeographs;
|
result.includesIdeographicGlyphs = definition.includeIdeographs;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (const mbgl::OfflineRegionDefinition)offlineRegionDefinition {
|
- (const mbgl::OfflineRegionDefinition)offlineRegionDefinition {
|
||||||
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
||||||
const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale];
|
const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)]
|
||||||
|
? [[UIScreen mainScreen] nativeScale]
|
||||||
|
: [[UIScreen mainScreen] scale];
|
||||||
#elif TARGET_OS_MAC
|
#elif TARGET_OS_MAC
|
||||||
const float scaleFactor = [NSScreen mainScreen].backingScaleFactor;
|
const float scaleFactor = [NSScreen mainScreen].backingScaleFactor;
|
||||||
#endif
|
#endif
|
||||||
return mbgl::OfflineTilePyramidRegionDefinition(_styleURL.absoluteString.UTF8String,
|
return mbgl::OfflineTilePyramidRegionDefinition(
|
||||||
MLNLatLngBoundsFromCoordinateBounds(_bounds),
|
_styleURL.absoluteString.UTF8String, MLNLatLngBoundsFromCoordinateBounds(_bounds),
|
||||||
_minimumZoomLevel, _maximumZoomLevel,
|
_minimumZoomLevel, _maximumZoomLevel, scaleFactor, _includesIdeographicGlyphs);
|
||||||
scaleFactor, _includesIdeographicGlyphs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (nullable instancetype)initWithCoder:(NSCoder *)coder {
|
- (nullable instancetype)initWithCoder:(NSCoder *)coder {
|
||||||
MLNLogInfo(@"Initializing with coder.");
|
MLNLogInfo(@"Initializing with coder.");
|
||||||
NSURL *styleURL = [coder decodeObjectForKey:@"styleURL"];
|
NSURL *styleURL = [coder decodeObjectForKey:@"styleURL"];
|
||||||
CLLocationCoordinate2D sw = CLLocationCoordinate2DMake([coder decodeDoubleForKey:@"southWestLatitude"],
|
CLLocationCoordinate2D sw =
|
||||||
|
CLLocationCoordinate2DMake([coder decodeDoubleForKey:@"southWestLatitude"],
|
||||||
[coder decodeDoubleForKey:@"southWestLongitude"]);
|
[coder decodeDoubleForKey:@"southWestLongitude"]);
|
||||||
CLLocationCoordinate2D ne = CLLocationCoordinate2DMake([coder decodeDoubleForKey:@"northEastLatitude"],
|
CLLocationCoordinate2D ne =
|
||||||
|
CLLocationCoordinate2DMake([coder decodeDoubleForKey:@"northEastLatitude"],
|
||||||
[coder decodeDoubleForKey:@"northEastLongitude"]);
|
[coder decodeDoubleForKey:@"northEastLongitude"]);
|
||||||
MLNCoordinateBounds bounds = MLNCoordinateBoundsMake(sw, ne);
|
MLNCoordinateBounds bounds = MLNCoordinateBoundsMake(sw, ne);
|
||||||
double minimumZoomLevel = [coder decodeDoubleForKey:@"minimumZoomLevel"];
|
double minimumZoomLevel = [coder decodeDoubleForKey:@"minimumZoomLevel"];
|
||||||
double maximumZoomLevel = [coder decodeDoubleForKey:@"maximumZoomLevel"];
|
double maximumZoomLevel = [coder decodeDoubleForKey:@"maximumZoomLevel"];
|
||||||
|
|
||||||
MLNTilePyramidOfflineRegion* result = [self initWithStyleURL:styleURL bounds:bounds fromZoomLevel:minimumZoomLevel toZoomLevel:maximumZoomLevel];
|
MLNTilePyramidOfflineRegion *result = [self initWithStyleURL:styleURL
|
||||||
|
bounds:bounds
|
||||||
|
fromZoomLevel:minimumZoomLevel
|
||||||
|
toZoomLevel:maximumZoomLevel];
|
||||||
result.includesIdeographicGlyphs = [coder decodeBoolForKey:@"includesIdeographicGlyphs"];
|
result.includesIdeographicGlyphs = [coder decodeBoolForKey:@"includesIdeographicGlyphs"];
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)encodeWithCoder:(NSCoder *)coder
|
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||||
{
|
|
||||||
[coder encodeObject:_styleURL forKey:@"styleURL"];
|
[coder encodeObject:_styleURL forKey:@"styleURL"];
|
||||||
[coder encodeDouble:_bounds.sw.latitude forKey:@"southWestLatitude"];
|
[coder encodeDouble:_bounds.sw.latitude forKey:@"southWestLatitude"];
|
||||||
[coder encodeDouble:_bounds.sw.longitude forKey:@"southWestLongitude"];
|
[coder encodeDouble:_bounds.sw.longitude forKey:@"southWestLongitude"];
|
||||||
@@ -111,7 +125,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (id)copyWithZone:(nullable NSZone *)zone {
|
- (id)copyWithZone:(nullable NSZone *)zone {
|
||||||
MLNTilePyramidOfflineRegion* result = [[[self class] allocWithZone:zone] initWithStyleURL:_styleURL bounds:_bounds fromZoomLevel:_minimumZoomLevel toZoomLevel:_maximumZoomLevel];
|
MLNTilePyramidOfflineRegion *result =
|
||||||
|
[[[self class] allocWithZone:zone] initWithStyleURL:_styleURL
|
||||||
|
bounds:_bounds
|
||||||
|
fromZoomLevel:_minimumZoomLevel
|
||||||
|
toZoomLevel:_maximumZoomLevel];
|
||||||
result.includesIdeographicGlyphs = _includesIdeographicGlyphs;
|
result.includesIdeographicGlyphs = _includesIdeographicGlyphs;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -125,19 +143,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
MLNTilePyramidOfflineRegion *otherRegion = other;
|
MLNTilePyramidOfflineRegion *otherRegion = other;
|
||||||
return (_minimumZoomLevel == otherRegion->_minimumZoomLevel
|
return (_minimumZoomLevel == otherRegion->_minimumZoomLevel &&
|
||||||
&& _maximumZoomLevel == otherRegion->_maximumZoomLevel
|
_maximumZoomLevel == otherRegion->_maximumZoomLevel &&
|
||||||
&& MLNCoordinateBoundsEqualToCoordinateBounds(_bounds, otherRegion->_bounds)
|
MLNCoordinateBoundsEqualToCoordinateBounds(_bounds, otherRegion->_bounds) &&
|
||||||
&& [_styleURL isEqual:otherRegion->_styleURL]
|
[_styleURL isEqual:otherRegion->_styleURL] &&
|
||||||
&& _includesIdeographicGlyphs == otherRegion->_includesIdeographicGlyphs);
|
_includesIdeographicGlyphs == otherRegion->_includesIdeographicGlyphs);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)hash {
|
- (NSUInteger)hash {
|
||||||
return (_styleURL.hash
|
return (_styleURL.hash + @(_bounds.sw.latitude).hash + @(_bounds.sw.longitude).hash +
|
||||||
+ @(_bounds.sw.latitude).hash + @(_bounds.sw.longitude).hash
|
@(_bounds.ne.latitude).hash + @(_bounds.ne.longitude).hash + @(_minimumZoomLevel).hash +
|
||||||
+ @(_bounds.ne.latitude).hash + @(_bounds.ne.longitude).hash
|
@(_maximumZoomLevel).hash + @(_includesIdeographicGlyphs).hash);
|
||||||
+ @(_minimumZoomLevel).hash + @(_maximumZoomLevel).hash
|
|
||||||
+ @(_includesIdeographicGlyphs).hash);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -8,25 +8,30 @@
|
|||||||
#import "NSValue+MLNAdditions.h"
|
#import "NSValue+MLNAdditions.h"
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#else
|
#else
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <mbgl/util/tileset.hpp>
|
#include <mbgl/util/tileset.hpp>
|
||||||
|
|
||||||
const MLNTileSourceOption MLNTileSourceOptionMinimumZoomLevel = @"MLNTileSourceOptionMinimumZoomLevel";
|
const MLNTileSourceOption MLNTileSourceOptionMinimumZoomLevel =
|
||||||
const MLNTileSourceOption MLNTileSourceOptionMaximumZoomLevel = @"MLNTileSourceOptionMaximumZoomLevel";
|
@"MLNTileSourceOptionMinimumZoomLevel";
|
||||||
const MLNTileSourceOption MLNTileSourceOptionCoordinateBounds = @"MLNTileSourceOptionCoordinateBounds";
|
const MLNTileSourceOption MLNTileSourceOptionMaximumZoomLevel =
|
||||||
const MLNTileSourceOption MLNTileSourceOptionAttributionHTMLString = @"MLNTileSourceOptionAttributionHTMLString";
|
@"MLNTileSourceOptionMaximumZoomLevel";
|
||||||
const MLNTileSourceOption MLNTileSourceOptionAttributionInfos = @"MLNTileSourceOptionAttributionInfos";
|
const MLNTileSourceOption MLNTileSourceOptionCoordinateBounds =
|
||||||
const MLNTileSourceOption MLNTileSourceOptionTileCoordinateSystem = @"MLNTileSourceOptionTileCoordinateSystem";
|
@"MLNTileSourceOptionCoordinateBounds";
|
||||||
|
const MLNTileSourceOption MLNTileSourceOptionAttributionHTMLString =
|
||||||
|
@"MLNTileSourceOptionAttributionHTMLString";
|
||||||
|
const MLNTileSourceOption MLNTileSourceOptionAttributionInfos =
|
||||||
|
@"MLNTileSourceOptionAttributionInfos";
|
||||||
|
const MLNTileSourceOption MLNTileSourceOptionTileCoordinateSystem =
|
||||||
|
@"MLNTileSourceOptionTileCoordinateSystem";
|
||||||
|
|
||||||
@implementation MLNTileSource
|
@implementation MLNTileSource
|
||||||
|
|
||||||
- (NSURL *)configurationURL {
|
- (NSURL *)configurationURL {
|
||||||
[NSException raise:MLNAbstractClassException
|
[NSException raise:MLNAbstractClassException format:@"MLNTileSource is an abstract class"];
|
||||||
format:@"MLNTileSource is an abstract class"];
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,21 +39,23 @@ const MLNTileSourceOption MLNTileSourceOptionTileCoordinateSystem = @"MLNTileSou
|
|||||||
return [self attributionInfosWithFontSize:0 linkColor:nil];
|
return [self attributionInfosWithFontSize:0 linkColor:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray<MLNAttributionInfo *> *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MLNColor *)linkColor {
|
- (NSArray<MLNAttributionInfo *> *)attributionInfosWithFontSize:(CGFloat)fontSize
|
||||||
|
linkColor:(nullable MLNColor *)linkColor {
|
||||||
return [MLNAttributionInfo attributionInfosFromHTMLString:self.attributionHTMLString
|
return [MLNAttributionInfo attributionInfosFromHTMLString:self.attributionHTMLString
|
||||||
fontSize:fontSize
|
fontSize:fontSize
|
||||||
linkColor:linkColor];
|
linkColor:linkColor];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)attributionHTMLString {
|
- (NSString *)attributionHTMLString {
|
||||||
[NSException raise:MLNAbstractClassException
|
[NSException raise:MLNAbstractClassException format:@"MLNTileSource is an abstract class"];
|
||||||
format:@"MLNTileSource is an abstract class"];
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
mbgl::Tileset MLNTileSetFromTileURLTemplates(NSArray<NSString *> *tileURLTemplates, NSDictionary<MLNTileSourceOption, id> * _Nullable options) {
|
mbgl::Tileset MLNTileSetFromTileURLTemplates(
|
||||||
|
NSArray<NSString *> *tileURLTemplates,
|
||||||
|
NSDictionary<MLNTileSourceOption, id> *_Nullable options) {
|
||||||
mbgl::Tileset tileSet;
|
mbgl::Tileset tileSet;
|
||||||
|
|
||||||
for (NSString *tileURLTemplate in tileURLTemplates) {
|
for (NSString *tileURLTemplate in tileURLTemplates) {
|
||||||
@@ -73,14 +80,16 @@ mbgl::Tileset MLNTileSetFromTileURLTemplates(NSArray<NSString *> *tileURLTemplat
|
|||||||
}
|
}
|
||||||
if (tileSet.zoomRange.min > tileSet.zoomRange.max) {
|
if (tileSet.zoomRange.min > tileSet.zoomRange.max) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"MLNTileSourceOptionMinimumZoomLevel must be less than MLNTileSourceOptionMaximumZoomLevel."];
|
format:@"MLNTileSourceOptionMinimumZoomLevel must be less than "
|
||||||
|
@"MLNTileSourceOptionMaximumZoomLevel."];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NSValue *coordinateBounds = options[MLNTileSourceOptionCoordinateBounds]) {
|
if (NSValue *coordinateBounds = options[MLNTileSourceOptionCoordinateBounds]) {
|
||||||
if (![coordinateBounds isKindOfClass:[NSValue class]]
|
if (![coordinateBounds isKindOfClass:[NSValue class]] &&
|
||||||
&& strcmp(coordinateBounds.objCType, @encode(MLNCoordinateBounds)) == 0) {
|
strcmp(coordinateBounds.objCType, @encode(MLNCoordinateBounds)) == 0) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"MLNTileSourceOptionCoordinateBounds must be set to an NSValue containing an MLNCoordinateBounds."];
|
format:@"MLNTileSourceOptionCoordinateBounds must be set to an NSValue "
|
||||||
|
@"containing an MLNCoordinateBounds."];
|
||||||
}
|
}
|
||||||
tileSet.bounds = MLNLatLngBoundsFromCoordinateBounds(coordinateBounds.MLNCoordinateBoundsValue);
|
tileSet.bounds = MLNLatLngBoundsFromCoordinateBounds(coordinateBounds.MLNCoordinateBoundsValue);
|
||||||
}
|
}
|
||||||
@@ -99,24 +108,30 @@ mbgl::Tileset MLNTileSetFromTileURLTemplates(NSArray<NSString *> *tileURLTemplat
|
|||||||
format:@"MLNTileSourceOptionAttributionInfos must be set to a string."];
|
format:@"MLNTileSourceOptionAttributionInfos must be set to a string."];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSAttributedString *attributedString = [MLNAttributionInfo attributedStringForAttributionInfos:attributionInfos];
|
NSAttributedString *attributedString =
|
||||||
|
[MLNAttributionInfo attributedStringForAttributionInfos:attributionInfos];
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
static NSString * const NSExcludedElementsDocumentAttribute = @"ExcludedElements";
|
static NSString *const NSExcludedElementsDocumentAttribute = @"ExcludedElements";
|
||||||
#endif
|
#endif
|
||||||
NSDictionary *documentAttributes = @{
|
NSDictionary *documentAttributes = @{
|
||||||
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
|
NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType,
|
||||||
NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding),
|
NSCharacterEncodingDocumentAttribute : @(NSUTF8StringEncoding),
|
||||||
// The attribution string is meant to be a simple, inline fragment, not a full-fledged, validating document.
|
// The attribution string is meant to be a simple, inline fragment, not a full-fledged,
|
||||||
NSExcludedElementsDocumentAttribute: @[@"XML", @"DOCTYPE", @"html", @"head", @"meta", @"title", @"style", @"body", @"p"],
|
// validating document.
|
||||||
|
NSExcludedElementsDocumentAttribute :
|
||||||
|
@[ @"XML", @"DOCTYPE", @"html", @"head", @"meta", @"title", @"style", @"body", @"p" ],
|
||||||
};
|
};
|
||||||
NSData *data = [attributedString dataFromRange:attributedString.mgl_wholeRange documentAttributes:documentAttributes error:NULL];
|
NSData *data = [attributedString dataFromRange:attributedString.mgl_wholeRange
|
||||||
|
documentAttributes:documentAttributes
|
||||||
|
error:NULL];
|
||||||
NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||||
tileSet.attribution = html.UTF8String;
|
tileSet.attribution = html.UTF8String;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NSNumber *tileCoordinateSystemNumber = options[MLNTileSourceOptionTileCoordinateSystem]) {
|
if (NSNumber *tileCoordinateSystemNumber = options[MLNTileSourceOptionTileCoordinateSystem]) {
|
||||||
if (![tileCoordinateSystemNumber isKindOfClass:[NSValue class]]) {
|
if (![tileCoordinateSystemNumber isKindOfClass:[NSValue class]]) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException
|
||||||
|
raise:NSInvalidArgumentException
|
||||||
format:@"MLNTileSourceOptionTileCoordinateSystem must be set to an NSValue or NSNumber."];
|
format:@"MLNTileSourceOptionTileCoordinateSystem must be set to an NSValue or NSNumber."];
|
||||||
}
|
}
|
||||||
MLNTileCoordinateSystem tileCoordinateSystem;
|
MLNTileCoordinateSystem tileCoordinateSystem;
|
||||||
@@ -152,7 +167,8 @@ mbgl::Tileset MLNTileSetFromTileURLTemplates(NSArray<NSString *> *tileURLTemplat
|
|||||||
encodingNumber = options[MLNVectorTileSourceOptionEncoding];
|
encodingNumber = options[MLNVectorTileSourceOptionEncoding];
|
||||||
if (encodingNumber) {
|
if (encodingNumber) {
|
||||||
if (![encodingNumber isKindOfClass:[NSValue class]]) {
|
if (![encodingNumber isKindOfClass:[NSValue class]]) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException
|
||||||
|
raise:NSInvalidArgumentException
|
||||||
format:@"MLNTileSourceOptionVectorEncoding must be set to an NSValue or NSNumber."];
|
format:@"MLNTileSourceOptionVectorEncoding must be set to an NSValue or NSNumber."];
|
||||||
}
|
}
|
||||||
MLNVectorTileSourceEncoding encoding;
|
MLNVectorTileSourceEncoding encoding;
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import <mbgl/util/feature.hpp>
|
||||||
#import <mbgl/util/geometry.hpp>
|
#import <mbgl/util/geometry.hpp>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Recursively transforms a C++ type into the corresponding Foundation type.
|
Recursively transforms a C++ type into the corresponding Foundation type.
|
||||||
*/
|
*/
|
||||||
class ValueEvaluator {
|
class ValueEvaluator {
|
||||||
public:
|
public:
|
||||||
id operator()(const mbgl::NullValue &) const { return [NSNull null]; }
|
id operator()(const mbgl::NullValue &) const { return [NSNull null]; }
|
||||||
|
|
||||||
id operator()(const bool &value) const { return value ? @YES : @NO; }
|
id operator()(const bool &value) const { return value ? @YES : @NO; }
|
||||||
|
|||||||
@@ -4,27 +4,25 @@
|
|||||||
@implementation MLNVectorStyleLayer
|
@implementation MLNVectorStyleLayer
|
||||||
|
|
||||||
- (void)setPredicate:(NSPredicate *)predicate {
|
- (void)setPredicate:(NSPredicate *)predicate {
|
||||||
[NSException raise:MLNAbstractClassException
|
[NSException raise:MLNAbstractClassException format:@"MLNVectorStyleLayer is an abstract class"];
|
||||||
format:@"MLNVectorStyleLayer is an abstract class"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSPredicate *)predicate {
|
- (NSPredicate *)predicate {
|
||||||
[NSException raise:MLNAbstractClassException
|
[NSException raise:MLNAbstractClassException format:@"MLNVectorStyleLayer is an abstract class"];
|
||||||
format:@"MLNVectorStyleLayer is an abstract class"];
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description {
|
- (NSString *)description {
|
||||||
if (self.rawLayer) {
|
if (self.rawLayer) {
|
||||||
return [NSString stringWithFormat:
|
return
|
||||||
@"<%@: %p; identifier = %@; sourceIdentifier = %@; "
|
[NSString stringWithFormat:@"<%@: %p; identifier = %@; sourceIdentifier = %@; "
|
||||||
@"sourceLayerIdentifier = %@; predicate = %@; visible = %@>",
|
@"sourceLayerIdentifier = %@; predicate = %@; visible = %@>",
|
||||||
NSStringFromClass([self class]), (void *)self, self.identifier,
|
NSStringFromClass([self class]), (void *)self, self.identifier,
|
||||||
self.sourceIdentifier, self.sourceLayerIdentifier, self.predicate,
|
self.sourceIdentifier, self.sourceLayerIdentifier,
|
||||||
self.visible ? @"YES" : @"NO"];
|
self.predicate, self.visible ? @"YES" : @"NO"];
|
||||||
}
|
} else {
|
||||||
else {
|
return [NSString
|
||||||
return [NSString stringWithFormat:
|
stringWithFormat:
|
||||||
@"<%@: %p; identifier = %@; sourceIdentifier = <unknown>; "
|
@"<%@: %p; identifier = %@; sourceIdentifier = <unknown>; "
|
||||||
@"sourceLayerIdentifier = <unknown>; predicate = <unknown>; visible = <unknown>>",
|
@"sourceLayerIdentifier = <unknown>; predicate = <unknown>; visible = <unknown>>",
|
||||||
NSStringFromClass([self class]), (void *)self, self.identifier];
|
NSStringFromClass([self class]), (void *)self, self.identifier];
|
||||||
|
|||||||
@@ -2,17 +2,17 @@
|
|||||||
|
|
||||||
#import "MLNFeature_Private.h"
|
#import "MLNFeature_Private.h"
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
#import "MLNSource_Private.h"
|
|
||||||
#import "MLNTileSource_Private.h"
|
|
||||||
#import "MLNStyle_Private.h"
|
|
||||||
#import "MLNMapView_Private.h"
|
#import "MLNMapView_Private.h"
|
||||||
|
#import "MLNSource_Private.h"
|
||||||
|
#import "MLNStyle_Private.h"
|
||||||
|
#import "MLNTileSource_Private.h"
|
||||||
|
|
||||||
#import "NSPredicate+MLNPrivateAdditions.h"
|
#import "NSPredicate+MLNPrivateAdditions.h"
|
||||||
#import "NSURL+MLNAdditions.h"
|
#import "NSURL+MLNAdditions.h"
|
||||||
|
|
||||||
#include <mbgl/map/map.hpp>
|
#include <mbgl/map/map.hpp>
|
||||||
#include <mbgl/style/sources/vector_source.hpp>
|
|
||||||
#include <mbgl/renderer/renderer.hpp>
|
#include <mbgl/renderer/renderer.hpp>
|
||||||
|
#include <mbgl/style/sources/vector_source.hpp>
|
||||||
|
|
||||||
const MLNTileSourceOption MLNVectorTileSourceOptionEncoding = @"MLNVectorTileSourceOptionEncoding";
|
const MLNTileSourceOption MLNVectorTileSourceOptionEncoding = @"MLNVectorTileSourceOptionEncoding";
|
||||||
|
|
||||||
@@ -24,24 +24,25 @@ const MLNTileSourceOption MLNVectorTileSourceOptionEncoding = @"MLNVectorTileSou
|
|||||||
|
|
||||||
@implementation MLNVectorTileSource
|
@implementation MLNVectorTileSource
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL {
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
auto source = std::make_unique<mbgl::style::VectorSource>(identifier.UTF8String,
|
configurationURL:(NSURL *)configurationURL {
|
||||||
|
auto source = std::make_unique<mbgl::style::VectorSource>(
|
||||||
|
identifier.UTF8String,
|
||||||
configurationURL.mgl_URLByStandardizingScheme.absoluteString.UTF8String);
|
configurationURL.mgl_URLByStandardizingScheme.absoluteString.UTF8String);
|
||||||
return self = [super initWithPendingSource:std::move(source)];
|
return self = [super initWithPendingSource:std::move(source)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
configurationURLString:(NSString *)configurationURLString
|
configurationURLString:(NSString *)configurationURLString {
|
||||||
{
|
auto source = std::make_unique<mbgl::style::VectorSource>(identifier.UTF8String,
|
||||||
auto source = std::make_unique<mbgl::style::VectorSource>(
|
configurationURLString.UTF8String);
|
||||||
identifier.UTF8String,
|
|
||||||
configurationURLString.UTF8String
|
|
||||||
);
|
|
||||||
|
|
||||||
return self = [super initWithPendingSource:std::move(source)];
|
return self = [super initWithPendingSource:std::move(source)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray<NSString *> *)tileURLTemplates options:(nullable NSDictionary<MLNTileSourceOption, id> *)options {
|
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||||
|
tileURLTemplates:(NSArray<NSString *> *)tileURLTemplates
|
||||||
|
options:(nullable NSDictionary<MLNTileSourceOption, id> *)options {
|
||||||
mbgl::Tileset tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, options);
|
mbgl::Tileset tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, options);
|
||||||
auto source = std::make_unique<mbgl::style::VectorSource>(identifier.UTF8String, tileSet);
|
auto source = std::make_unique<mbgl::style::VectorSource>(identifier.UTF8String, tileSet);
|
||||||
return self = [super initWithPendingSource:std::move(source)];
|
return self = [super initWithPendingSource:std::move(source)];
|
||||||
@@ -59,7 +60,8 @@ const MLNTileSourceOption MLNVectorTileSourceOptionEncoding = @"MLNVectorTileSou
|
|||||||
|
|
||||||
- (NSString *)attributionHTMLString {
|
- (NSString *)attributionHTMLString {
|
||||||
if (!self.rawSource) {
|
if (!self.rawSource) {
|
||||||
MLNAssert(0, @"Source with identifier `%@` was invalidated after a style change", self.identifier);
|
MLNAssert(0, @"Source with identifier `%@` was invalidated after a style change",
|
||||||
|
self.identifier);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,13 +69,16 @@ const MLNTileSourceOption MLNVectorTileSourceOptionEncoding = @"MLNVectorTileSou
|
|||||||
return attribution ? @(attribution->c_str()) : nil;
|
return attribution ? @(attribution->c_str()) : nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray<id <MLNFeature>> *)featuresInSourceLayersWithIdentifiers:(NSSet<NSString *> *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate {
|
- (NSArray<id<MLNFeature>> *)
|
||||||
|
featuresInSourceLayersWithIdentifiers:(NSSet<NSString *> *)sourceLayerIdentifiers
|
||||||
|
predicate:(nullable NSPredicate *)predicate {
|
||||||
MLNAssertStyleSourceIsValid();
|
MLNAssertStyleSourceIsValid();
|
||||||
std::optional<std::vector<std::string>> optionalSourceLayerIDs;
|
std::optional<std::vector<std::string>> optionalSourceLayerIDs;
|
||||||
if (sourceLayerIdentifiers) {
|
if (sourceLayerIdentifiers) {
|
||||||
__block std::vector<std::string> layerIDs;
|
__block std::vector<std::string> layerIDs;
|
||||||
layerIDs.reserve(sourceLayerIdentifiers.count);
|
layerIDs.reserve(sourceLayerIdentifiers.count);
|
||||||
[sourceLayerIdentifiers enumerateObjectsUsingBlock:^(NSString * _Nonnull identifier, BOOL * _Nonnull stop) {
|
[sourceLayerIdentifiers
|
||||||
|
enumerateObjectsUsingBlock:^(NSString *_Nonnull identifier, BOOL *_Nonnull stop) {
|
||||||
layerIDs.push_back(identifier.UTF8String);
|
layerIDs.push_back(identifier.UTF8String);
|
||||||
}];
|
}];
|
||||||
optionalSourceLayerIDs = layerIDs;
|
optionalSourceLayerIDs = layerIDs;
|
||||||
@@ -87,7 +92,8 @@ const MLNTileSourceOption MLNVectorTileSourceOptionEncoding = @"MLNVectorTileSou
|
|||||||
std::vector<mbgl::Feature> features;
|
std::vector<mbgl::Feature> features;
|
||||||
if ([self.stylable isKindOfClass:[MLNMapView class]]) {
|
if ([self.stylable isKindOfClass:[MLNMapView class]]) {
|
||||||
MLNMapView *mapView = (MLNMapView *)self.stylable;
|
MLNMapView *mapView = (MLNMapView *)self.stylable;
|
||||||
features = mapView.renderer->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter });
|
features = mapView.renderer->querySourceFeatures(self.rawSource->getID(),
|
||||||
|
{optionalSourceLayerIDs, optionalFilter});
|
||||||
}
|
}
|
||||||
return MLNFeaturesFromMBGLFeatures(features);
|
return MLNFeaturesFromMBGLFeatures(features);
|
||||||
}
|
}
|
||||||
@@ -102,18 +108,38 @@ const MLNTileSourceOption MLNVectorTileSourceOptionEncoding = @"MLNVectorTileSou
|
|||||||
|
|
||||||
https://www.mapbox.com/vector-tiles/mapbox-streets-v8/
|
https://www.mapbox.com/vector-tiles/mapbox-streets-v8/
|
||||||
*/
|
*/
|
||||||
static NSArray * const MLNMapboxStreetsLanguages = @[
|
static NSArray *const MLNMapboxStreetsLanguages = @[
|
||||||
@"ar", @"de", @"en", @"es", @"fr", @"ja", @"ko", @"pt", @"ru", @"vi",
|
@"ar",
|
||||||
@"zh-Hans", @"zh-Hant",
|
@"de",
|
||||||
|
@"en",
|
||||||
|
@"es",
|
||||||
|
@"fr",
|
||||||
|
@"ja",
|
||||||
|
@"ko",
|
||||||
|
@"pt",
|
||||||
|
@"ru",
|
||||||
|
@"vi",
|
||||||
|
@"zh-Hans",
|
||||||
|
@"zh-Hant",
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Like ``MLNMapboxStreetsLanguages``, but deanglicized for use with
|
Like ``MLNMapboxStreetsLanguages``, but deanglicized for use with
|
||||||
`+[NSBundle preferredLocalizationsFromArray:forPreferences:]`.
|
`+[NSBundle preferredLocalizationsFromArray:forPreferences:]`.
|
||||||
*/
|
*/
|
||||||
static NSArray * const MLNMapboxStreetsAlternativeLanguages = @[
|
static NSArray *const MLNMapboxStreetsAlternativeLanguages = @[
|
||||||
@"mul", @"ar", @"de", @"es", @"fr", @"ja", @"ko", @"pt", @"ru", @"vi",
|
@"mul",
|
||||||
@"zh-Hans", @"zh-Hant",
|
@"ar",
|
||||||
|
@"de",
|
||||||
|
@"es",
|
||||||
|
@"fr",
|
||||||
|
@"ja",
|
||||||
|
@"ko",
|
||||||
|
@"pt",
|
||||||
|
@"ru",
|
||||||
|
@"vi",
|
||||||
|
@"zh-Hans",
|
||||||
|
@"zh-Hant",
|
||||||
];
|
];
|
||||||
|
|
||||||
+ (NSSet<NSString *> *)mapboxStreetsLanguages {
|
+ (NSSet<NSString *> *)mapboxStreetsLanguages {
|
||||||
@@ -130,23 +156,29 @@ static NSArray * const MLNMapboxStreetsAlternativeLanguages = @[
|
|||||||
}
|
}
|
||||||
|
|
||||||
+ (NSString *)preferredMapboxStreetsLanguageForPreferences:(NSArray<NSString *> *)preferencesArray {
|
+ (NSString *)preferredMapboxStreetsLanguageForPreferences:(NSArray<NSString *> *)preferencesArray {
|
||||||
BOOL acceptsEnglish = [preferencesArray filteredArrayUsingPredicate:
|
BOOL acceptsEnglish =
|
||||||
[NSPredicate predicateWithBlock:^BOOL(NSString * _Nullable language, NSDictionary<NSString *,id> * _Nullable bindings) {
|
[preferencesArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
|
||||||
|
NSString *_Nullable language,
|
||||||
|
NSDictionary<NSString *, id>
|
||||||
|
*_Nullable bindings) {
|
||||||
NSString *languageCode;
|
NSString *languageCode;
|
||||||
|
|
||||||
if (@available(iOS 10.0, macOS 10.12.0, *)) {
|
if (@available(iOS 10.0, macOS 10.12.0, *)) {
|
||||||
languageCode = [NSLocale localeWithLocaleIdentifier:language].languageCode;
|
languageCode =
|
||||||
}
|
[NSLocale localeWithLocaleIdentifier:language].languageCode;
|
||||||
else {
|
} else {
|
||||||
languageCode = [[NSLocale localeWithLocaleIdentifier:language] objectForKey:NSLocaleLanguageCode];
|
languageCode = [[NSLocale localeWithLocaleIdentifier:language]
|
||||||
|
objectForKey:NSLocaleLanguageCode];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [languageCode isEqualToString:@"en"];
|
return [languageCode isEqualToString:@"en"];
|
||||||
}]].count;
|
}]]
|
||||||
|
.count;
|
||||||
|
|
||||||
NSArray<NSString *> *availableLanguages = acceptsEnglish ? MLNMapboxStreetsLanguages : MLNMapboxStreetsAlternativeLanguages;
|
NSArray<NSString *> *availableLanguages =
|
||||||
NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:availableLanguages
|
acceptsEnglish ? MLNMapboxStreetsLanguages : MLNMapboxStreetsAlternativeLanguages;
|
||||||
forPreferences:preferencesArray];
|
NSArray<NSString *> *preferredLanguages =
|
||||||
|
[NSBundle preferredLocalizationsFromArray:availableLanguages forPreferences:preferencesArray];
|
||||||
NSString *mostSpecificLanguage;
|
NSString *mostSpecificLanguage;
|
||||||
for (NSString *language in preferredLanguages) {
|
for (NSString *language in preferredLanguages) {
|
||||||
if (language.length > mostSpecificLanguage.length) {
|
if (language.length > mostSpecificLanguage.length) {
|
||||||
@@ -162,7 +194,8 @@ static NSArray * const MLNMapboxStreetsAlternativeLanguages = @[
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
NSArray *identifiers = [url.host componentsSeparatedByString:@","];
|
NSArray *identifiers = [url.host componentsSeparatedByString:@","];
|
||||||
return [identifiers containsObject:@"mapbox.mapbox-streets-v8"] || [identifiers containsObject:@"mapbox.mapbox-streets-v7"];
|
return [identifiers containsObject:@"mapbox.mapbox-streets-v8"] ||
|
||||||
|
[identifiers containsObject:@"mapbox.mapbox-streets-v7"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#import "NSArray+MLNAdditions.h"
|
#import "NSArray+MLNAdditions.h"
|
||||||
|
|
||||||
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
#import "NSDictionary+MLNAdditions.h"
|
#import "NSDictionary+MLNAdditions.h"
|
||||||
#import "NSExpression+MLNPrivateAdditions.h"
|
#import "NSExpression+MLNPrivateAdditions.h"
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
|
||||||
|
|
||||||
@implementation NSArray (MLNAdditions)
|
@implementation NSArray (MLNAdditions)
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
|
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
|
||||||
BOOL isSubsequentItem = NO;
|
BOOL isSubsequentItem = NO;
|
||||||
for (NSAttributedString *component in self) {
|
for (NSAttributedString *component in self) {
|
||||||
MLNAssert([component isKindOfClass:[NSAttributedString class]], @"Items in array must be attributed strings.");
|
MLNAssert([component isKindOfClass:[NSAttributedString class]],
|
||||||
|
@"Items in array must be attributed strings.");
|
||||||
if (isSubsequentItem) {
|
if (isSubsequentItem) {
|
||||||
[attributedString appendAttributedString:attributedSeparator];
|
[attributedString appendAttributedString:attributedSeparator];
|
||||||
}
|
}
|
||||||
@@ -42,22 +43,22 @@
|
|||||||
+ (NSArray *)mgl_coordinatesFromCoordinates:(std::vector<CLLocationCoordinate2D>)coords {
|
+ (NSArray *)mgl_coordinatesFromCoordinates:(std::vector<CLLocationCoordinate2D>)coords {
|
||||||
NSMutableArray *coordinates = [NSMutableArray array];
|
NSMutableArray *coordinates = [NSMutableArray array];
|
||||||
for (auto coord : coords) {
|
for (auto coord : coords) {
|
||||||
[coordinates addObject:@{@"latitude": @(coord.latitude),
|
[coordinates addObject:@{@"latitude" : @(coord.latitude), @"longitude" : @(coord.longitude)}];
|
||||||
@"longitude": @(coord.longitude)}];
|
|
||||||
}
|
}
|
||||||
return coordinates;
|
return coordinates;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (std::vector<CLLocationCoordinate2D>)mgl_coordinates {
|
- (std::vector<CLLocationCoordinate2D>)mgl_coordinates {
|
||||||
NSUInteger numberOfCoordinates = [self count];
|
NSUInteger numberOfCoordinates = [self count];
|
||||||
CLLocationCoordinate2D *coords = (CLLocationCoordinate2D *)malloc(numberOfCoordinates * sizeof(CLLocationCoordinate2D));
|
CLLocationCoordinate2D *coords =
|
||||||
|
(CLLocationCoordinate2D *)malloc(numberOfCoordinates * sizeof(CLLocationCoordinate2D));
|
||||||
|
|
||||||
for (NSUInteger i = 0; i < numberOfCoordinates; i++) {
|
for (NSUInteger i = 0; i < numberOfCoordinates; i++) {
|
||||||
coords[i] = CLLocationCoordinate2DMake([self[i][@"latitude"] doubleValue],
|
coords[i] = CLLocationCoordinate2DMake([self[i][@"latitude"] doubleValue],
|
||||||
[self[i][@"longitude"] doubleValue]);
|
[self[i][@"longitude"] doubleValue]);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CLLocationCoordinate2D> coordinates = { coords, coords + numberOfCoordinates };
|
std::vector<CLLocationCoordinate2D> coordinates = {coords, coords + numberOfCoordinates};
|
||||||
free(coords);
|
free(coords);
|
||||||
|
|
||||||
return coordinates;
|
return coordinates;
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ const MLNExceptionName MLNBundleNotFoundException = @"MLNBundleNotFoundException
|
|||||||
bundle = [self bundleWithPath:bundlePath];
|
bundle = [self bundleWithPath:bundlePath];
|
||||||
} else {
|
} else {
|
||||||
[NSException raise:MLNBundleNotFoundException
|
[NSException raise:MLNBundleNotFoundException
|
||||||
format:@"The MapLibre framework bundle could not be found. If using MapLibre iOS as a static framework, make sure that MapLibre bundle is copied into the root of the app bundle."];
|
format:@"The MapLibre framework bundle could not be found. If using MapLibre "
|
||||||
|
@"iOS as a static framework, make sure that MapLibre bundle is copied "
|
||||||
|
@"into the root of the app bundle."];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -40,8 +42,8 @@ const MLNExceptionName MLNBundleNotFoundException = @"MLNBundleNotFoundException
|
|||||||
|
|
||||||
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && (__IPHONE_OS_VERSION_MAX_ALLOWED < 120200)) || \
|
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && (__IPHONE_OS_VERSION_MAX_ALLOWED < 120200)) || \
|
||||||
(defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED < 101404))
|
(defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED < 101404))
|
||||||
// Before SDK 12.2 (bundled with Xcode 10.2): There’s no main bundle identifier when running in a unit test bundle.
|
// Before SDK 12.2 (bundled with Xcode 10.2): There’s no main bundle identifier when running in a
|
||||||
// 12.2 and after: the above bundle identifier is: com.apple.dt.xctest.tool
|
// unit test bundle. 12.2 and after: the above bundle identifier is: com.apple.dt.xctest.tool
|
||||||
if (!bundleIdentifier) {
|
if (!bundleIdentifier) {
|
||||||
bundleIdentifier = [NSBundle bundleForClass:[MLNSettings class]].bundleIdentifier;
|
bundleIdentifier = [NSBundle bundleForClass:[MLNSettings class]].bundleIdentifier;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,23 +5,26 @@
|
|||||||
|
|
||||||
@implementation NSCoder (MLNAdditions)
|
@implementation NSCoder (MLNAdditions)
|
||||||
|
|
||||||
- (void)mgl_encodeLocationCoordinates2D:(std::vector<CLLocationCoordinate2D>)coordinates forKey:(NSString *)key {
|
- (void)mgl_encodeLocationCoordinates2D:(std::vector<CLLocationCoordinate2D>)coordinates
|
||||||
|
forKey:(NSString *)key {
|
||||||
[self encodeObject:[NSArray mgl_coordinatesFromCoordinates:coordinates] forKey:key];
|
[self encodeObject:[NSArray mgl_coordinatesFromCoordinates:coordinates] forKey:key];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (std::vector<CLLocationCoordinate2D>)mgl_decodeLocationCoordinates2DForKey:(NSString *)key {
|
- (std::vector<CLLocationCoordinate2D>)mgl_decodeLocationCoordinates2DForKey:(NSString *)key {
|
||||||
NSSet<Class> *coordinateClasses = [NSSet setWithArray:@[[NSArray class], [NSDictionary class]]];
|
NSSet<Class> *coordinateClasses = [NSSet setWithArray:@[ [NSArray class], [NSDictionary class] ]];
|
||||||
NSArray *coordinates = [self decodeObjectOfClasses:coordinateClasses forKey:key];
|
NSArray *coordinates = [self decodeObjectOfClasses:coordinateClasses forKey:key];
|
||||||
return [coordinates mgl_coordinates];
|
return [coordinates mgl_coordinates];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)encodeMLNCoordinate:(CLLocationCoordinate2D)coordinate forKey:(NSString *)key {
|
- (void)encodeMLNCoordinate:(CLLocationCoordinate2D)coordinate forKey:(NSString *)key {
|
||||||
[self encodeObject:@{@"latitude": @(coordinate.latitude), @"longitude": @(coordinate.longitude)} forKey:key];
|
[self encodeObject:@{@"latitude" : @(coordinate.latitude), @"longitude" : @(coordinate.longitude)}
|
||||||
|
forKey:key];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CLLocationCoordinate2D)decodeMLNCoordinateForKey:(NSString *)key {
|
- (CLLocationCoordinate2D)decodeMLNCoordinateForKey:(NSString *)key {
|
||||||
NSDictionary *coordinate = [self decodeObjectOfClass:[NSDictionary class] forKey:key];
|
NSDictionary *coordinate = [self decodeObjectOfClass:[NSDictionary class] forKey:key];
|
||||||
return CLLocationCoordinate2DMake([coordinate[@"latitude"] doubleValue], [coordinate[@"longitude"] doubleValue]);
|
return CLLocationCoordinate2DMake([coordinate[@"latitude"] doubleValue],
|
||||||
|
[coordinate[@"longitude"] doubleValue]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
#import "MLNStyleValue_Private.h"
|
#import "MLNStyleValue_Private.h"
|
||||||
|
|
||||||
#import "NSPredicate+MLNAdditions.h"
|
|
||||||
#import "NSExpression+MLNPrivateAdditions.h"
|
#import "NSExpression+MLNPrivateAdditions.h"
|
||||||
|
#import "NSPredicate+MLNAdditions.h"
|
||||||
|
|
||||||
@implementation NSComparisonPredicate (MLNAdditions)
|
@implementation NSComparisonPredicate (MLNAdditions)
|
||||||
|
|
||||||
@@ -19,7 +19,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"Comparison predicate must compare an attribute (as a key path) to a constant or vice versa."];
|
format:@"Comparison predicate must compare an attribute (as a key path) to a "
|
||||||
|
@"constant or vice versa."];
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +36,8 @@
|
|||||||
value = leftExpression.mgl_constantMBGLValue;
|
value = leftExpression.mgl_constantMBGLValue;
|
||||||
} else {
|
} else {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"Comparison predicate must compare an attribute (as a key path) to a constant or vice versa."];
|
format:@"Comparison predicate must compare an attribute (as a key path) to a "
|
||||||
|
@"constant or vice versa."];
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -52,7 +54,8 @@
|
|||||||
type = leftExpression.mgl_featureType;
|
type = leftExpression.mgl_featureType;
|
||||||
} else {
|
} else {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"Comparison predicate must compare an attribute (as a key path) to a constant or vice versa."];
|
format:@"Comparison predicate must compare an attribute (as a key path) to a "
|
||||||
|
@"constant or vice versa."];
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
@@ -69,7 +72,8 @@
|
|||||||
identifier = leftExpression.mgl_featureIdentifier;
|
identifier = leftExpression.mgl_featureIdentifier;
|
||||||
} else {
|
} else {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"Comparison predicate must compare an attribute (as a key path) to a constant or vice versa."];
|
format:@"Comparison predicate must compare an attribute (as a key path) to a "
|
||||||
|
@"constant or vice versa."];
|
||||||
}
|
}
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
@@ -88,18 +92,23 @@
|
|||||||
if (self.predicateOperatorType == NSNotEqualToPredicateOperatorType) {
|
if (self.predicateOperatorType == NSNotEqualToPredicateOperatorType) {
|
||||||
// https://github.com/mapbox/mapbox-gl-js/issues/9339
|
// https://github.com/mapbox/mapbox-gl-js/issues/9339
|
||||||
if (self.options) {
|
if (self.options) {
|
||||||
[NSException raise:NSInvalidArgumentException format:@"NSComparisonPredicateOptions not supported for “ALL … !=” comparisons."];
|
[NSException
|
||||||
|
raise:NSInvalidArgumentException
|
||||||
|
format:@"NSComparisonPredicateOptions not supported for “ALL … !=” comparisons."];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSPredicate *directPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression
|
NSPredicate *directPredicate =
|
||||||
|
[NSComparisonPredicate predicateWithLeftExpression:self.rightExpression
|
||||||
rightExpression:self.leftExpression
|
rightExpression:self.leftExpression
|
||||||
modifier:NSDirectPredicateModifier
|
modifier:NSDirectPredicateModifier
|
||||||
type:NSInPredicateOperatorType
|
type:NSInPredicateOperatorType
|
||||||
options:self.options];
|
options:self.options];
|
||||||
NSPredicate *invertedPredicate = [NSCompoundPredicate notPredicateWithSubpredicate:directPredicate];
|
NSPredicate *invertedPredicate =
|
||||||
|
[NSCompoundPredicate notPredicateWithSubpredicate:directPredicate];
|
||||||
return invertedPredicate.mgl_jsonExpressionObject;
|
return invertedPredicate.mgl_jsonExpressionObject;
|
||||||
} else {
|
} else {
|
||||||
[NSException raise:NSInvalidArgumentException format:@"“ALL” is only supported for the “!=” operator."];
|
[NSException raise:NSInvalidArgumentException
|
||||||
|
format:@"“ALL” is only supported for the “!=” operator."];
|
||||||
}
|
}
|
||||||
|
|
||||||
case NSAnyPredicateModifier:
|
case NSAnyPredicateModifier:
|
||||||
@@ -107,22 +116,27 @@
|
|||||||
if (self.predicateOperatorType == NSEqualToPredicateOperatorType) {
|
if (self.predicateOperatorType == NSEqualToPredicateOperatorType) {
|
||||||
// https://github.com/mapbox/mapbox-gl-js/issues/9339
|
// https://github.com/mapbox/mapbox-gl-js/issues/9339
|
||||||
if (self.options) {
|
if (self.options) {
|
||||||
[NSException raise:NSInvalidArgumentException format:@"NSComparisonPredicateOptions not supported for “ANY … =” comparisons."];
|
[NSException
|
||||||
|
raise:NSInvalidArgumentException
|
||||||
|
format:@"NSComparisonPredicateOptions not supported for “ANY … =” comparisons."];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSPredicate *directPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression
|
NSPredicate *directPredicate =
|
||||||
|
[NSComparisonPredicate predicateWithLeftExpression:self.rightExpression
|
||||||
rightExpression:self.leftExpression
|
rightExpression:self.leftExpression
|
||||||
modifier:NSDirectPredicateModifier
|
modifier:NSDirectPredicateModifier
|
||||||
type:NSInPredicateOperatorType
|
type:NSInPredicateOperatorType
|
||||||
options:self.options];
|
options:self.options];
|
||||||
return directPredicate.mgl_jsonExpressionObject;
|
return directPredicate.mgl_jsonExpressionObject;
|
||||||
} else {
|
} else {
|
||||||
[NSException raise:NSInvalidArgumentException format:@"“ANY” or “SOME” is only supported for the “=” operator."];
|
[NSException raise:NSInvalidArgumentException
|
||||||
|
format:@"“ANY” or “SOME” is only supported for the “=” operator."];
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"NSComparisonPredicateModifier:%lu is not supported.", (unsigned long)self.comparisonPredicateModifier];
|
format:@"NSComparisonPredicateModifier:%lu is not supported.",
|
||||||
|
(unsigned long)self.comparisonPredicateModifier];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *op;
|
NSString *op;
|
||||||
@@ -148,21 +162,25 @@
|
|||||||
case NSBetweenPredicateOperatorType: {
|
case NSBetweenPredicateOperatorType: {
|
||||||
op = @"all";
|
op = @"all";
|
||||||
NSArray *limits = self.rightExpression.constantValue;
|
NSArray *limits = self.rightExpression.constantValue;
|
||||||
NSPredicate *leftHandPredicate = [NSComparisonPredicate predicateWithLeftExpression:limits[0]
|
NSPredicate *leftHandPredicate = [NSComparisonPredicate
|
||||||
|
predicateWithLeftExpression:limits[0]
|
||||||
rightExpression:self.leftExpression
|
rightExpression:self.leftExpression
|
||||||
modifier:NSDirectPredicateModifier
|
modifier:NSDirectPredicateModifier
|
||||||
type:NSLessThanOrEqualToPredicateOperatorType
|
type:NSLessThanOrEqualToPredicateOperatorType
|
||||||
options:0];
|
options:0];
|
||||||
NSPredicate *rightHandPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.leftExpression
|
NSPredicate *rightHandPredicate = [NSComparisonPredicate
|
||||||
|
predicateWithLeftExpression:self.leftExpression
|
||||||
rightExpression:limits[1]
|
rightExpression:limits[1]
|
||||||
modifier:NSDirectPredicateModifier
|
modifier:NSDirectPredicateModifier
|
||||||
type:NSLessThanOrEqualToPredicateOperatorType
|
type:NSLessThanOrEqualToPredicateOperatorType
|
||||||
options:0];
|
options:0];
|
||||||
return @[op, leftHandPredicate.mgl_jsonExpressionObject, rightHandPredicate.mgl_jsonExpressionObject];
|
return @[
|
||||||
|
op, leftHandPredicate.mgl_jsonExpressionObject, rightHandPredicate.mgl_jsonExpressionObject
|
||||||
|
];
|
||||||
}
|
}
|
||||||
case NSInPredicateOperatorType: {
|
case NSInPredicateOperatorType: {
|
||||||
if (self.leftExpression.expressionType == NSEvaluatedObjectExpressionType) {
|
if (self.leftExpression.expressionType == NSEvaluatedObjectExpressionType) {
|
||||||
return @[@"within", self.rightExpression.mgl_jsonExpressionObject];
|
return @[ @"within", self.rightExpression.mgl_jsonExpressionObject ];
|
||||||
}
|
}
|
||||||
// An “in” expression comparing two string literals is unfortunately
|
// An “in” expression comparing two string literals is unfortunately
|
||||||
// misinterpreted as a legacy “in” filter due to ambiguity. Wrap one
|
// misinterpreted as a legacy “in” filter due to ambiguity. Wrap one
|
||||||
@@ -172,8 +190,10 @@
|
|||||||
self.rightExpression.expressionType == NSConstantValueExpressionType &&
|
self.rightExpression.expressionType == NSConstantValueExpressionType &&
|
||||||
[self.leftExpression.constantValue isKindOfClass:[NSString class]] &&
|
[self.leftExpression.constantValue isKindOfClass:[NSString class]] &&
|
||||||
[self.rightExpression.constantValue isKindOfClass:[NSString class]]) {
|
[self.rightExpression.constantValue isKindOfClass:[NSString class]]) {
|
||||||
NSExpression *rightLiteralExpression = [NSExpression expressionWithFormat:@"MLN_FUNCTION('literal', %@)", self.rightExpression];
|
NSExpression *rightLiteralExpression = [NSExpression
|
||||||
NSPredicate *literalPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.leftExpression
|
expressionWithFormat:@"MLN_FUNCTION('literal', %@)", self.rightExpression];
|
||||||
|
NSPredicate *literalPredicate =
|
||||||
|
[NSComparisonPredicate predicateWithLeftExpression:self.leftExpression
|
||||||
rightExpression:rightLiteralExpression
|
rightExpression:rightLiteralExpression
|
||||||
modifier:NSDirectPredicateModifier
|
modifier:NSDirectPredicateModifier
|
||||||
type:NSInPredicateOperatorType
|
type:NSInPredicateOperatorType
|
||||||
@@ -184,7 +204,8 @@
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NSContainsPredicateOperatorType: {
|
case NSContainsPredicateOperatorType: {
|
||||||
NSPredicate *inPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression
|
NSPredicate *inPredicate =
|
||||||
|
[NSComparisonPredicate predicateWithLeftExpression:self.rightExpression
|
||||||
rightExpression:self.leftExpression
|
rightExpression:self.leftExpression
|
||||||
modifier:self.comparisonPredicateModifier
|
modifier:self.comparisonPredicateModifier
|
||||||
type:NSInPredicateOperatorType
|
type:NSInPredicateOperatorType
|
||||||
@@ -197,18 +218,21 @@
|
|||||||
case NSEndsWithPredicateOperatorType:
|
case NSEndsWithPredicateOperatorType:
|
||||||
case NSCustomSelectorPredicateOperatorType:
|
case NSCustomSelectorPredicateOperatorType:
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"NSPredicateOperatorType:%lu is not supported.", (unsigned long)self.predicateOperatorType];
|
format:@"NSPredicateOperatorType:%lu is not supported.",
|
||||||
|
(unsigned long)self.predicateOperatorType];
|
||||||
}
|
}
|
||||||
if (!op) {
|
if (!op) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
NSArray *comparisonArray = @[op, self.leftExpression.mgl_jsonExpressionObject, self.rightExpression.mgl_jsonExpressionObject];
|
NSArray *comparisonArray = @[
|
||||||
|
op, self.leftExpression.mgl_jsonExpressionObject, self.rightExpression.mgl_jsonExpressionObject
|
||||||
|
];
|
||||||
if (self.options) {
|
if (self.options) {
|
||||||
NSDictionary *collatorObject = @{
|
NSDictionary *collatorObject = @{
|
||||||
@"case-sensitive": @(!(self.options & NSCaseInsensitivePredicateOption)),
|
@"case-sensitive" : @(!(self.options & NSCaseInsensitivePredicateOption)),
|
||||||
@"diacritic-sensitive": @(!(self.options & NSDiacriticInsensitivePredicateOption)),
|
@"diacritic-sensitive" : @(!(self.options & NSDiacriticInsensitivePredicateOption)),
|
||||||
};
|
};
|
||||||
return [comparisonArray arrayByAddingObject:@[@"collator", collatorObject]];
|
return [comparisonArray arrayByAddingObject:@[ @"collator", collatorObject ]];
|
||||||
}
|
}
|
||||||
return comparisonArray;
|
return comparisonArray;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,16 @@
|
|||||||
|
|
||||||
#import "MLNStyleValue_Private.h"
|
#import "MLNStyleValue_Private.h"
|
||||||
|
|
||||||
#import "NSPredicate+MLNPrivateAdditions.h"
|
|
||||||
#import "NSExpression+MLNPrivateAdditions.h"
|
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
#import "NSExpression+MLNPrivateAdditions.h"
|
||||||
|
#import "NSPredicate+MLNPrivateAdditions.h"
|
||||||
|
|
||||||
#include <mbgl/style/conversion/property_value.hpp>
|
#include <mbgl/style/conversion/property_value.hpp>
|
||||||
|
|
||||||
@implementation NSCompoundPredicate (MLNAdditions)
|
@implementation NSCompoundPredicate (MLNAdditions)
|
||||||
|
|
||||||
- (std::vector<mbgl::style::Filter>)mgl_subfilters
|
- (std::vector<mbgl::style::Filter>)mgl_subfilters {
|
||||||
{
|
std::vector<mbgl::style::Filter> filters;
|
||||||
std::vector<mbgl::style::Filter>filters;
|
|
||||||
for (NSPredicate *predicate in self.subpredicates) {
|
for (NSPredicate *predicate in self.subpredicates) {
|
||||||
filters.push_back(predicate.mgl_filter);
|
filters.push_back(predicate.mgl_filter);
|
||||||
}
|
}
|
||||||
@@ -26,24 +25,24 @@
|
|||||||
- (id)mgl_jsonExpressionObject {
|
- (id)mgl_jsonExpressionObject {
|
||||||
switch (self.compoundPredicateType) {
|
switch (self.compoundPredicateType) {
|
||||||
case NSNotPredicateType: {
|
case NSNotPredicateType: {
|
||||||
MLNAssert(self.subpredicates.count <= 1, @"NOT predicate cannot have multiple subpredicates.");
|
MLNAssert(self.subpredicates.count <= 1,
|
||||||
|
@"NOT predicate cannot have multiple subpredicates.");
|
||||||
NSPredicate *subpredicate = self.subpredicates.firstObject;
|
NSPredicate *subpredicate = self.subpredicates.firstObject;
|
||||||
return @[@"!", subpredicate.mgl_jsonExpressionObject];
|
return @[ @"!", subpredicate.mgl_jsonExpressionObject ];
|
||||||
}
|
}
|
||||||
|
|
||||||
case NSAndPredicateType: {
|
case NSAndPredicateType: {
|
||||||
NSArray *subarrays = [self.subpredicates valueForKeyPath:@"mgl_jsonExpressionObject"];
|
NSArray *subarrays = [self.subpredicates valueForKeyPath:@"mgl_jsonExpressionObject"];
|
||||||
return [@[@"all"] arrayByAddingObjectsFromArray:subarrays];
|
return [@[ @"all" ] arrayByAddingObjectsFromArray:subarrays];
|
||||||
}
|
}
|
||||||
|
|
||||||
case NSOrPredicateType: {
|
case NSOrPredicateType: {
|
||||||
NSArray *subarrays = [self.subpredicates valueForKeyPath:@"mgl_jsonExpressionObject"];
|
NSArray *subarrays = [self.subpredicates valueForKeyPath:@"mgl_jsonExpressionObject"];
|
||||||
return [@[@"any"] arrayByAddingObjectsFromArray:subarrays];
|
return [@[ @"any" ] arrayByAddingObjectsFromArray:subarrays];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[NSException raise:@"Compound predicate type not handled"
|
[NSException raise:@"Compound predicate type not handled" format:@""];
|
||||||
format:@""];
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
#import "NSDate+MLNAdditions.h"
|
|
||||||
#import <ratio>
|
#import <ratio>
|
||||||
|
#import "NSDate+MLNAdditions.h"
|
||||||
|
|
||||||
mbgl::Duration MLNDurationFromTimeInterval(NSTimeInterval duration)
|
mbgl::Duration MLNDurationFromTimeInterval(NSTimeInterval duration) {
|
||||||
{
|
return std::chrono::duration_cast<mbgl::Duration>(
|
||||||
return std::chrono::duration_cast<mbgl::Duration>(std::chrono::duration<NSTimeInterval>(duration));
|
std::chrono::duration<NSTimeInterval>(duration));
|
||||||
}
|
}
|
||||||
|
|
||||||
NSTimeInterval MLNTimeIntervalFromDuration(mbgl::Duration duration)
|
NSTimeInterval MLNTimeIntervalFromDuration(mbgl::Duration duration) {
|
||||||
{
|
|
||||||
return std::chrono::duration<NSTimeInterval, std::ratio<1>>(duration).count();
|
return std::chrono::duration<NSTimeInterval, std::ratio<1>>(duration).count();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#import "NSDictionary+MLNAdditions.h"
|
#import "NSDictionary+MLNAdditions.h"
|
||||||
|
|
||||||
#import "NSExpression+MLNPrivateAdditions.h"
|
|
||||||
#import "NSArray+MLNAdditions.h"
|
#import "NSArray+MLNAdditions.h"
|
||||||
|
#import "NSExpression+MLNPrivateAdditions.h"
|
||||||
|
|
||||||
@implementation NSDictionary (MLNAdditions)
|
@implementation NSDictionary (MLNAdditions)
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,18 @@
|
|||||||
#import "NSPredicate+MLNPrivateAdditions.h"
|
#import "NSPredicate+MLNPrivateAdditions.h"
|
||||||
|
|
||||||
#import "MLNValueEvaluator.h"
|
|
||||||
#import "MLNStyleValue_Private.h"
|
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
#import "MLNStyleValue_Private.h"
|
||||||
|
#import "MLNValueEvaluator.h"
|
||||||
|
|
||||||
#include <mbgl/style/conversion/filter.hpp>
|
#include <mbgl/style/conversion/filter.hpp>
|
||||||
|
|
||||||
@implementation NSPredicate (MLNPrivateAdditions)
|
@implementation NSPredicate (MLNPrivateAdditions)
|
||||||
|
|
||||||
- (mbgl::style::Filter)mgl_filter
|
- (mbgl::style::Filter)mgl_filter {
|
||||||
{
|
|
||||||
mbgl::style::conversion::Error valueError;
|
mbgl::style::conversion::Error valueError;
|
||||||
NSArray *jsonObject = self.mgl_jsonExpressionObject;
|
NSArray *jsonObject = self.mgl_jsonExpressionObject;
|
||||||
auto value = mbgl::style::conversion::convert<mbgl::style::Filter>(mbgl::style::conversion::makeConvertible(jsonObject), valueError);
|
auto value = mbgl::style::conversion::convert<mbgl::style::Filter>(
|
||||||
|
mbgl::style::conversion::makeConvertible(jsonObject), valueError);
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
@@ -24,8 +24,7 @@
|
|||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter
|
+ (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter {
|
||||||
{
|
|
||||||
if (filter.expression) {
|
if (filter.expression) {
|
||||||
id jsonObject = MLNJSONObjectFromMBGLExpression(**filter.expression);
|
id jsonObject = MLNJSONObjectFromMBGLExpression(**filter.expression);
|
||||||
return [NSPredicate predicateWithMLNJSONObject:jsonObject];
|
return [NSPredicate predicateWithMLNJSONObject:jsonObject];
|
||||||
@@ -47,14 +46,14 @@ NSArray *MLNSubpredicatesWithJSONObjects(NSArray *objects) {
|
|||||||
return subpredicates;
|
return subpredicates;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NSDictionary * const MLNPredicateOperatorTypesByJSONOperator = @{
|
static NSDictionary *const MLNPredicateOperatorTypesByJSONOperator = @{
|
||||||
@"==": @(NSEqualToPredicateOperatorType),
|
@"==" : @(NSEqualToPredicateOperatorType),
|
||||||
@"!=": @(NSNotEqualToPredicateOperatorType),
|
@"!=" : @(NSNotEqualToPredicateOperatorType),
|
||||||
@"<": @(NSLessThanPredicateOperatorType),
|
@"<" : @(NSLessThanPredicateOperatorType),
|
||||||
@"<=": @(NSLessThanOrEqualToPredicateOperatorType),
|
@"<=" : @(NSLessThanOrEqualToPredicateOperatorType),
|
||||||
@">": @(NSGreaterThanPredicateOperatorType),
|
@">" : @(NSGreaterThanPredicateOperatorType),
|
||||||
@">=": @(NSGreaterThanOrEqualToPredicateOperatorType),
|
@">=" : @(NSGreaterThanOrEqualToPredicateOperatorType),
|
||||||
@"in": @(NSInPredicateOperatorType),
|
@"in" : @(NSInPredicateOperatorType),
|
||||||
};
|
};
|
||||||
|
|
||||||
+ (instancetype)predicateWithMLNJSONObject:(id)object {
|
+ (instancetype)predicateWithMLNJSONObject:(id)object {
|
||||||
@@ -65,21 +64,25 @@ static NSDictionary * const MLNPredicateOperatorTypesByJSONOperator = @{
|
|||||||
return [NSPredicate predicateWithValue:NO];
|
return [NSPredicate predicateWithValue:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNAssert([object isKindOfClass:[NSArray class]], @"Condition for case expression should be an expression.");
|
MLNAssert([object isKindOfClass:[NSArray class]],
|
||||||
|
@"Condition for case expression should be an expression.");
|
||||||
NSArray *objects = (NSArray *)object;
|
NSArray *objects = (NSArray *)object;
|
||||||
NSString *op = objects.firstObject;
|
NSString *op = objects.firstObject;
|
||||||
|
|
||||||
NSNumber *operatorTypeNumber = MLNPredicateOperatorTypesByJSONOperator[op];
|
NSNumber *operatorTypeNumber = MLNPredicateOperatorTypesByJSONOperator[op];
|
||||||
if (operatorTypeNumber) {
|
if (operatorTypeNumber) {
|
||||||
NSPredicateOperatorType operatorType = (NSPredicateOperatorType)[operatorTypeNumber unsignedIntegerValue];
|
NSPredicateOperatorType operatorType =
|
||||||
|
(NSPredicateOperatorType)[operatorTypeNumber unsignedIntegerValue];
|
||||||
|
|
||||||
NSComparisonPredicateOptions options = 0;
|
NSComparisonPredicateOptions options = 0;
|
||||||
if (objects.count > 3) {
|
if (objects.count > 3) {
|
||||||
NSArray *collatorExpression = objects[3];
|
NSArray *collatorExpression = objects[3];
|
||||||
MLNCAssert([collatorExpression isKindOfClass:[NSArray class]], @"Collators must be dictionaries.");
|
MLNCAssert([collatorExpression isKindOfClass:[NSArray class]],
|
||||||
|
@"Collators must be dictionaries.");
|
||||||
MLNCAssert(collatorExpression.count == 2, @"Malformed collator expression");
|
MLNCAssert(collatorExpression.count == 2, @"Malformed collator expression");
|
||||||
NSDictionary *collator = collatorExpression[1];
|
NSDictionary *collator = collatorExpression[1];
|
||||||
MLNCAssert([collator isKindOfClass:[NSDictionary class]], @"Malformed collator in collator expression");
|
MLNCAssert([collator isKindOfClass:[NSDictionary class]],
|
||||||
|
@"Malformed collator in collator expression");
|
||||||
|
|
||||||
// Predicate options can’t express specific locales as collators can.
|
// Predicate options can’t express specific locales as collators can.
|
||||||
if (!collator[@"locale"]) {
|
if (!collator[@"locale"]) {
|
||||||
@@ -92,7 +95,8 @@ static NSDictionary * const MLNPredicateOperatorTypesByJSONOperator = @{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSArray *subexpressions = MLNSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
|
NSArray *subexpressions = MLNSubexpressionsWithJSONObjects(
|
||||||
|
[objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
|
||||||
return [NSComparisonPredicate predicateWithLeftExpression:subexpressions[0]
|
return [NSComparisonPredicate predicateWithLeftExpression:subexpressions[0]
|
||||||
rightExpression:subexpressions[1]
|
rightExpression:subexpressions[1]
|
||||||
modifier:NSDirectPredicateModifier
|
modifier:NSDirectPredicateModifier
|
||||||
@@ -101,9 +105,11 @@ static NSDictionary * const MLNPredicateOperatorTypesByJSONOperator = @{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ([op isEqualToString:@"!"]) {
|
if ([op isEqualToString:@"!"]) {
|
||||||
NSArray *subpredicates = MLNSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
|
NSArray *subpredicates = MLNSubpredicatesWithJSONObjects(
|
||||||
|
[objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
|
||||||
if (subpredicates.count > 1) {
|
if (subpredicates.count > 1) {
|
||||||
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];
|
NSCompoundPredicate *predicate =
|
||||||
|
[NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];
|
||||||
return [NSCompoundPredicate notPredicateWithSubpredicate:predicate];
|
return [NSCompoundPredicate notPredicateWithSubpredicate:predicate];
|
||||||
}
|
}
|
||||||
if (subpredicates.count) {
|
if (subpredicates.count) {
|
||||||
@@ -112,7 +118,8 @@ static NSDictionary * const MLNPredicateOperatorTypesByJSONOperator = @{
|
|||||||
return [NSPredicate predicateWithValue:YES];
|
return [NSPredicate predicateWithValue:YES];
|
||||||
}
|
}
|
||||||
if ([op isEqualToString:@"all"]) {
|
if ([op isEqualToString:@"all"]) {
|
||||||
NSArray<NSPredicate *> *subpredicates = MLNSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
|
NSArray<NSPredicate *> *subpredicates = MLNSubpredicatesWithJSONObjects(
|
||||||
|
[objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
|
||||||
if (subpredicates.count == 2) {
|
if (subpredicates.count == 2) {
|
||||||
// Determine if the expression is of BETWEEN type
|
// Determine if the expression is of BETWEEN type
|
||||||
if ([subpredicates[0] isKindOfClass:[NSComparisonPredicate class]] &&
|
if ([subpredicates[0] isKindOfClass:[NSComparisonPredicate class]] &&
|
||||||
@@ -123,41 +130,51 @@ static NSDictionary * const MLNPredicateOperatorTypesByJSONOperator = @{
|
|||||||
NSArray *limits;
|
NSArray *limits;
|
||||||
NSExpression *leftConditionExpression;
|
NSExpression *leftConditionExpression;
|
||||||
|
|
||||||
if(leftCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType &&
|
if (leftCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType &&
|
||||||
rightCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType) {
|
rightCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType) {
|
||||||
limits = @[leftCondition.rightExpression, rightCondition.rightExpression];
|
limits = @[ leftCondition.rightExpression, rightCondition.rightExpression ];
|
||||||
leftConditionExpression = leftCondition.leftExpression;
|
leftConditionExpression = leftCondition.leftExpression;
|
||||||
|
|
||||||
} else if (leftCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType &&
|
} else if (leftCondition.predicateOperatorType ==
|
||||||
rightCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType) {
|
NSLessThanOrEqualToPredicateOperatorType &&
|
||||||
limits = @[leftCondition.leftExpression, rightCondition.rightExpression];
|
rightCondition.predicateOperatorType ==
|
||||||
|
NSLessThanOrEqualToPredicateOperatorType) {
|
||||||
|
limits = @[ leftCondition.leftExpression, rightCondition.rightExpression ];
|
||||||
leftConditionExpression = leftCondition.rightExpression;
|
leftConditionExpression = leftCondition.rightExpression;
|
||||||
|
|
||||||
} else if(leftCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType &&
|
} else if (leftCondition.predicateOperatorType ==
|
||||||
rightCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType) {
|
NSLessThanOrEqualToPredicateOperatorType &&
|
||||||
limits = @[leftCondition.leftExpression, rightCondition.leftExpression];
|
rightCondition.predicateOperatorType ==
|
||||||
|
NSGreaterThanOrEqualToPredicateOperatorType) {
|
||||||
|
limits = @[ leftCondition.leftExpression, rightCondition.leftExpression ];
|
||||||
leftConditionExpression = leftCondition.rightExpression;
|
leftConditionExpression = leftCondition.rightExpression;
|
||||||
|
|
||||||
} else if(leftCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType &&
|
} else if (leftCondition.predicateOperatorType ==
|
||||||
rightCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType) {
|
NSGreaterThanOrEqualToPredicateOperatorType &&
|
||||||
limits = @[leftCondition.rightExpression, rightCondition.leftExpression];
|
rightCondition.predicateOperatorType ==
|
||||||
|
NSGreaterThanOrEqualToPredicateOperatorType) {
|
||||||
|
limits = @[ leftCondition.rightExpression, rightCondition.leftExpression ];
|
||||||
leftConditionExpression = leftCondition.leftExpression;
|
leftConditionExpression = leftCondition.leftExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (limits && leftConditionExpression) {
|
if (limits && leftConditionExpression) {
|
||||||
return [NSPredicate predicateWithFormat:@"%@ BETWEEN %@", leftConditionExpression, [NSExpression expressionForAggregate:limits]];
|
return [NSPredicate predicateWithFormat:@"%@ BETWEEN %@", leftConditionExpression,
|
||||||
|
[NSExpression expressionForAggregate:limits]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
|
return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
|
||||||
}
|
}
|
||||||
if ([op isEqualToString:@"any"]) {
|
if ([op isEqualToString:@"any"]) {
|
||||||
NSArray *subpredicates = MLNSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
|
NSArray *subpredicates = MLNSubpredicatesWithJSONObjects(
|
||||||
|
[objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
|
||||||
return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];
|
return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];
|
||||||
}
|
}
|
||||||
if ([op isEqualToString:@"within"]) {
|
if ([op isEqualToString:@"within"]) {
|
||||||
NSArray *subexpressions = MLNSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
|
NSArray *subexpressions = MLNSubexpressionsWithJSONObjects(
|
||||||
return [NSComparisonPredicate predicateWithLeftExpression:[NSExpression expressionForEvaluatedObject]
|
[objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
|
||||||
|
return [NSComparisonPredicate
|
||||||
|
predicateWithLeftExpression:[NSExpression expressionForEvaluatedObject]
|
||||||
rightExpression:subexpressions[0]
|
rightExpression:subexpressions[0]
|
||||||
modifier:NSDirectPredicateModifier
|
modifier:NSDirectPredicateModifier
|
||||||
type:NSInPredicateOperatorType
|
type:NSInPredicateOperatorType
|
||||||
@@ -165,12 +182,12 @@ static NSDictionary * const MLNPredicateOperatorTypesByJSONOperator = @{
|
|||||||
}
|
}
|
||||||
|
|
||||||
NSExpression *expression = [NSExpression expressionWithMLNJSONObject:object];
|
NSExpression *expression = [NSExpression expressionWithMLNJSONObject:object];
|
||||||
return [NSComparisonPredicate predicateWithLeftExpression:expression
|
return [NSComparisonPredicate
|
||||||
|
predicateWithLeftExpression:expression
|
||||||
rightExpression:[NSExpression expressionForConstantValue:@YES]
|
rightExpression:[NSExpression expressionForConstantValue:@YES]
|
||||||
modifier:NSDirectPredicateModifier
|
modifier:NSDirectPredicateModifier
|
||||||
type:NSEqualToPredicateOperatorType
|
type:NSEqualToPredicateOperatorType
|
||||||
options:0];
|
options:0];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)mgl_jsonExpressionObject {
|
- (id)mgl_jsonExpressionObject {
|
||||||
@@ -186,8 +203,7 @@ static NSDictionary * const MLNPredicateOperatorTypesByJSONOperator = @{
|
|||||||
format:@"Block-based predicates are not supported."];
|
format:@"Block-based predicates are not supported."];
|
||||||
}
|
}
|
||||||
|
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException format:@"Unrecognized predicate type."];
|
||||||
format:@"Unrecognized predicate type."];
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +212,6 @@ static NSDictionary * const MLNPredicateOperatorTypesByJSONOperator = @{
|
|||||||
@implementation NSPredicate (MLNExpressionAdditions)
|
@implementation NSPredicate (MLNExpressionAdditions)
|
||||||
|
|
||||||
- (id)mgl_if:(id)firstValue, ... {
|
- (id)mgl_if:(id)firstValue, ... {
|
||||||
|
|
||||||
if ([self evaluateWithObject:nil]) {
|
if ([self evaluateWithObject:nil]) {
|
||||||
return firstValue;
|
return firstValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
#import "NSValue+MLNStyleAttributeAdditions.h"
|
#import "MLNGeometry_Private.h"
|
||||||
#import "MLNLight.h"
|
#import "MLNLight.h"
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
#import "MLNGeometry_Private.h"
|
#import "NSValue+MLNStyleAttributeAdditions.h"
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@implementation NSValue (MLNStyleAttributeAdditions)
|
@implementation NSValue (MLNStyleAttributeAdditions)
|
||||||
|
|
||||||
+ (instancetype)mgl_valueWithOffsetArray:(std::array<float, 2>)offsetArray
|
+ (instancetype)mgl_valueWithOffsetArray:(std::array<float, 2>)offsetArray {
|
||||||
{
|
|
||||||
CGVector vector = CGVectorMake(offsetArray[0], offsetArray[1]);
|
CGVector vector = CGVectorMake(offsetArray[0], offsetArray[1]);
|
||||||
#if !TARGET_OS_IPHONE
|
#if !TARGET_OS_IPHONE
|
||||||
// Style specification assumes an origin at the upper-left corner.
|
// Style specification assumes an origin at the upper-left corner.
|
||||||
@@ -19,8 +18,7 @@
|
|||||||
return [NSValue value:&vector withObjCType:@encode(CGVector)];
|
return [NSValue value:&vector withObjCType:@encode(CGVector)];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (instancetype)mgl_valueWithPaddingArray:(std::array<float, 4>)paddingArray
|
+ (instancetype)mgl_valueWithPaddingArray:(std::array<float, 4>)paddingArray {
|
||||||
{
|
|
||||||
// Style specification defines padding in clockwise order: top, right, bottom, left.
|
// Style specification defines padding in clockwise order: top, right, bottom, left.
|
||||||
// Foundation defines padding in counterclockwise order: top, left, bottom, right.
|
// Foundation defines padding in counterclockwise order: top, left, bottom, right.
|
||||||
MLNEdgeInsets insets = {
|
MLNEdgeInsets insets = {
|
||||||
@@ -32,8 +30,7 @@
|
|||||||
return [NSValue value:&insets withObjCType:@encode(MLNEdgeInsets)];
|
return [NSValue value:&insets withObjCType:@encode(MLNEdgeInsets)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (std::array<float, 2>)mgl_offsetArrayValue
|
- (std::array<float, 2>)mgl_offsetArrayValue {
|
||||||
{
|
|
||||||
MLNAssert(strcmp(self.objCType, @encode(CGVector)) == 0, @"Value does not represent a CGVector");
|
MLNAssert(strcmp(self.objCType, @encode(CGVector)) == 0, @"Value does not represent a CGVector");
|
||||||
CGVector vector;
|
CGVector vector;
|
||||||
[self getValue:&vector];
|
[self getValue:&vector];
|
||||||
@@ -46,9 +43,9 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
- (std::array<float, 4>)mgl_paddingArrayValue
|
- (std::array<float, 4>)mgl_paddingArrayValue {
|
||||||
{
|
MLNAssert(strcmp(self.objCType, @encode(MLNEdgeInsets)) == 0,
|
||||||
MLNAssert(strcmp(self.objCType, @encode(MLNEdgeInsets)) == 0, @"Value does not represent an NSEdgeInsets/UIEdgeInsets");
|
@"Value does not represent an NSEdgeInsets/UIEdgeInsets");
|
||||||
MLNEdgeInsets insets;
|
MLNEdgeInsets insets;
|
||||||
[self getValue:&insets];
|
[self getValue:&insets];
|
||||||
// Style specification defines padding in clockwise order: top, right, bottom, left.
|
// Style specification defines padding in clockwise order: top, right, bottom, left.
|
||||||
@@ -60,9 +57,9 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
- (std::array<float, 3>)mgl_lightPositionArrayValue
|
- (std::array<float, 3>)mgl_lightPositionArrayValue {
|
||||||
{
|
MLNAssert(strcmp(self.objCType, @encode(MLNSphericalPosition)) == 0,
|
||||||
MLNAssert(strcmp(self.objCType, @encode(MLNSphericalPosition)) == 0, @"Value does not represent an MLNSphericalPosition");
|
@"Value does not represent an MLNSphericalPosition");
|
||||||
MLNSphericalPosition lightPosition;
|
MLNSphericalPosition lightPosition;
|
||||||
[self getValue:&lightPosition];
|
[self getValue:&lightPosition];
|
||||||
// Style specification defines padding in clockwise order: top, right, bottom, left.
|
// Style specification defines padding in clockwise order: top, right, bottom, left.
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
#include <mbgl/gl/headless_backend.hpp>
|
#include <mbgl/gl/headless_backend.hpp>
|
||||||
#include <mbgl/util/logging.hpp>
|
#include <mbgl/util/logging.hpp>
|
||||||
|
|
||||||
#include <OpenGL/OpenGL.h>
|
|
||||||
#include <Foundation/NSString.h>
|
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#include <Foundation/NSString.h>
|
||||||
|
#include <OpenGL/OpenGL.h>
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace mbgl {
|
namespace mbgl {
|
||||||
namespace gl {
|
namespace gl {
|
||||||
@@ -16,7 +16,9 @@ namespace gl {
|
|||||||
class CGLDisplayConfig {
|
class CGLDisplayConfig {
|
||||||
private:
|
private:
|
||||||
// Key for singleton construction.
|
// Key for singleton construction.
|
||||||
struct Key { explicit Key() = default; };
|
struct Key {
|
||||||
|
explicit Key() = default;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CGLDisplayConfig(Key) {
|
CGLDisplayConfig(Key) {
|
||||||
@@ -24,23 +26,22 @@ public:
|
|||||||
// If it is, use kCGLOGLPVersion_3_2_Core and enable that extension.
|
// If it is, use kCGLOGLPVersion_3_2_Core and enable that extension.
|
||||||
CGLPixelFormatAttribute attributes[] = {
|
CGLPixelFormatAttribute attributes[] = {
|
||||||
kCGLPFAOpenGLProfile, static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_Legacy),
|
kCGLPFAOpenGLProfile, static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_Legacy),
|
||||||
// Not adding kCGLPFAAccelerated, as this will make headless rendering impossible when running in VMs.
|
// Not adding kCGLPFAAccelerated, as this will make headless rendering impossible when
|
||||||
kCGLPFAClosestPolicy,
|
// running in VMs.
|
||||||
kCGLPFAAccumSize, static_cast<CGLPixelFormatAttribute>(32),
|
kCGLPFAClosestPolicy, kCGLPFAAccumSize, static_cast<CGLPixelFormatAttribute>(32),
|
||||||
kCGLPFAColorSize, static_cast<CGLPixelFormatAttribute>(24),
|
kCGLPFAColorSize, static_cast<CGLPixelFormatAttribute>(24), kCGLPFAAlphaSize,
|
||||||
kCGLPFAAlphaSize, static_cast<CGLPixelFormatAttribute>(8),
|
static_cast<CGLPixelFormatAttribute>(8), kCGLPFADepthSize,
|
||||||
kCGLPFADepthSize, static_cast<CGLPixelFormatAttribute>(16),
|
static_cast<CGLPixelFormatAttribute>(16), kCGLPFAStencilSize,
|
||||||
kCGLPFAStencilSize, static_cast<CGLPixelFormatAttribute>(8),
|
static_cast<CGLPixelFormatAttribute>(8), kCGLPFASupportsAutomaticGraphicsSwitching,
|
||||||
kCGLPFASupportsAutomaticGraphicsSwitching,
|
|
||||||
kCGLPFAAllowOfflineRenderers, // Allows using the integrated GPU
|
kCGLPFAAllowOfflineRenderers, // Allows using the integrated GPU
|
||||||
static_cast<CGLPixelFormatAttribute>(0)
|
static_cast<CGLPixelFormatAttribute>(0)};
|
||||||
};
|
|
||||||
|
|
||||||
GLint num;
|
GLint num;
|
||||||
// TODO: obtain all configurations and choose the best one.
|
// TODO: obtain all configurations and choose the best one.
|
||||||
const CGLError error = CGLChoosePixelFormat(attributes, &pixelFormat, &num);
|
const CGLError error = CGLChoosePixelFormat(attributes, &pixelFormat, &num);
|
||||||
if (error != kCGLNoError) {
|
if (error != kCGLNoError) {
|
||||||
throw std::runtime_error(std::string("Error choosing pixel format:") + CGLErrorString(error) + "\n");
|
throw std::runtime_error(std::string("Error choosing pixel format:") + CGLErrorString(error) +
|
||||||
|
"\n");
|
||||||
}
|
}
|
||||||
if (num <= 0) {
|
if (num <= 0) {
|
||||||
throw std::runtime_error("No pixel formats found.");
|
throw std::runtime_error("No pixel formats found.");
|
||||||
@@ -50,7 +51,8 @@ public:
|
|||||||
~CGLDisplayConfig() {
|
~CGLDisplayConfig() {
|
||||||
const CGLError error = CGLDestroyPixelFormat(pixelFormat);
|
const CGLError error = CGLDestroyPixelFormat(pixelFormat);
|
||||||
if (error != kCGLNoError) {
|
if (error != kCGLNoError) {
|
||||||
Log::Error(Event::OpenGL, std::string("Error destroying pixel format:") + CGLErrorString(error));
|
Log::Error(Event::OpenGL,
|
||||||
|
std::string("Error destroying pixel format:") + CGLErrorString(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,9 +85,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~CGLBackendImpl() final {
|
~CGLBackendImpl() final { CGLDestroyContext(glContext); }
|
||||||
CGLDestroyContext(glContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
gl::ProcAddress getExtensionFunctionPointer(const char* name) final {
|
gl::ProcAddress getExtensionFunctionPointer(const char* name) final {
|
||||||
static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
|
static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
|
||||||
|
|||||||
@@ -30,13 +30,9 @@ public:
|
|||||||
framework, (__bridge CFStringRef)[NSString stringWithUTF8String:name]));
|
framework, (__bridge CFStringRef)[NSString stringWithUTF8String:name]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void activateContext() final {
|
void activateContext() final { [EAGLContext setCurrentContext:glContext]; }
|
||||||
[EAGLContext setCurrentContext:glContext];
|
|
||||||
}
|
|
||||||
|
|
||||||
void deactivateContext() final {
|
void deactivateContext() final { [EAGLContext setCurrentContext:nil]; }
|
||||||
[EAGLContext setCurrentContext:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EAGLContext* glContext = nullptr;
|
EAGLContext* glContext = nullptr;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#import <Mapbox.h>
|
#import <Mapbox.h>
|
||||||
#import <XCTest/XCTest.h>
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
#import "MLNFoundation_Private.h"
|
|
||||||
#import "MLNCluster.h"
|
#import "MLNCluster.h"
|
||||||
|
#import "MLNFoundation_Private.h"
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
#import "MLNUserLocation_Private.h"
|
#import "MLNUserLocation_Private.h"
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
pointFeature.title = @"title";
|
pointFeature.title = @"title";
|
||||||
pointFeature.subtitle = @"subtitle";
|
pointFeature.subtitle = @"subtitle";
|
||||||
pointFeature.identifier = @(123);
|
pointFeature.identifier = @(123);
|
||||||
pointFeature.attributes = @{@"bbox": @[@1, @2, @3, @4]};
|
pointFeature.attributes = @{@"bbox" : @[ @1, @2, @3, @4 ]};
|
||||||
|
|
||||||
NSString *filePath = [self temporaryFilePathForClass:MLNPointFeature.class];
|
NSString *filePath = [self temporaryFilePathForClass:MLNPointFeature.class];
|
||||||
[NSKeyedArchiver archiveRootObject:pointFeature toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:pointFeature toFile:filePath];
|
||||||
@@ -73,16 +73,15 @@
|
|||||||
XCTAssert(cluster.clusterPointCount == 2);
|
XCTAssert(cluster.clusterPointCount == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)testPolyline {
|
- (void)testPolyline {
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {
|
||||||
CLLocationCoordinate2DMake(0.129631234123, 1.7812739312551),
|
CLLocationCoordinate2DMake(0.129631234123, 1.7812739312551),
|
||||||
CLLocationCoordinate2DMake(2.532083092342, 3.5216418292392)
|
CLLocationCoordinate2DMake(2.532083092342, 3.5216418292392)};
|
||||||
};
|
|
||||||
|
|
||||||
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
|
|
||||||
MLNPolyline *polyline = [MLNPolyline polylineWithCoordinates:coordinates count:numberOfCoordinates];
|
MLNPolyline *polyline = [MLNPolyline polylineWithCoordinates:coordinates
|
||||||
|
count:numberOfCoordinates];
|
||||||
polyline.title = @"title";
|
polyline.title = @"title";
|
||||||
polyline.subtitle = @"subtitle";
|
polyline.subtitle = @"subtitle";
|
||||||
|
|
||||||
@@ -92,9 +91,7 @@
|
|||||||
|
|
||||||
XCTAssertEqualObjects(polyline, unarchivedPolyline);
|
XCTAssertEqualObjects(polyline, unarchivedPolyline);
|
||||||
|
|
||||||
CLLocationCoordinate2D otherCoordinates[] = {
|
CLLocationCoordinate2D otherCoordinates[] = {CLLocationCoordinate2DMake(-1, -2)};
|
||||||
CLLocationCoordinate2DMake(-1, -2)
|
|
||||||
};
|
|
||||||
|
|
||||||
[unarchivedPolyline replaceCoordinatesInRange:NSMakeRange(0, 1) withCoordinates:otherCoordinates];
|
[unarchivedPolyline replaceCoordinatesInRange:NSMakeRange(0, 1) withCoordinates:otherCoordinates];
|
||||||
|
|
||||||
@@ -106,8 +103,10 @@
|
|||||||
CLLocationCoordinate2DMake(51.000000, 2.000000),
|
CLLocationCoordinate2DMake(51.000000, 2.000000),
|
||||||
};
|
};
|
||||||
|
|
||||||
NSUInteger multiLineCoordinatesCount = sizeof(multiLineCoordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger multiLineCoordinatesCount =
|
||||||
MLNPolyline *multiLine = [MLNPolyline polylineWithCoordinates:multiLineCoordinates count:multiLineCoordinatesCount];
|
sizeof(multiLineCoordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
|
MLNPolyline *multiLine = [MLNPolyline polylineWithCoordinates:multiLineCoordinates
|
||||||
|
count:multiLineCoordinatesCount];
|
||||||
CLLocationCoordinate2D multiLineCenter = CLLocationCoordinate2DMake(51.000000, 1.000000);
|
CLLocationCoordinate2D multiLineCenter = CLLocationCoordinate2DMake(51.000000, 1.000000);
|
||||||
|
|
||||||
XCTAssertEqual([multiLine coordinate].latitude, multiLineCenter.latitude);
|
XCTAssertEqual([multiLine coordinate].latitude, multiLineCenter.latitude);
|
||||||
@@ -119,7 +118,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
NSUInteger segmentCoordinatesCount = sizeof(segmentCoordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger segmentCoordinatesCount = sizeof(segmentCoordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
MLNPolyline *segmentLine = [MLNPolyline polylineWithCoordinates:segmentCoordinates count:segmentCoordinatesCount];
|
MLNPolyline *segmentLine = [MLNPolyline polylineWithCoordinates:segmentCoordinates
|
||||||
|
count:segmentCoordinatesCount];
|
||||||
CLLocationCoordinate2D segmentCenter = CLLocationCoordinate2DMake(35.0404006631, -85.2604935);
|
CLLocationCoordinate2D segmentCenter = CLLocationCoordinate2DMake(35.0404006631, -85.2604935);
|
||||||
|
|
||||||
XCTAssertEqualWithAccuracy([segmentLine coordinate].latitude, segmentCenter.latitude, 0.0001);
|
XCTAssertEqualWithAccuracy([segmentLine coordinate].latitude, segmentCenter.latitude, 0.0001);
|
||||||
@@ -132,22 +132,25 @@
|
|||||||
CLLocationCoordinate2DMake(37.862172, -122.262700),
|
CLLocationCoordinate2DMake(37.862172, -122.262700),
|
||||||
};
|
};
|
||||||
|
|
||||||
NSUInteger sfToBerkeleyCoordinatesCount = sizeof(sfToBerkeleyCoordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger sfToBerkeleyCoordinatesCount =
|
||||||
MLNPolyline *sfToBerkeleyLine = [MLNPolyline polylineWithCoordinates:sfToBerkeleyCoordinates count:sfToBerkeleyCoordinatesCount];
|
sizeof(sfToBerkeleyCoordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
CLLocationCoordinate2D sfToBerkeleyCenter = CLLocationCoordinate2DMake(37.8230575118,-122.324867587);
|
MLNPolyline *sfToBerkeleyLine =
|
||||||
|
[MLNPolyline polylineWithCoordinates:sfToBerkeleyCoordinates
|
||||||
XCTAssertEqualWithAccuracy([sfToBerkeleyLine coordinate].latitude, sfToBerkeleyCenter.latitude, 0.0001);
|
count:sfToBerkeleyCoordinatesCount];
|
||||||
XCTAssertEqualWithAccuracy([sfToBerkeleyLine coordinate].longitude, sfToBerkeleyCenter.longitude, 0.0001);
|
CLLocationCoordinate2D sfToBerkeleyCenter =
|
||||||
|
CLLocationCoordinate2DMake(37.8230575118, -122.324867587);
|
||||||
|
|
||||||
|
XCTAssertEqualWithAccuracy([sfToBerkeleyLine coordinate].latitude, sfToBerkeleyCenter.latitude,
|
||||||
|
0.0001);
|
||||||
|
XCTAssertEqualWithAccuracy([sfToBerkeleyLine coordinate].longitude, sfToBerkeleyCenter.longitude,
|
||||||
|
0.0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testPolygon {
|
- (void)testPolygon {
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {CLLocationCoordinate2DMake(35.090745, -85.300259),
|
||||||
CLLocationCoordinate2DMake(35.090745, -85.300259),
|
|
||||||
CLLocationCoordinate2DMake(35.092035, -85.298885),
|
CLLocationCoordinate2DMake(35.092035, -85.298885),
|
||||||
CLLocationCoordinate2DMake(35.090639, -85.297416),
|
CLLocationCoordinate2DMake(35.090639, -85.297416),
|
||||||
CLLocationCoordinate2DMake(35.089112, -85.298928)
|
CLLocationCoordinate2DMake(35.089112, -85.298928)};
|
||||||
};
|
|
||||||
|
|
||||||
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
|
|
||||||
@@ -171,31 +174,31 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
NSUInteger squareCoordinatesCount = sizeof(squareCoordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger squareCoordinatesCount = sizeof(squareCoordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
MLNPolygon *squarePolygon = [MLNPolygon polygonWithCoordinates:squareCoordinates count:squareCoordinatesCount];
|
MLNPolygon *squarePolygon = [MLNPolygon polygonWithCoordinates:squareCoordinates
|
||||||
|
count:squareCoordinatesCount];
|
||||||
CLLocationCoordinate2D squareCenter = CLLocationCoordinate2DMake(100.5, 0.5);
|
CLLocationCoordinate2D squareCenter = CLLocationCoordinate2DMake(100.5, 0.5);
|
||||||
|
|
||||||
XCTAssertEqual([squarePolygon coordinate].latitude, squareCenter.latitude);
|
XCTAssertEqual([squarePolygon coordinate].latitude, squareCenter.latitude);
|
||||||
XCTAssertEqual([squarePolygon coordinate].longitude, squareCenter.longitude);
|
XCTAssertEqual([squarePolygon coordinate].longitude, squareCenter.longitude);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testPolygonWithInteriorPolygons {
|
- (void)testPolygonWithInteriorPolygons {
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {CLLocationCoordinate2DMake(0, 1),
|
||||||
CLLocationCoordinate2DMake(0, 1),
|
CLLocationCoordinate2DMake(10, 20)};
|
||||||
CLLocationCoordinate2DMake(10, 20)
|
|
||||||
};
|
|
||||||
|
|
||||||
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
|
|
||||||
CLLocationCoordinate2D interiorCoordinates[] = {
|
CLLocationCoordinate2D interiorCoordinates[] = {CLLocationCoordinate2DMake(4, 4),
|
||||||
CLLocationCoordinate2DMake(4, 4),
|
CLLocationCoordinate2DMake(6, 6)};
|
||||||
CLLocationCoordinate2DMake(6, 6)
|
|
||||||
};
|
|
||||||
|
|
||||||
NSUInteger numberOfInteriorCoordinates = sizeof(interiorCoordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger numberOfInteriorCoordinates =
|
||||||
|
sizeof(interiorCoordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
|
|
||||||
MLNPolygon *interiorPolygon = [MLNPolygon polygonWithCoordinates:interiorCoordinates count:numberOfInteriorCoordinates];
|
MLNPolygon *interiorPolygon = [MLNPolygon polygonWithCoordinates:interiorCoordinates
|
||||||
MLNPolygon *polygon = [MLNPolygon polygonWithCoordinates:coordinates count:numberOfCoordinates interiorPolygons:@[interiorPolygon]];
|
count:numberOfInteriorCoordinates];
|
||||||
|
MLNPolygon *polygon = [MLNPolygon polygonWithCoordinates:coordinates
|
||||||
|
count:numberOfCoordinates
|
||||||
|
interiorPolygons:@[ interiorPolygon ]];
|
||||||
|
|
||||||
NSString *filePath = [self temporaryFilePathForClass:[MLNPolygon class]];
|
NSString *filePath = [self temporaryFilePathForClass:[MLNPolygon class]];
|
||||||
[NSKeyedArchiver archiveRootObject:polygon toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:polygon toFile:filePath];
|
||||||
@@ -206,41 +209,41 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testPolylineFeature {
|
- (void)testPolylineFeature {
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {CLLocationCoordinate2DMake(0, 1),
|
||||||
CLLocationCoordinate2DMake(0, 1),
|
CLLocationCoordinate2DMake(10, 20)};
|
||||||
CLLocationCoordinate2DMake(10, 20)
|
|
||||||
};
|
|
||||||
|
|
||||||
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
MLNPolylineFeature *polylineFeature = [MLNPolylineFeature polylineWithCoordinates:coordinates count:numberOfCoordinates];
|
MLNPolylineFeature *polylineFeature =
|
||||||
polylineFeature.attributes = @{@"bbox": @[@0, @1, @2, @3]};
|
[MLNPolylineFeature polylineWithCoordinates:coordinates count:numberOfCoordinates];
|
||||||
|
polylineFeature.attributes = @{@"bbox" : @[ @0, @1, @2, @3 ]};
|
||||||
polylineFeature.identifier = @"identifier";
|
polylineFeature.identifier = @"identifier";
|
||||||
|
|
||||||
NSString *filePath = [self temporaryFilePathForClass:[MLNPolylineFeature class]];
|
NSString *filePath = [self temporaryFilePathForClass:[MLNPolylineFeature class]];
|
||||||
[NSKeyedArchiver archiveRootObject:polylineFeature toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:polylineFeature toFile:filePath];
|
||||||
|
|
||||||
MLNPolylineFeature *unarchivedPolylineFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
MLNPolylineFeature *unarchivedPolylineFeature =
|
||||||
|
[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||||
|
|
||||||
XCTAssertEqualObjects(polylineFeature, unarchivedPolylineFeature);
|
XCTAssertEqualObjects(polylineFeature, unarchivedPolylineFeature);
|
||||||
|
|
||||||
unarchivedPolylineFeature.attributes = @{@"bbox": @[@4, @3, @2, @1]};
|
unarchivedPolylineFeature.attributes = @{@"bbox" : @[ @4, @3, @2, @1 ]};
|
||||||
|
|
||||||
XCTAssertNotEqualObjects(polylineFeature, unarchivedPolylineFeature);
|
XCTAssertNotEqualObjects(polylineFeature, unarchivedPolylineFeature);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testPolygonFeature {
|
- (void)testPolygonFeature {
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {CLLocationCoordinate2DMake(0, 1),
|
||||||
CLLocationCoordinate2DMake(0, 1),
|
CLLocationCoordinate2DMake(10, 20)};
|
||||||
CLLocationCoordinate2DMake(10, 20)
|
|
||||||
};
|
|
||||||
|
|
||||||
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
MLNPolygonFeature *polygonFeature = [MLNPolygonFeature polygonWithCoordinates:coordinates count:numberOfCoordinates];
|
MLNPolygonFeature *polygonFeature =
|
||||||
|
[MLNPolygonFeature polygonWithCoordinates:coordinates count:numberOfCoordinates];
|
||||||
|
|
||||||
NSString *filePath = [self temporaryFilePathForClass:[MLNPolygonFeature class]];
|
NSString *filePath = [self temporaryFilePathForClass:[MLNPolygonFeature class]];
|
||||||
[NSKeyedArchiver archiveRootObject:polygonFeature toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:polygonFeature toFile:filePath];
|
||||||
|
|
||||||
MLNPolygonFeature *unarchivedPolygonFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
MLNPolygonFeature *unarchivedPolygonFeature =
|
||||||
|
[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||||
|
|
||||||
XCTAssertEqualObjects(polygonFeature, unarchivedPolygonFeature);
|
XCTAssertEqualObjects(polygonFeature, unarchivedPolygonFeature);
|
||||||
|
|
||||||
@@ -259,7 +262,8 @@
|
|||||||
|
|
||||||
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
|
|
||||||
MLNPointCollection *pointCollection = [MLNPointCollection pointCollectionWithCoordinates:coordinates count:numberOfCoordinates];
|
MLNPointCollection *pointCollection =
|
||||||
|
[MLNPointCollection pointCollectionWithCoordinates:coordinates count:numberOfCoordinates];
|
||||||
CLLocationCoordinate2D pointsCenter = CLLocationCoordinate2DMake(0, 1);
|
CLLocationCoordinate2D pointsCenter = CLLocationCoordinate2DMake(0, 1);
|
||||||
|
|
||||||
XCTAssertEqual([pointCollection coordinate].latitude, pointsCenter.latitude);
|
XCTAssertEqual([pointCollection coordinate].latitude, pointsCenter.latitude);
|
||||||
@@ -268,7 +272,8 @@
|
|||||||
NSString *filePath = [self temporaryFilePathForClass:[MLNPointCollection class]];
|
NSString *filePath = [self temporaryFilePathForClass:[MLNPointCollection class]];
|
||||||
[NSKeyedArchiver archiveRootObject:pointCollection toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:pointCollection toFile:filePath];
|
||||||
|
|
||||||
MLNPointCollection *unarchivedPointCollection = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
MLNPointCollection *unarchivedPointCollection =
|
||||||
|
[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||||
|
|
||||||
XCTAssertEqualObjects(pointCollection, unarchivedPointCollection);
|
XCTAssertEqualObjects(pointCollection, unarchivedPointCollection);
|
||||||
}
|
}
|
||||||
@@ -290,14 +295,17 @@
|
|||||||
|
|
||||||
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
|
|
||||||
MLNPointCollectionFeature *collection = [MLNPointCollectionFeature pointCollectionWithCoordinates:coordinates count:numberOfCoordinates];
|
MLNPointCollectionFeature *collection =
|
||||||
|
[MLNPointCollectionFeature pointCollectionWithCoordinates:coordinates
|
||||||
|
count:numberOfCoordinates];
|
||||||
collection.identifier = @"identifier";
|
collection.identifier = @"identifier";
|
||||||
collection.attributes = @{@"bbox": @[@1, @2, @3, @4]};
|
collection.attributes = @{@"bbox" : @[ @1, @2, @3, @4 ]};
|
||||||
|
|
||||||
NSString *filePath = [self temporaryFilePathForClass:[MLNPointCollectionFeature class]];
|
NSString *filePath = [self temporaryFilePathForClass:[MLNPointCollectionFeature class]];
|
||||||
[NSKeyedArchiver archiveRootObject:collection toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:collection toFile:filePath];
|
||||||
|
|
||||||
MLNPointCollectionFeature *unarchivedCollection = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
MLNPointCollectionFeature *unarchivedCollection =
|
||||||
|
[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||||
|
|
||||||
XCTAssertEqualObjects(collection, unarchivedCollection);
|
XCTAssertEqualObjects(collection, unarchivedCollection);
|
||||||
|
|
||||||
@@ -307,7 +315,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMultiPolyline {
|
- (void)testMultiPolyline {
|
||||||
|
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {
|
||||||
CLLocationCoordinate2DMake(0, 1),
|
CLLocationCoordinate2DMake(0, 1),
|
||||||
CLLocationCoordinate2DMake(10, 11),
|
CLLocationCoordinate2DMake(10, 11),
|
||||||
@@ -315,25 +322,24 @@
|
|||||||
CLLocationCoordinate2DMake(30, 31),
|
CLLocationCoordinate2DMake(30, 31),
|
||||||
};
|
};
|
||||||
|
|
||||||
CLLocationCoordinate2D line1[] = {
|
CLLocationCoordinate2D line1[] = {CLLocationCoordinate2DMake(100, 40),
|
||||||
CLLocationCoordinate2DMake(100, 40),
|
|
||||||
CLLocationCoordinate2DMake(105, 45),
|
CLLocationCoordinate2DMake(105, 45),
|
||||||
CLLocationCoordinate2DMake(110, 55)
|
CLLocationCoordinate2DMake(110, 55)};
|
||||||
};
|
|
||||||
|
|
||||||
CLLocationCoordinate2D line2[] = {
|
CLLocationCoordinate2D line2[] = {CLLocationCoordinate2DMake(105, 40),
|
||||||
CLLocationCoordinate2DMake(105, 40),
|
|
||||||
CLLocationCoordinate2DMake(110, 45),
|
CLLocationCoordinate2DMake(110, 45),
|
||||||
CLLocationCoordinate2DMake(115, 55)
|
CLLocationCoordinate2DMake(115, 55)};
|
||||||
};
|
|
||||||
|
|
||||||
NSUInteger road1CoordinatesCount = sizeof(line1) / sizeof(CLLocationCoordinate2D);
|
NSUInteger road1CoordinatesCount = sizeof(line1) / sizeof(CLLocationCoordinate2D);
|
||||||
NSUInteger road2CoordinatesCount = sizeof(line2) / sizeof(CLLocationCoordinate2D);
|
NSUInteger road2CoordinatesCount = sizeof(line2) / sizeof(CLLocationCoordinate2D);
|
||||||
|
|
||||||
MLNPolyline *road1Polyline = [MLNPolyline polylineWithCoordinates:line1 count:road1CoordinatesCount];
|
MLNPolyline *road1Polyline = [MLNPolyline polylineWithCoordinates:line1
|
||||||
MLNPolyline *road2Polyline = [MLNPolyline polylineWithCoordinates:line1 count:road2CoordinatesCount];
|
count:road1CoordinatesCount];
|
||||||
|
MLNPolyline *road2Polyline = [MLNPolyline polylineWithCoordinates:line1
|
||||||
|
count:road2CoordinatesCount];
|
||||||
|
|
||||||
MLNMultiPolyline *roads = [MLNMultiPolyline multiPolylineWithPolylines:@[road1Polyline, road2Polyline]];
|
MLNMultiPolyline *roads =
|
||||||
|
[MLNMultiPolyline multiPolylineWithPolylines:@[ road1Polyline, road2Polyline ]];
|
||||||
CLLocationCoordinate2D roadCenter = CLLocationCoordinate2DMake(100, 40);
|
CLLocationCoordinate2D roadCenter = CLLocationCoordinate2DMake(100, 40);
|
||||||
|
|
||||||
XCTAssertEqual([roads coordinate].latitude, roadCenter.latitude);
|
XCTAssertEqual([roads coordinate].latitude, roadCenter.latitude);
|
||||||
@@ -344,7 +350,8 @@
|
|||||||
NSMutableArray *polylines = [NSMutableArray array];
|
NSMutableArray *polylines = [NSMutableArray array];
|
||||||
|
|
||||||
for (NSUInteger i = 0; i < 100; i++) {
|
for (NSUInteger i = 0; i < 100; i++) {
|
||||||
MLNPolyline *polyline = [MLNPolyline polylineWithCoordinates:coordinates count:numberOfCoordinates];
|
MLNPolyline *polyline = [MLNPolyline polylineWithCoordinates:coordinates
|
||||||
|
count:numberOfCoordinates];
|
||||||
[polylines addObject:polyline];
|
[polylines addObject:polyline];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,14 +361,14 @@
|
|||||||
[NSKeyedArchiver archiveRootObject:multiPolyline toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:multiPolyline toFile:filePath];
|
||||||
|
|
||||||
MLNMultiPolyline *unarchivedMultiPolyline = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
MLNMultiPolyline *unarchivedMultiPolyline = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||||
MLNMultiPolyline *anotherMultipolyline = [MLNMultiPolyline multiPolylineWithPolylines:[polylines subarrayWithRange:NSMakeRange(0, polylines.count/2)]];
|
MLNMultiPolyline *anotherMultipolyline = [MLNMultiPolyline
|
||||||
|
multiPolylineWithPolylines:[polylines subarrayWithRange:NSMakeRange(0, polylines.count / 2)]];
|
||||||
|
|
||||||
XCTAssertEqualObjects(multiPolyline, unarchivedMultiPolyline);
|
XCTAssertEqualObjects(multiPolyline, unarchivedMultiPolyline);
|
||||||
XCTAssertNotEqualObjects(unarchivedMultiPolyline, anotherMultipolyline);
|
XCTAssertNotEqualObjects(unarchivedMultiPolyline, anotherMultipolyline);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMultiPolygon {
|
- (void)testMultiPolygon {
|
||||||
|
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {
|
||||||
CLLocationCoordinate2DMake(0, 1),
|
CLLocationCoordinate2DMake(0, 1),
|
||||||
CLLocationCoordinate2DMake(10, 11),
|
CLLocationCoordinate2DMake(10, 11),
|
||||||
@@ -386,9 +393,13 @@
|
|||||||
NSUInteger outerCoordinatesCount = sizeof(outerSquare) / sizeof(CLLocationCoordinate2D);
|
NSUInteger outerCoordinatesCount = sizeof(outerSquare) / sizeof(CLLocationCoordinate2D);
|
||||||
NSUInteger innerCoordinatesCount = sizeof(innerSquare) / sizeof(CLLocationCoordinate2D);
|
NSUInteger innerCoordinatesCount = sizeof(innerSquare) / sizeof(CLLocationCoordinate2D);
|
||||||
|
|
||||||
MLNPolygon *innerPolygonSquare = [MLNPolygon polygonWithCoordinates:innerSquare count:innerCoordinatesCount];
|
MLNPolygon *innerPolygonSquare = [MLNPolygon polygonWithCoordinates:innerSquare
|
||||||
MLNPolygon *outerPolygonSquare = [MLNPolygon polygonWithCoordinates:outerSquare count:outerCoordinatesCount interiorPolygons:@[innerPolygonSquare]];
|
count:innerCoordinatesCount];
|
||||||
MLNMultiPolygon *squares = [MLNMultiPolygon multiPolygonWithPolygons:@[outerPolygonSquare, innerPolygonSquare]];
|
MLNPolygon *outerPolygonSquare = [MLNPolygon polygonWithCoordinates:outerSquare
|
||||||
|
count:outerCoordinatesCount
|
||||||
|
interiorPolygons:@[ innerPolygonSquare ]];
|
||||||
|
MLNMultiPolygon *squares =
|
||||||
|
[MLNMultiPolygon multiPolygonWithPolygons:@[ outerPolygonSquare, innerPolygonSquare ]];
|
||||||
CLLocationCoordinate2D squareCenter = CLLocationCoordinate2DMake(100.5, 0.5);
|
CLLocationCoordinate2D squareCenter = CLLocationCoordinate2DMake(100.5, 0.5);
|
||||||
|
|
||||||
XCTAssertEqual([squares coordinate].latitude, squareCenter.latitude);
|
XCTAssertEqual([squares coordinate].latitude, squareCenter.latitude);
|
||||||
@@ -409,11 +420,11 @@
|
|||||||
[NSKeyedArchiver archiveRootObject:multiPolygon toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:multiPolygon toFile:filePath];
|
||||||
|
|
||||||
MLNMultiPolygon *unarchivedMultiPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
MLNMultiPolygon *unarchivedMultiPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||||
MLNMultiPolygon *anotherMultiPolygon = [MLNMultiPolygon multiPolygonWithPolygons:[polygons subarrayWithRange:NSMakeRange(0, polygons.count/2)]];
|
MLNMultiPolygon *anotherMultiPolygon = [MLNMultiPolygon
|
||||||
|
multiPolygonWithPolygons:[polygons subarrayWithRange:NSMakeRange(0, polygons.count / 2)]];
|
||||||
|
|
||||||
XCTAssertEqualObjects(multiPolygon, unarchivedMultiPolygon);
|
XCTAssertEqualObjects(multiPolygon, unarchivedMultiPolygon);
|
||||||
XCTAssertNotEqualObjects(anotherMultiPolygon, unarchivedMultiPolygon);
|
XCTAssertNotEqualObjects(anotherMultiPolygon, unarchivedMultiPolygon);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testShapeCollection {
|
- (void)testShapeCollection {
|
||||||
@@ -425,16 +436,20 @@
|
|||||||
|
|
||||||
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
|
|
||||||
MLNPolyline *polyline = [MLNPolyline polylineWithCoordinates:coordinates count:numberOfCoordinates];
|
MLNPolyline *polyline = [MLNPolyline polylineWithCoordinates:coordinates
|
||||||
|
count:numberOfCoordinates];
|
||||||
MLNPolygon *polygon = [MLNPolygon polygonWithCoordinates:coordinates count:numberOfCoordinates];
|
MLNPolygon *polygon = [MLNPolygon polygonWithCoordinates:coordinates count:numberOfCoordinates];
|
||||||
|
|
||||||
MLNShapeCollection *shapeCollection = [MLNShapeCollection shapeCollectionWithShapes:@[polyline, polygon]];
|
MLNShapeCollection *shapeCollection =
|
||||||
|
[MLNShapeCollection shapeCollectionWithShapes:@[ polyline, polygon ]];
|
||||||
|
|
||||||
NSString *filePath = [self temporaryFilePathForClass:[MLNShapeCollection class]];
|
NSString *filePath = [self temporaryFilePathForClass:[MLNShapeCollection class]];
|
||||||
[NSKeyedArchiver archiveRootObject:shapeCollection toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:shapeCollection toFile:filePath];
|
||||||
|
|
||||||
MLNShapeCollection *unarchivedShapeCollection = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
MLNShapeCollection *unarchivedShapeCollection =
|
||||||
MLNShapeCollection *anotherShapeCollection = [MLNShapeCollection shapeCollectionWithShapes:@[polygon]];
|
[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||||
|
MLNShapeCollection *anotherShapeCollection =
|
||||||
|
[MLNShapeCollection shapeCollectionWithShapes:@[ polygon ]];
|
||||||
|
|
||||||
XCTAssertEqualObjects(shapeCollection, unarchivedShapeCollection);
|
XCTAssertEqualObjects(shapeCollection, unarchivedShapeCollection);
|
||||||
XCTAssertNotEqualObjects(shapeCollection, anotherShapeCollection);
|
XCTAssertNotEqualObjects(shapeCollection, anotherShapeCollection);
|
||||||
@@ -451,19 +466,23 @@
|
|||||||
|
|
||||||
NSMutableArray *polylines = [NSMutableArray array];
|
NSMutableArray *polylines = [NSMutableArray array];
|
||||||
for (NSUInteger i = 0; i < 100; i++) {
|
for (NSUInteger i = 0; i < 100; i++) {
|
||||||
MLNPolylineFeature *polylineFeature = [MLNPolylineFeature polylineWithCoordinates:coordinates count:numberOfCoordinates];
|
MLNPolylineFeature *polylineFeature =
|
||||||
|
[MLNPolylineFeature polylineWithCoordinates:coordinates count:numberOfCoordinates];
|
||||||
polylineFeature.identifier = @(arc4random() % 100).stringValue;
|
polylineFeature.identifier = @(arc4random() % 100).stringValue;
|
||||||
[polylines addObject:polylineFeature];
|
[polylines addObject:polylineFeature];
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNMultiPolylineFeature *multiPolylineFeature = [MLNMultiPolylineFeature multiPolylineWithPolylines:polylines];
|
MLNMultiPolylineFeature *multiPolylineFeature =
|
||||||
multiPolylineFeature.attributes = @{@"bbox": @[@4, @3, @2, @1]};
|
[MLNMultiPolylineFeature multiPolylineWithPolylines:polylines];
|
||||||
|
multiPolylineFeature.attributes = @{@"bbox" : @[ @4, @3, @2, @1 ]};
|
||||||
|
|
||||||
NSString *filePath = [self temporaryFilePathForClass:[MLNMultiPolylineFeature class]];
|
NSString *filePath = [self temporaryFilePathForClass:[MLNMultiPolylineFeature class]];
|
||||||
[NSKeyedArchiver archiveRootObject:multiPolylineFeature toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:multiPolylineFeature toFile:filePath];
|
||||||
|
|
||||||
MLNMultiPolylineFeature *unarchivedMultiPolylineFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
MLNMultiPolylineFeature *unarchivedMultiPolylineFeature =
|
||||||
MLNMultiPolylineFeature *anotherMultiPolylineFeature = [MLNMultiPolylineFeature multiPolylineWithPolylines:[polylines subarrayWithRange:NSMakeRange(0, polylines.count/2)]];
|
[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||||
|
MLNMultiPolylineFeature *anotherMultiPolylineFeature = [MLNMultiPolylineFeature
|
||||||
|
multiPolylineWithPolylines:[polylines subarrayWithRange:NSMakeRange(0, polylines.count / 2)]];
|
||||||
|
|
||||||
XCTAssertEqualObjects(multiPolylineFeature, unarchivedMultiPolylineFeature);
|
XCTAssertEqualObjects(multiPolylineFeature, unarchivedMultiPolylineFeature);
|
||||||
XCTAssertNotEqualObjects(unarchivedMultiPolylineFeature, anotherMultiPolylineFeature);
|
XCTAssertNotEqualObjects(unarchivedMultiPolylineFeature, anotherMultiPolylineFeature);
|
||||||
@@ -479,23 +498,29 @@
|
|||||||
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
|
|
||||||
NSMutableArray *polygons = [NSMutableArray array];
|
NSMutableArray *polygons = [NSMutableArray array];
|
||||||
for (NSUInteger i = 0; i < 100; i++ ) {
|
for (NSUInteger i = 0; i < 100; i++) {
|
||||||
MLNPolygonFeature *polygonFeature = [MLNPolygonFeature polygonWithCoordinates:coordinates count:numberOfCoordinates];
|
MLNPolygonFeature *polygonFeature =
|
||||||
|
[MLNPolygonFeature polygonWithCoordinates:coordinates count:numberOfCoordinates];
|
||||||
polygonFeature.identifier = @(arc4random_uniform(100)).stringValue;
|
polygonFeature.identifier = @(arc4random_uniform(100)).stringValue;
|
||||||
[polygons addObject:polygonFeature];
|
[polygons addObject:polygonFeature];
|
||||||
}
|
}
|
||||||
|
|
||||||
MLNMultiPolygonFeature *multiPolygonFeature = [MLNMultiPolygonFeature multiPolygonWithPolygons:polygons];
|
MLNMultiPolygonFeature *multiPolygonFeature =
|
||||||
multiPolygonFeature.attributes = @{@"bbox": @[@(arc4random_uniform(100)),
|
[MLNMultiPolygonFeature multiPolygonWithPolygons:polygons];
|
||||||
@(arc4random_uniform(100)),
|
multiPolygonFeature.attributes = @{
|
||||||
@(arc4random_uniform(100)),
|
@"bbox" : @[
|
||||||
@(arc4random_uniform(100))]};
|
@(arc4random_uniform(100)), @(arc4random_uniform(100)), @(arc4random_uniform(100)),
|
||||||
|
@(arc4random_uniform(100))
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
NSString *filePath = [self temporaryFilePathForClass:[MLNMultiPolylineFeature class]];
|
NSString *filePath = [self temporaryFilePathForClass:[MLNMultiPolylineFeature class]];
|
||||||
[NSKeyedArchiver archiveRootObject:multiPolygonFeature toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:multiPolygonFeature toFile:filePath];
|
||||||
|
|
||||||
MLNMultiPolygonFeature *unarchivedMultiPolygonFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
MLNMultiPolygonFeature *unarchivedMultiPolygonFeature =
|
||||||
MLNMultiPolygonFeature *anotherMultiPolygonFeature = [MLNMultiPolygonFeature multiPolygonWithPolygons:[polygons subarrayWithRange:NSMakeRange(0, polygons.count/2)]];
|
[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||||
|
MLNMultiPolygonFeature *anotherMultiPolygonFeature = [MLNMultiPolygonFeature
|
||||||
|
multiPolygonWithPolygons:[polygons subarrayWithRange:NSMakeRange(0, polygons.count / 2)]];
|
||||||
|
|
||||||
XCTAssertEqualObjects(multiPolygonFeature, unarchivedMultiPolygonFeature);
|
XCTAssertEqualObjects(multiPolygonFeature, unarchivedMultiPolygonFeature);
|
||||||
XCTAssertNotEqualObjects(anotherMultiPolygonFeature, unarchivedMultiPolygonFeature);
|
XCTAssertNotEqualObjects(anotherMultiPolygonFeature, unarchivedMultiPolygonFeature);
|
||||||
@@ -510,20 +535,26 @@
|
|||||||
|
|
||||||
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
|
||||||
|
|
||||||
MLNPolylineFeature *polyline = [MLNPolylineFeature polylineWithCoordinates:coordinates count:numberOfCoordinates];
|
MLNPolylineFeature *polyline = [MLNPolylineFeature polylineWithCoordinates:coordinates
|
||||||
MLNPolygonFeature *polygon = [MLNPolygonFeature polygonWithCoordinates:coordinates count:numberOfCoordinates];
|
count:numberOfCoordinates];
|
||||||
|
MLNPolygonFeature *polygon = [MLNPolygonFeature polygonWithCoordinates:coordinates
|
||||||
|
count:numberOfCoordinates];
|
||||||
|
|
||||||
MLNShapeCollectionFeature *shapeCollectionFeature = [MLNShapeCollectionFeature shapeCollectionWithShapes:@[polyline, polygon]];
|
MLNShapeCollectionFeature *shapeCollectionFeature =
|
||||||
|
[MLNShapeCollectionFeature shapeCollectionWithShapes:@[ polyline, polygon ]];
|
||||||
shapeCollectionFeature.identifier = @(arc4random_uniform(100)).stringValue;
|
shapeCollectionFeature.identifier = @(arc4random_uniform(100)).stringValue;
|
||||||
shapeCollectionFeature.attributes = @{@"bbox":@[@(arc4random_uniform(100)),
|
shapeCollectionFeature.attributes = @{
|
||||||
@(arc4random_uniform(100)),
|
@"bbox" : @[
|
||||||
@(arc4random_uniform(100)),
|
@(arc4random_uniform(100)), @(arc4random_uniform(100)), @(arc4random_uniform(100)),
|
||||||
@(arc4random_uniform(100))]};
|
@(arc4random_uniform(100))
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
NSString *filePath = [self temporaryFilePathForClass:[MLNShapeCollectionFeature class]];
|
NSString *filePath = [self temporaryFilePathForClass:[MLNShapeCollectionFeature class]];
|
||||||
[NSKeyedArchiver archiveRootObject:shapeCollectionFeature toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:shapeCollectionFeature toFile:filePath];
|
||||||
|
|
||||||
MLNShapeCollectionFeature *unarchivedShapeCollectionFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
MLNShapeCollectionFeature *unarchivedShapeCollectionFeature =
|
||||||
|
[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||||
|
|
||||||
XCTAssertEqualObjects(shapeCollectionFeature, unarchivedShapeCollectionFeature);
|
XCTAssertEqualObjects(shapeCollectionFeature, unarchivedShapeCollectionFeature);
|
||||||
}
|
}
|
||||||
@@ -542,12 +573,15 @@
|
|||||||
[image unlockFocus];
|
[image unlockFocus];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MLNAnnotationImage *annotationImage = [MLNAnnotationImage annotationImageWithImage:image reuseIdentifier:@(arc4random_uniform(100)).stringValue];
|
MLNAnnotationImage *annotationImage =
|
||||||
|
[MLNAnnotationImage annotationImageWithImage:image
|
||||||
|
reuseIdentifier:@(arc4random_uniform(100)).stringValue];
|
||||||
|
|
||||||
NSString *filePath = [self temporaryFilePathForClass:[MLNAnnotationImage class]];
|
NSString *filePath = [self temporaryFilePathForClass:[MLNAnnotationImage class]];
|
||||||
[NSKeyedArchiver archiveRootObject:annotationImage toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:annotationImage toFile:filePath];
|
||||||
|
|
||||||
MLNAnnotationImage *unarchivedAnnotationImage = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
MLNAnnotationImage *unarchivedAnnotationImage =
|
||||||
|
[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||||
|
|
||||||
XCTAssertEqualObjects(annotationImage, unarchivedAnnotationImage);
|
XCTAssertEqualObjects(annotationImage, unarchivedAnnotationImage);
|
||||||
}
|
}
|
||||||
@@ -564,13 +598,16 @@
|
|||||||
NSString *filePath = [self temporaryFilePathForClass:[MLNAnnotationView class]];
|
NSString *filePath = [self temporaryFilePathForClass:[MLNAnnotationView class]];
|
||||||
[NSKeyedArchiver archiveRootObject:annotationView toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:annotationView toFile:filePath];
|
||||||
|
|
||||||
MLNAnnotationView *unarchivedAnnotationView = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
MLNAnnotationView *unarchivedAnnotationView =
|
||||||
|
[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||||
|
|
||||||
XCTAssertEqual(annotationView.enabled, unarchivedAnnotationView.enabled);
|
XCTAssertEqual(annotationView.enabled, unarchivedAnnotationView.enabled);
|
||||||
XCTAssertEqual(annotationView.selected, unarchivedAnnotationView.selected);
|
XCTAssertEqual(annotationView.selected, unarchivedAnnotationView.selected);
|
||||||
XCTAssertEqual(annotationView.draggable, unarchivedAnnotationView.draggable);
|
XCTAssertEqual(annotationView.draggable, unarchivedAnnotationView.draggable);
|
||||||
XCTAssertEqualObjects(NSStringFromCGVector(annotationView.centerOffset), NSStringFromCGVector(unarchivedAnnotationView.centerOffset));
|
XCTAssertEqualObjects(NSStringFromCGVector(annotationView.centerOffset),
|
||||||
XCTAssertEqual(annotationView.scalesWithViewingDistance, unarchivedAnnotationView.scalesWithViewingDistance);
|
NSStringFromCGVector(unarchivedAnnotationView.centerOffset));
|
||||||
|
XCTAssertEqual(annotationView.scalesWithViewingDistance,
|
||||||
|
unarchivedAnnotationView.scalesWithViewingDistance);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -602,13 +639,16 @@
|
|||||||
NSString *filePath = [self temporaryFilePathForClass:[MLNUserLocationAnnotationView class]];
|
NSString *filePath = [self temporaryFilePathForClass:[MLNUserLocationAnnotationView class]];
|
||||||
[NSKeyedArchiver archiveRootObject:annotationView toFile:filePath];
|
[NSKeyedArchiver archiveRootObject:annotationView toFile:filePath];
|
||||||
|
|
||||||
MLNUserLocationAnnotationView *unarchivedAnnotationView = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
MLNUserLocationAnnotationView *unarchivedAnnotationView =
|
||||||
|
[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||||
|
|
||||||
XCTAssertEqual(annotationView.enabled, unarchivedAnnotationView.enabled);
|
XCTAssertEqual(annotationView.enabled, unarchivedAnnotationView.enabled);
|
||||||
XCTAssertEqual(annotationView.selected, unarchivedAnnotationView.selected);
|
XCTAssertEqual(annotationView.selected, unarchivedAnnotationView.selected);
|
||||||
XCTAssertEqual(annotationView.draggable, unarchivedAnnotationView.draggable);
|
XCTAssertEqual(annotationView.draggable, unarchivedAnnotationView.draggable);
|
||||||
XCTAssertEqualObjects(NSStringFromCGVector(annotationView.centerOffset), NSStringFromCGVector(unarchivedAnnotationView.centerOffset));
|
XCTAssertEqualObjects(NSStringFromCGVector(annotationView.centerOffset),
|
||||||
XCTAssertEqual(annotationView.scalesWithViewingDistance, unarchivedAnnotationView.scalesWithViewingDistance);
|
NSStringFromCGVector(unarchivedAnnotationView.centerOffset));
|
||||||
|
XCTAssertEqual(annotationView.scalesWithViewingDistance,
|
||||||
|
unarchivedAnnotationView.scalesWithViewingDistance);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,8 @@
|
|||||||
#import <XCTest/XCTest.h>
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
#import <mbgl/util/geometry.hpp>
|
#import <mbgl/util/geometry.hpp>
|
||||||
#import "MLNFoundation_Private.h"
|
|
||||||
#import "../../darwin/src/MLNFeature_Private.h"
|
#import "../../darwin/src/MLNFeature_Private.h"
|
||||||
|
#import "MLNFoundation_Private.h"
|
||||||
|
|
||||||
@interface MLNFeatureTests : XCTestCase
|
@interface MLNFeatureTests : XCTestCase
|
||||||
|
|
||||||
@@ -14,37 +14,38 @@
|
|||||||
- (void)testGeometryConversion {
|
- (void)testGeometryConversion {
|
||||||
std::vector<mbgl::Feature> features;
|
std::vector<mbgl::Feature> features;
|
||||||
|
|
||||||
mbgl::Point<double> point = { -90.066667, 29.95 };
|
mbgl::Point<double> point = {-90.066667, 29.95};
|
||||||
features.push_back(mbgl::Feature { point });
|
features.push_back(mbgl::Feature{point});
|
||||||
|
|
||||||
mbgl::LineString<double> lineString = {
|
mbgl::LineString<double> lineString = {
|
||||||
{ -84.516667, 39.1 },
|
{-84.516667, 39.1},
|
||||||
{ -90.066667, 29.95 },
|
{-90.066667, 29.95},
|
||||||
};
|
};
|
||||||
features.push_back(mbgl::Feature { lineString });
|
features.push_back(mbgl::Feature{lineString});
|
||||||
|
|
||||||
mbgl::Polygon<double> polygon = {
|
mbgl::Polygon<double> polygon = {
|
||||||
{
|
{
|
||||||
{ 1, 1 },
|
{1, 1},
|
||||||
{ 4, 1 },
|
{4, 1},
|
||||||
{ 4, 4 },
|
{4, 4},
|
||||||
{ 1, 4 },
|
{1, 4},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
{ 2, 2 },
|
{2, 2},
|
||||||
{ 3, 2 },
|
{3, 2},
|
||||||
{ 3, 3 },
|
{3, 3},
|
||||||
{ 2, 3 },
|
{2, 3},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
features.push_back(mbgl::Feature { polygon });
|
features.push_back(mbgl::Feature{polygon});
|
||||||
|
|
||||||
NSArray<MLNShape <MLNFeature> *> *shapes = MLNFeaturesFromMBGLFeatures(features);
|
NSArray<MLNShape<MLNFeature> *> *shapes = MLNFeaturesFromMBGLFeatures(features);
|
||||||
XCTAssertEqual(shapes.count, 3UL, @"All features should be converted into shapes");
|
XCTAssertEqual(shapes.count, 3UL, @"All features should be converted into shapes");
|
||||||
|
|
||||||
MLNPointFeature *pointShape = (MLNPointFeature *)shapes[0];
|
MLNPointFeature *pointShape = (MLNPointFeature *)shapes[0];
|
||||||
XCTAssertTrue([pointShape isKindOfClass:[MLNPointFeature class]]);
|
XCTAssertTrue([pointShape isKindOfClass:[MLNPointFeature class]]);
|
||||||
XCTAssertEqualObjects([NSValue valueWithMLNCoordinate:pointShape.coordinate],
|
XCTAssertEqualObjects(
|
||||||
|
[NSValue valueWithMLNCoordinate:pointShape.coordinate],
|
||||||
[NSValue valueWithMLNCoordinate:CLLocationCoordinate2DMake(29.95, -90.066667)]);
|
[NSValue valueWithMLNCoordinate:CLLocationCoordinate2DMake(29.95, -90.066667)]);
|
||||||
|
|
||||||
MLNPolylineFeature *polylineShape = (MLNPolylineFeature *)shapes[1];
|
MLNPolylineFeature *polylineShape = (MLNPolylineFeature *)shapes[1];
|
||||||
@@ -52,9 +53,11 @@
|
|||||||
XCTAssertEqual(polylineShape.pointCount, 2UL);
|
XCTAssertEqual(polylineShape.pointCount, 2UL);
|
||||||
CLLocationCoordinate2D polylineCoordinates[2];
|
CLLocationCoordinate2D polylineCoordinates[2];
|
||||||
[polylineShape getCoordinates:polylineCoordinates range:NSMakeRange(0, polylineShape.pointCount)];
|
[polylineShape getCoordinates:polylineCoordinates range:NSMakeRange(0, polylineShape.pointCount)];
|
||||||
XCTAssertEqualObjects([NSValue valueWithMLNCoordinate:polylineCoordinates[0]],
|
XCTAssertEqualObjects(
|
||||||
|
[NSValue valueWithMLNCoordinate:polylineCoordinates[0]],
|
||||||
[NSValue valueWithMLNCoordinate:CLLocationCoordinate2DMake(39.1, -84.516667)]);
|
[NSValue valueWithMLNCoordinate:CLLocationCoordinate2DMake(39.1, -84.516667)]);
|
||||||
XCTAssertEqualObjects([NSValue valueWithMLNCoordinate:polylineCoordinates[1]],
|
XCTAssertEqualObjects(
|
||||||
|
[NSValue valueWithMLNCoordinate:polylineCoordinates[1]],
|
||||||
[NSValue valueWithMLNCoordinate:CLLocationCoordinate2DMake(29.95, -90.066667)]);
|
[NSValue valueWithMLNCoordinate:CLLocationCoordinate2DMake(29.95, -90.066667)]);
|
||||||
|
|
||||||
MLNPolygonFeature *polygonShape = (MLNPolygonFeature *)shapes[2];
|
MLNPolygonFeature *polygonShape = (MLNPolygonFeature *)shapes[2];
|
||||||
@@ -75,7 +78,8 @@
|
|||||||
MLNPolygon *interiorPolygon = interiorPolygons.firstObject;
|
MLNPolygon *interiorPolygon = interiorPolygons.firstObject;
|
||||||
XCTAssertEqual(interiorPolygon.pointCount, 4UL);
|
XCTAssertEqual(interiorPolygon.pointCount, 4UL);
|
||||||
CLLocationCoordinate2D interiorPolygonCoordinates[4];
|
CLLocationCoordinate2D interiorPolygonCoordinates[4];
|
||||||
[interiorPolygon getCoordinates:interiorPolygonCoordinates range:NSMakeRange(0, interiorPolygon.pointCount)];
|
[interiorPolygon getCoordinates:interiorPolygonCoordinates
|
||||||
|
range:NSMakeRange(0, interiorPolygon.pointCount)];
|
||||||
XCTAssertEqualObjects([NSValue valueWithMLNCoordinate:interiorPolygonCoordinates[0]],
|
XCTAssertEqualObjects([NSValue valueWithMLNCoordinate:interiorPolygonCoordinates[0]],
|
||||||
[NSValue valueWithMLNCoordinate:CLLocationCoordinate2DMake(2, 2)]);
|
[NSValue valueWithMLNCoordinate:CLLocationCoordinate2DMake(2, 2)]);
|
||||||
XCTAssertEqualObjects([NSValue valueWithMLNCoordinate:interiorPolygonCoordinates[1]],
|
XCTAssertEqualObjects([NSValue valueWithMLNCoordinate:interiorPolygonCoordinates[1]],
|
||||||
@@ -87,9 +91,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testClusterGeometryConversion {
|
- (void)testClusterGeometryConversion {
|
||||||
mbgl::Point<double> point = { -90.066667, 29.95 };
|
mbgl::Point<double> point = {-90.066667, 29.95};
|
||||||
mbgl::Feature pointFeature { point };
|
mbgl::Feature pointFeature{point};
|
||||||
pointFeature.id = { UINT64_MAX };
|
pointFeature.id = {UINT64_MAX};
|
||||||
pointFeature.properties["cluster"] = true;
|
pointFeature.properties["cluster"] = true;
|
||||||
pointFeature.properties["cluster_id"] = 1ULL;
|
pointFeature.properties["cluster_id"] = 1ULL;
|
||||||
pointFeature.properties["point_count"] = 5ULL;
|
pointFeature.properties["point_count"] = 5ULL;
|
||||||
@@ -109,9 +113,9 @@
|
|||||||
- (void)testPropertyConversion {
|
- (void)testPropertyConversion {
|
||||||
std::vector<mbgl::Feature> features;
|
std::vector<mbgl::Feature> features;
|
||||||
|
|
||||||
mbgl::Point<double> point = { -90.066667, 29.95 };
|
mbgl::Point<double> point = {-90.066667, 29.95};
|
||||||
mbgl::Feature pointFeature { point };
|
mbgl::Feature pointFeature{point};
|
||||||
pointFeature.id = { UINT64_MAX };
|
pointFeature.id = {UINT64_MAX};
|
||||||
pointFeature.properties["null"] = mapbox::feature::null_value;
|
pointFeature.properties["null"] = mapbox::feature::null_value;
|
||||||
pointFeature.properties["bool"] = true;
|
pointFeature.properties["bool"] = true;
|
||||||
pointFeature.properties["unsigned int"] = UINT64_MAX;
|
pointFeature.properties["unsigned int"] = UINT64_MAX;
|
||||||
@@ -124,16 +128,18 @@
|
|||||||
vector.push_back(true);
|
vector.push_back(true);
|
||||||
features.push_back(pointFeature);
|
features.push_back(pointFeature);
|
||||||
|
|
||||||
NSArray<MLNShape <MLNFeature> *> *shapes = MLNFeaturesFromMBGLFeatures(features);
|
NSArray<MLNShape<MLNFeature> *> *shapes = MLNFeaturesFromMBGLFeatures(features);
|
||||||
XCTAssertEqual(shapes.count, 1UL, @"All features should be converted into shapes");
|
XCTAssertEqual(shapes.count, 1UL, @"All features should be converted into shapes");
|
||||||
|
|
||||||
MLNShape <MLNFeature> *shape = shapes.firstObject;
|
MLNShape<MLNFeature> *shape = shapes.firstObject;
|
||||||
XCTAssertTrue([shape conformsToProtocol:@protocol(MLNFeature)]);
|
XCTAssertTrue([shape conformsToProtocol:@protocol(MLNFeature)]);
|
||||||
XCTAssertTrue([shape isKindOfClass:[MLNShape class]]);
|
XCTAssertTrue([shape isKindOfClass:[MLNShape class]]);
|
||||||
|
|
||||||
NSNumber *identifier = shape.identifier;
|
NSNumber *identifier = shape.identifier;
|
||||||
XCTAssertTrue([identifier isKindOfClass:[NSNumber class]], @"Feature identifier should be NSNumber");
|
XCTAssertTrue([identifier isKindOfClass:[NSNumber class]],
|
||||||
XCTAssertEqual(strcmp(identifier.objCType, @encode(uint64_t)), 0, @"Feature identifier should be 64-bit unsigned integer");
|
@"Feature identifier should be NSNumber");
|
||||||
|
XCTAssertEqual(strcmp(identifier.objCType, @encode(uint64_t)), 0,
|
||||||
|
@"Feature identifier should be 64-bit unsigned integer");
|
||||||
|
|
||||||
NSNull *null = [shape attributeForKey:@"null"];
|
NSNull *null = [shape attributeForKey:@"null"];
|
||||||
XCTAssertNotNil(null);
|
XCTAssertNotNil(null);
|
||||||
@@ -143,28 +149,32 @@
|
|||||||
NSNumber *boolean = [shape attributeForKey:@"bool"];
|
NSNumber *boolean = [shape attributeForKey:@"bool"];
|
||||||
XCTAssertNotNil(boolean);
|
XCTAssertNotNil(boolean);
|
||||||
XCTAssertTrue([boolean isKindOfClass:[NSNumber class]]);
|
XCTAssertTrue([boolean isKindOfClass:[NSNumber class]]);
|
||||||
XCTAssertEqual(strcmp(boolean.objCType, @encode(char)), 0, @"Boolean property should be converted to bool NSNumber");
|
XCTAssertEqual(strcmp(boolean.objCType, @encode(char)), 0,
|
||||||
|
@"Boolean property should be converted to bool NSNumber");
|
||||||
XCTAssertTrue(boolean.boolValue);
|
XCTAssertTrue(boolean.boolValue);
|
||||||
XCTAssertEqual(boolean, shape.attributes[@"bool"]);
|
XCTAssertEqual(boolean, shape.attributes[@"bool"]);
|
||||||
|
|
||||||
NSNumber *unsignedInteger = [shape attributeForKey:@"unsigned int"];
|
NSNumber *unsignedInteger = [shape attributeForKey:@"unsigned int"];
|
||||||
XCTAssertNotNil(unsignedInteger);
|
XCTAssertNotNil(unsignedInteger);
|
||||||
XCTAssertTrue([unsignedInteger isKindOfClass:[NSNumber class]]);
|
XCTAssertTrue([unsignedInteger isKindOfClass:[NSNumber class]]);
|
||||||
XCTAssertEqual(strcmp(unsignedInteger.objCType, @encode(uint64_t)), 0, @"Unsigned integer property should be converted to unsigned long long NSNumber");
|
XCTAssertEqual(strcmp(unsignedInteger.objCType, @encode(uint64_t)), 0,
|
||||||
|
@"Unsigned integer property should be converted to unsigned long long NSNumber");
|
||||||
XCTAssertEqual(unsignedInteger.unsignedLongLongValue, UINT64_MAX);
|
XCTAssertEqual(unsignedInteger.unsignedLongLongValue, UINT64_MAX);
|
||||||
XCTAssertEqual(unsignedInteger, shape.attributes[@"unsigned int"]);
|
XCTAssertEqual(unsignedInteger, shape.attributes[@"unsigned int"]);
|
||||||
|
|
||||||
NSNumber *integer = [shape attributeForKey:@"int"];
|
NSNumber *integer = [shape attributeForKey:@"int"];
|
||||||
XCTAssertNotNil(integer);
|
XCTAssertNotNil(integer);
|
||||||
XCTAssertTrue([integer isKindOfClass:[NSNumber class]]);
|
XCTAssertTrue([integer isKindOfClass:[NSNumber class]]);
|
||||||
XCTAssertEqual(strcmp(integer.objCType, @encode(int64_t)), 0, @"Integer property should be converted to long long NSNumber");
|
XCTAssertEqual(strcmp(integer.objCType, @encode(int64_t)), 0,
|
||||||
|
@"Integer property should be converted to long long NSNumber");
|
||||||
XCTAssertEqual(integer.longLongValue, INT64_MIN);
|
XCTAssertEqual(integer.longLongValue, INT64_MIN);
|
||||||
XCTAssertEqual(integer, shape.attributes[@"int"]);
|
XCTAssertEqual(integer, shape.attributes[@"int"]);
|
||||||
|
|
||||||
NSNumber *floatingPointNumber = [shape attributeForKey:@"double"];
|
NSNumber *floatingPointNumber = [shape attributeForKey:@"double"];
|
||||||
XCTAssertNotNil(floatingPointNumber);
|
XCTAssertNotNil(floatingPointNumber);
|
||||||
XCTAssertTrue([floatingPointNumber isKindOfClass:[NSNumber class]]);
|
XCTAssertTrue([floatingPointNumber isKindOfClass:[NSNumber class]]);
|
||||||
XCTAssertEqual(strcmp(floatingPointNumber.objCType, @encode(double)), 0, @"Floating-point number property should be converted to double NSNumber");
|
XCTAssertEqual(strcmp(floatingPointNumber.objCType, @encode(double)), 0,
|
||||||
|
@"Floating-point number property should be converted to double NSNumber");
|
||||||
XCTAssertEqual(floatingPointNumber.doubleValue, DBL_MAX);
|
XCTAssertEqual(floatingPointNumber.doubleValue, DBL_MAX);
|
||||||
XCTAssertEqual(floatingPointNumber, shape.attributes[@"double"]);
|
XCTAssertEqual(floatingPointNumber, shape.attributes[@"double"]);
|
||||||
|
|
||||||
@@ -177,7 +187,7 @@
|
|||||||
|
|
||||||
- (void)testPointFeatureGeoJSONDictionary {
|
- (void)testPointFeatureGeoJSONDictionary {
|
||||||
MLNPointFeature *pointFeature = [[MLNPointFeature alloc] init];
|
MLNPointFeature *pointFeature = [[MLNPointFeature alloc] init];
|
||||||
CLLocationCoordinate2D coordinate = { 10, 10 };
|
CLLocationCoordinate2D coordinate = {10, 10};
|
||||||
pointFeature.coordinate = coordinate;
|
pointFeature.coordinate = coordinate;
|
||||||
|
|
||||||
// A GeoJSON feature
|
// A GeoJSON feature
|
||||||
@@ -187,8 +197,8 @@
|
|||||||
// it has the correct type
|
// it has the correct type
|
||||||
XCTAssertEqualObjects(geoJSONFeature[@"type"], @"Feature");
|
XCTAssertEqualObjects(geoJSONFeature[@"type"], @"Feature");
|
||||||
// it has the correct geometry
|
// it has the correct geometry
|
||||||
NSDictionary *expectedGeometry = @{@"type": @"Point",
|
NSDictionary *expectedGeometry =
|
||||||
@"coordinates": @[@(coordinate.longitude), @(coordinate.latitude)]};
|
@{@"type" : @"Point", @"coordinates" : @[ @(coordinate.longitude), @(coordinate.latitude) ]};
|
||||||
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
||||||
// it has no "id" key (or value)
|
// it has no "id" key (or value)
|
||||||
XCTAssertNil(geoJSONFeature[@"id"]);
|
XCTAssertNil(geoJSONFeature[@"id"]);
|
||||||
@@ -203,7 +213,7 @@
|
|||||||
XCTAssertEqualObjects(geoJSONFeature[@"id"], pointFeature.identifier);
|
XCTAssertEqualObjects(geoJSONFeature[@"id"], pointFeature.identifier);
|
||||||
|
|
||||||
// when there are properties
|
// when there are properties
|
||||||
pointFeature.attributes = @{@"name": @"name-value"};
|
pointFeature.attributes = @{@"name" : @"name-value"};
|
||||||
|
|
||||||
// it has the properties value in the result
|
// it has the properties value in the result
|
||||||
geoJSONFeature = [pointFeature geoJSONDictionary];
|
geoJSONFeature = [pointFeature geoJSONDictionary];
|
||||||
@@ -211,137 +221,169 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testPolylineFeatureGeoJSONDictionary {
|
- (void)testPolylineFeatureGeoJSONDictionary {
|
||||||
CLLocationCoordinate2D coord1 = { 0, 0 };
|
CLLocationCoordinate2D coord1 = {0, 0};
|
||||||
CLLocationCoordinate2D coord2 = { 10, 10 };
|
CLLocationCoordinate2D coord2 = {10, 10};
|
||||||
CLLocationCoordinate2D coords[] = { coord1, coord2 };
|
CLLocationCoordinate2D coords[] = {coord1, coord2};
|
||||||
MLNPolylineFeature *polyLineFeature = [MLNPolylineFeature polylineWithCoordinates:coords count:2];
|
MLNPolylineFeature *polyLineFeature = [MLNPolylineFeature polylineWithCoordinates:coords count:2];
|
||||||
|
|
||||||
// A GeoJSON feature
|
// A GeoJSON feature
|
||||||
NSDictionary *geoJSONFeature = [polyLineFeature geoJSONDictionary];
|
NSDictionary *geoJSONFeature = [polyLineFeature geoJSONDictionary];
|
||||||
|
|
||||||
// it has the correct geometry
|
// it has the correct geometry
|
||||||
NSDictionary *expectedGeometry = @{@"type": @"LineString",
|
NSDictionary *expectedGeometry = @{
|
||||||
@"coordinates": @[@[@(coord1.longitude), @(coord1.latitude)],
|
@"type" : @"LineString",
|
||||||
@[@(coord2.longitude), @(coord2.latitude)]]};
|
@"coordinates" : @[
|
||||||
|
@[ @(coord1.longitude), @(coord1.latitude) ], @[ @(coord2.longitude), @(coord2.latitude) ]
|
||||||
|
]
|
||||||
|
};
|
||||||
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testPolygonFeatureGeoJSONDictionary {
|
- (void)testPolygonFeatureGeoJSONDictionary {
|
||||||
CLLocationCoordinate2D coord1 = { 0, 0 };
|
CLLocationCoordinate2D coord1 = {0, 0};
|
||||||
CLLocationCoordinate2D coord2 = { 10, 10 };
|
CLLocationCoordinate2D coord2 = {10, 10};
|
||||||
CLLocationCoordinate2D coord3 = { 0, 0 };
|
CLLocationCoordinate2D coord3 = {0, 0};
|
||||||
CLLocationCoordinate2D coords[] = { coord1, coord2, coord3 };
|
CLLocationCoordinate2D coords[] = {coord1, coord2, coord3};
|
||||||
MLNPolygonFeature *polygonFeature = [MLNPolygonFeature polygonWithCoordinates:coords count:3];
|
MLNPolygonFeature *polygonFeature = [MLNPolygonFeature polygonWithCoordinates:coords count:3];
|
||||||
|
|
||||||
// A GeoJSON feature
|
// A GeoJSON feature
|
||||||
NSDictionary *geoJSONFeature = [polygonFeature geoJSONDictionary];
|
NSDictionary *geoJSONFeature = [polygonFeature geoJSONDictionary];
|
||||||
|
|
||||||
// it has the correct geometry
|
// it has the correct geometry
|
||||||
NSDictionary *expectedGeometry = @{@"type": @"Polygon",
|
NSDictionary *expectedGeometry = @{
|
||||||
@"coordinates": @[@[@[@(coord1.longitude), @(coord1.latitude)],
|
@"type" : @"Polygon",
|
||||||
@[@(coord2.longitude), @(coord2.latitude)],
|
@"coordinates" : @[ @[
|
||||||
@[@(coord3.longitude), @(coord3.latitude)]]]};
|
@[ @(coord1.longitude), @(coord1.latitude) ], @[ @(coord2.longitude), @(coord2.latitude) ],
|
||||||
|
@[ @(coord3.longitude), @(coord3.latitude) ]
|
||||||
|
] ]
|
||||||
|
};
|
||||||
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMultiPolylineFeatureGeoJSONDictionary {
|
- (void)testMultiPolylineFeatureGeoJSONDictionary {
|
||||||
CLLocationCoordinate2D coord1 = { 0, 0 };
|
CLLocationCoordinate2D coord1 = {0, 0};
|
||||||
CLLocationCoordinate2D coord2 = { 10, 10 };
|
CLLocationCoordinate2D coord2 = {10, 10};
|
||||||
CLLocationCoordinate2D coord3 = { 0, 0 };
|
CLLocationCoordinate2D coord3 = {0, 0};
|
||||||
CLLocationCoordinate2D coords[] = { coord1, coord2, coord3 };
|
CLLocationCoordinate2D coords[] = {coord1, coord2, coord3};
|
||||||
|
|
||||||
MLNPolyline *polyLine1 = [MLNPolyline polylineWithCoordinates:coords count:3];
|
MLNPolyline *polyLine1 = [MLNPolyline polylineWithCoordinates:coords count:3];
|
||||||
MLNPolyline *polyLine2 = [MLNPolyline polylineWithCoordinates:coords count:3];
|
MLNPolyline *polyLine2 = [MLNPolyline polylineWithCoordinates:coords count:3];
|
||||||
|
|
||||||
MLNMultiPolylineFeature *multiPolylineFeature = [MLNMultiPolylineFeature multiPolylineWithPolylines:@[polyLine1, polyLine2]];
|
MLNMultiPolylineFeature *multiPolylineFeature =
|
||||||
|
[MLNMultiPolylineFeature multiPolylineWithPolylines:@[ polyLine1, polyLine2 ]];
|
||||||
|
|
||||||
// A GeoJSON feature
|
// A GeoJSON feature
|
||||||
NSDictionary *geoJSONFeature = [multiPolylineFeature geoJSONDictionary];
|
NSDictionary *geoJSONFeature = [multiPolylineFeature geoJSONDictionary];
|
||||||
|
|
||||||
// it has the correct geometry
|
// it has the correct geometry
|
||||||
NSDictionary *expectedGeometry = @{@"type": @"MultiLineString",
|
NSDictionary *expectedGeometry = @{
|
||||||
@"coordinates": @[@[@[@(coord1.longitude), @(coord1.latitude)],
|
@"type" : @"MultiLineString",
|
||||||
@[@(coord2.longitude), @(coord2.latitude)],
|
@"coordinates" : @[
|
||||||
@[@(coord3.longitude), @(coord3.latitude)]],
|
@[
|
||||||
@[@[@(coord1.longitude), @(coord1.latitude)],
|
@[ @(coord1.longitude), @(coord1.latitude) ], @[ @(coord2.longitude), @(coord2.latitude) ],
|
||||||
@[@(coord2.longitude), @(coord2.latitude)],
|
@[ @(coord3.longitude), @(coord3.latitude) ]
|
||||||
@[@(coord3.longitude), @(coord3.latitude)]]]};
|
],
|
||||||
|
@[
|
||||||
|
@[ @(coord1.longitude), @(coord1.latitude) ], @[ @(coord2.longitude), @(coord2.latitude) ],
|
||||||
|
@[ @(coord3.longitude), @(coord3.latitude) ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
};
|
||||||
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMultiPolygonFeatureGeoJSONDictionary {
|
- (void)testMultiPolygonFeatureGeoJSONDictionary {
|
||||||
CLLocationCoordinate2D coord1 = { 0, 0 };
|
CLLocationCoordinate2D coord1 = {0, 0};
|
||||||
CLLocationCoordinate2D coord2 = { 10, 10 };
|
CLLocationCoordinate2D coord2 = {10, 10};
|
||||||
CLLocationCoordinate2D coord3 = { 0, 0 };
|
CLLocationCoordinate2D coord3 = {0, 0};
|
||||||
CLLocationCoordinate2D coords[] = { coord1, coord2, coord3 };
|
CLLocationCoordinate2D coords[] = {coord1, coord2, coord3};
|
||||||
|
|
||||||
MLNPolygon *polygon1 = [MLNPolygon polygonWithCoordinates:coords count:3];
|
MLNPolygon *polygon1 = [MLNPolygon polygonWithCoordinates:coords count:3];
|
||||||
MLNPolygon *polygon2 = [MLNPolygon polygonWithCoordinates:coords count:3];
|
MLNPolygon *polygon2 = [MLNPolygon polygonWithCoordinates:coords count:3];
|
||||||
|
|
||||||
MLNMultiPolygonFeature *multiPolylineFeature = [MLNMultiPolygonFeature multiPolygonWithPolygons:@[polygon1, polygon2]];
|
MLNMultiPolygonFeature *multiPolylineFeature =
|
||||||
|
[MLNMultiPolygonFeature multiPolygonWithPolygons:@[ polygon1, polygon2 ]];
|
||||||
|
|
||||||
// A GeoJSON feature
|
// A GeoJSON feature
|
||||||
NSDictionary *geoJSONFeature = [multiPolylineFeature geoJSONDictionary];
|
NSDictionary *geoJSONFeature = [multiPolylineFeature geoJSONDictionary];
|
||||||
|
|
||||||
// it has the correct geometry
|
// it has the correct geometry
|
||||||
NSDictionary *expectedGeometry = @{@"type": @"MultiPolygon",
|
NSDictionary *expectedGeometry = @{
|
||||||
@"coordinates": @[
|
@"type" : @"MultiPolygon",
|
||||||
@[@[@[@(coord1.longitude), @(coord1.latitude)],
|
@"coordinates" : @[
|
||||||
@[@(coord2.longitude), @(coord2.latitude)],
|
@[ @[
|
||||||
@[@(coord3.longitude), @(coord3.latitude)]]],
|
@[ @(coord1.longitude), @(coord1.latitude) ], @[ @(coord2.longitude), @(coord2.latitude) ],
|
||||||
@[@[@[@(coord1.longitude), @(coord1.latitude)],
|
@[ @(coord3.longitude), @(coord3.latitude) ]
|
||||||
@[@(coord2.longitude), @(coord2.latitude)],
|
] ],
|
||||||
@[@(coord3.longitude), @(coord3.latitude)]]]]};
|
@[ @[
|
||||||
|
@[ @(coord1.longitude), @(coord1.latitude) ], @[ @(coord2.longitude), @(coord2.latitude) ],
|
||||||
|
@[ @(coord3.longitude), @(coord3.latitude) ]
|
||||||
|
] ]
|
||||||
|
]
|
||||||
|
};
|
||||||
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testPointCollectionFeatureGeoJSONDictionary {
|
- (void)testPointCollectionFeatureGeoJSONDictionary {
|
||||||
CLLocationCoordinate2D coord1 = { 0, 0 };
|
CLLocationCoordinate2D coord1 = {0, 0};
|
||||||
CLLocationCoordinate2D coord2 = { 10, 10 };
|
CLLocationCoordinate2D coord2 = {10, 10};
|
||||||
CLLocationCoordinate2D coord3 = { 0, 0 };
|
CLLocationCoordinate2D coord3 = {0, 0};
|
||||||
CLLocationCoordinate2D coords[] = { coord1, coord2, coord3 };
|
CLLocationCoordinate2D coords[] = {coord1, coord2, coord3};
|
||||||
MLNPointCollectionFeature *pointCollectionFeature = [MLNPointCollectionFeature pointCollectionWithCoordinates:coords count:3];
|
MLNPointCollectionFeature *pointCollectionFeature =
|
||||||
|
[MLNPointCollectionFeature pointCollectionWithCoordinates:coords count:3];
|
||||||
|
|
||||||
// A GeoJSON feature
|
// A GeoJSON feature
|
||||||
NSDictionary *geoJSONFeature = [pointCollectionFeature geoJSONDictionary];
|
NSDictionary *geoJSONFeature = [pointCollectionFeature geoJSONDictionary];
|
||||||
|
|
||||||
// it has the correct geometry
|
// it has the correct geometry
|
||||||
NSDictionary *expectedGeometry = @{@"type": @"MultiPoint",
|
NSDictionary *expectedGeometry = @{
|
||||||
@"coordinates": @[@[@(coord1.longitude), @(coord1.latitude)],
|
@"type" : @"MultiPoint",
|
||||||
@[@(coord2.longitude), @(coord2.latitude)],
|
@"coordinates" : @[
|
||||||
@[@(coord3.longitude), @(coord3.latitude)]]};
|
@[ @(coord1.longitude), @(coord1.latitude) ], @[ @(coord2.longitude), @(coord2.latitude) ],
|
||||||
|
@[ @(coord3.longitude), @(coord3.latitude) ]
|
||||||
|
]
|
||||||
|
};
|
||||||
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testShapeCollectionFeatureGeoJSONDictionary {
|
- (void)testShapeCollectionFeatureGeoJSONDictionary {
|
||||||
MLNPointFeature *pointFeature = [[MLNPointFeature alloc] init];
|
MLNPointFeature *pointFeature = [[MLNPointFeature alloc] init];
|
||||||
CLLocationCoordinate2D pointCoordinate = { 10, 10 };
|
CLLocationCoordinate2D pointCoordinate = {10, 10};
|
||||||
pointFeature.coordinate = pointCoordinate;
|
pointFeature.coordinate = pointCoordinate;
|
||||||
|
|
||||||
CLLocationCoordinate2D coord1 = { 0, 0 };
|
CLLocationCoordinate2D coord1 = {0, 0};
|
||||||
CLLocationCoordinate2D coord2 = { 10, 10 };
|
CLLocationCoordinate2D coord2 = {10, 10};
|
||||||
CLLocationCoordinate2D coords[] = { coord1, coord2 };
|
CLLocationCoordinate2D coords[] = {coord1, coord2};
|
||||||
MLNPolylineFeature *polylineFeature = [MLNPolylineFeature polylineWithCoordinates:coords count:2];
|
MLNPolylineFeature *polylineFeature = [MLNPolylineFeature polylineWithCoordinates:coords count:2];
|
||||||
|
|
||||||
MLNShapeCollectionFeature *shapeCollectionFeature = [MLNShapeCollectionFeature shapeCollectionWithShapes:@[pointFeature, polylineFeature]];
|
MLNShapeCollectionFeature *shapeCollectionFeature =
|
||||||
|
[MLNShapeCollectionFeature shapeCollectionWithShapes:@[ pointFeature, polylineFeature ]];
|
||||||
|
|
||||||
// A GeoJSON feature
|
// A GeoJSON feature
|
||||||
NSDictionary *geoJSONFeature = [shapeCollectionFeature geoJSONDictionary];
|
NSDictionary *geoJSONFeature = [shapeCollectionFeature geoJSONDictionary];
|
||||||
|
|
||||||
// it has the correct geometry
|
// it has the correct geometry
|
||||||
NSDictionary *expectedGeometry = @{@"type": @"GeometryCollection",
|
NSDictionary *expectedGeometry = @{
|
||||||
@"geometries": @[
|
@"type" : @"GeometryCollection",
|
||||||
@{ @"geometry": @{@"type": @"Point",
|
@"geometries" : @[
|
||||||
@"coordinates": @[@(pointCoordinate.longitude), @(pointCoordinate.latitude)]},
|
@{
|
||||||
@"properties": @{},
|
@"geometry" : @{
|
||||||
@"type": @"Feature",
|
@"type" : @"Point",
|
||||||
|
@"coordinates" : @[ @(pointCoordinate.longitude), @(pointCoordinate.latitude) ]
|
||||||
},
|
},
|
||||||
@{ @"geometry": @{@"type": @"LineString",
|
@"properties" : @{},
|
||||||
@"coordinates": @[@[@(coord1.longitude), @(coord1.latitude)],
|
@"type" : @"Feature",
|
||||||
@[@(coord2.longitude), @(coord2.latitude)]]},
|
},
|
||||||
@"properties": @{},
|
@{
|
||||||
@"type": @"Feature",
|
@"geometry" : @{
|
||||||
|
@"type" : @"LineString",
|
||||||
|
@"coordinates" : @[
|
||||||
|
@[ @(coord1.longitude), @(coord1.latitude) ],
|
||||||
|
@[ @(coord2.longitude), @(coord2.latitude) ]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
@"properties" : @{},
|
||||||
|
@"type" : @"Feature",
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -352,8 +394,7 @@
|
|||||||
|
|
||||||
// it has the correct (empty) geometry
|
// it has the correct (empty) geometry
|
||||||
geoJSONFeature = [shapeCollectionFeature geoJSONDictionary];
|
geoJSONFeature = [shapeCollectionFeature geoJSONDictionary];
|
||||||
expectedGeometry = @{@"type": @"GeometryCollection",
|
expectedGeometry = @{@"type" : @"GeometryCollection", @"geometries" : @[]};
|
||||||
@"geometries": @[]};
|
|
||||||
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,11 @@
|
|||||||
@implementation MLNGeometryTests
|
@implementation MLNGeometryTests
|
||||||
|
|
||||||
- (void)testCoordinateBoundsIsEmpty {
|
- (void)testCoordinateBoundsIsEmpty {
|
||||||
MLNCoordinateBounds emptyBounds = MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 0));
|
MLNCoordinateBounds emptyBounds =
|
||||||
|
MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 0));
|
||||||
XCTAssertTrue(MLNCoordinateBoundsIsEmpty(emptyBounds));
|
XCTAssertTrue(MLNCoordinateBoundsIsEmpty(emptyBounds));
|
||||||
XCTAssertFalse(MLNCoordinateSpanEqualToCoordinateSpan(MLNCoordinateSpanZero, MLNCoordinateBoundsGetCoordinateSpan(emptyBounds)));
|
XCTAssertFalse(MLNCoordinateSpanEqualToCoordinateSpan(
|
||||||
|
MLNCoordinateSpanZero, MLNCoordinateBoundsGetCoordinateSpan(emptyBounds)));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testAngleConversions {
|
- (void)testAngleConversions {
|
||||||
@@ -39,36 +41,57 @@
|
|||||||
CGSize midSize = CGSizeMake(600, 800);
|
CGSize midSize = CGSizeMake(600, 800);
|
||||||
CGSize shortSize = CGSizeMake(600, 400);
|
CGSize shortSize = CGSizeMake(600, 400);
|
||||||
|
|
||||||
XCTAssertEqualWithAccuracy(1800, MLNAltitudeForZoomLevel(MLNZoomLevelForAltitude(1800, 0, 0, midSize), 0, 0, midSize), 1e-8);
|
XCTAssertEqualWithAccuracy(
|
||||||
XCTAssertLessThan(MLNZoomLevelForAltitude(1800, 0, 0, midSize), MLNZoomLevelForAltitude(1800, 0, 0, tallSize));
|
1800, MLNAltitudeForZoomLevel(MLNZoomLevelForAltitude(1800, 0, 0, midSize), 0, 0, midSize),
|
||||||
XCTAssertGreaterThan(MLNZoomLevelForAltitude(1800, 0, 0, midSize), MLNZoomLevelForAltitude(1800, 0, 0, shortSize));
|
1e-8);
|
||||||
|
XCTAssertLessThan(MLNZoomLevelForAltitude(1800, 0, 0, midSize),
|
||||||
|
MLNZoomLevelForAltitude(1800, 0, 0, tallSize));
|
||||||
|
XCTAssertGreaterThan(MLNZoomLevelForAltitude(1800, 0, 0, midSize),
|
||||||
|
MLNZoomLevelForAltitude(1800, 0, 0, shortSize));
|
||||||
|
|
||||||
XCTAssertEqualWithAccuracy(0, MLNZoomLevelForAltitude(MLNAltitudeForZoomLevel(0, 0, 0, midSize), 0, 0, midSize), 1e-8);
|
XCTAssertEqualWithAccuracy(
|
||||||
XCTAssertEqualWithAccuracy(18, MLNZoomLevelForAltitude(MLNAltitudeForZoomLevel(18, 0, 0, midSize), 0, 0, midSize), 1e-8);
|
0, MLNZoomLevelForAltitude(MLNAltitudeForZoomLevel(0, 0, 0, midSize), 0, 0, midSize), 1e-8);
|
||||||
|
XCTAssertEqualWithAccuracy(
|
||||||
|
18, MLNZoomLevelForAltitude(MLNAltitudeForZoomLevel(18, 0, 0, midSize), 0, 0, midSize), 1e-8);
|
||||||
|
|
||||||
XCTAssertEqualWithAccuracy(0, MLNZoomLevelForAltitude(MLNAltitudeForZoomLevel(0, 0, 40, midSize), 0, 40, midSize), 1e-8);
|
XCTAssertEqualWithAccuracy(
|
||||||
XCTAssertEqualWithAccuracy(18, MLNZoomLevelForAltitude(MLNAltitudeForZoomLevel(18, 0, 40, midSize), 0, 40, midSize), 1e-8);
|
0, MLNZoomLevelForAltitude(MLNAltitudeForZoomLevel(0, 0, 40, midSize), 0, 40, midSize), 1e-8);
|
||||||
|
XCTAssertEqualWithAccuracy(
|
||||||
|
18, MLNZoomLevelForAltitude(MLNAltitudeForZoomLevel(18, 0, 40, midSize), 0, 40, midSize),
|
||||||
|
1e-8);
|
||||||
|
|
||||||
XCTAssertEqualWithAccuracy(0, MLNZoomLevelForAltitude(MLNAltitudeForZoomLevel(0, 60, 40, midSize), 60, 40, midSize), 1e-8);
|
XCTAssertEqualWithAccuracy(
|
||||||
XCTAssertEqualWithAccuracy(18, MLNZoomLevelForAltitude(MLNAltitudeForZoomLevel(18, 60, 40, midSize), 60, 40, midSize), 1e-8);
|
0, MLNZoomLevelForAltitude(MLNAltitudeForZoomLevel(0, 60, 40, midSize), 60, 40, midSize),
|
||||||
|
1e-8);
|
||||||
|
XCTAssertEqualWithAccuracy(
|
||||||
|
18, MLNZoomLevelForAltitude(MLNAltitudeForZoomLevel(18, 60, 40, midSize), 60, 40, midSize),
|
||||||
|
1e-8);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testGeometryBoxing {
|
- (void)testGeometryBoxing {
|
||||||
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239);
|
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239);
|
||||||
CLLocationCoordinate2D roundTrippedCoordinate = [NSValue valueWithMLNCoordinate:coordinate].MLNCoordinateValue;
|
CLLocationCoordinate2D roundTrippedCoordinate =
|
||||||
|
[NSValue valueWithMLNCoordinate:coordinate].MLNCoordinateValue;
|
||||||
|
|
||||||
XCTAssertEqual(coordinate.latitude, roundTrippedCoordinate.latitude, @"Latitude should round-trip.");
|
XCTAssertEqual(coordinate.latitude, roundTrippedCoordinate.latitude,
|
||||||
XCTAssertEqual(coordinate.longitude, roundTrippedCoordinate.longitude, @"Longitude should round-trip.");
|
@"Latitude should round-trip.");
|
||||||
|
XCTAssertEqual(coordinate.longitude, roundTrippedCoordinate.longitude,
|
||||||
|
@"Longitude should round-trip.");
|
||||||
|
|
||||||
MLNCoordinateSpan span = MLNCoordinateSpanMake(4.383333333333335, -4.299999999999997);
|
MLNCoordinateSpan span = MLNCoordinateSpanMake(4.383333333333335, -4.299999999999997);
|
||||||
MLNCoordinateSpan roundTrippedSpan = [NSValue valueWithMLNCoordinateSpan:span].MLNCoordinateSpanValue;
|
MLNCoordinateSpan roundTrippedSpan =
|
||||||
|
[NSValue valueWithMLNCoordinateSpan:span].MLNCoordinateSpanValue;
|
||||||
|
|
||||||
XCTAssertEqual(span.latitudeDelta, roundTrippedSpan.latitudeDelta, @"Latitude delta should round-trip.");
|
XCTAssertEqual(span.latitudeDelta, roundTrippedSpan.latitudeDelta,
|
||||||
XCTAssertEqual(span.longitudeDelta, roundTrippedSpan.longitudeDelta, @"Longitude delta should round-trip.");
|
@"Latitude delta should round-trip.");
|
||||||
|
XCTAssertEqual(span.longitudeDelta, roundTrippedSpan.longitudeDelta,
|
||||||
|
@"Longitude delta should round-trip.");
|
||||||
|
|
||||||
MLNCoordinateBounds bounds = MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(38.9131982, -77.0325453144239),
|
MLNCoordinateBounds bounds =
|
||||||
|
MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(38.9131982, -77.0325453144239),
|
||||||
CLLocationCoordinate2DMake(37.7757368, -122.4135302));
|
CLLocationCoordinate2DMake(37.7757368, -122.4135302));
|
||||||
MLNCoordinateBounds roundTrippedBounds = [NSValue valueWithMLNCoordinateBounds:bounds].MLNCoordinateBoundsValue;
|
MLNCoordinateBounds roundTrippedBounds =
|
||||||
|
[NSValue valueWithMLNCoordinateBounds:bounds].MLNCoordinateBoundsValue;
|
||||||
|
|
||||||
XCTAssertEqualObjects([NSValue valueWithMLNCoordinate:bounds.sw],
|
XCTAssertEqualObjects([NSValue valueWithMLNCoordinate:bounds.sw],
|
||||||
[NSValue valueWithMLNCoordinate:roundTrippedBounds.sw],
|
[NSValue valueWithMLNCoordinate:roundTrippedBounds.sw],
|
||||||
@@ -100,17 +123,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testGeoJSONDeserialization {
|
- (void)testGeoJSONDeserialization {
|
||||||
NSData *data = [@"{\"type\": \"Feature\", \"geometry\": {\"type\": \"Point\", \"coordinates\": [0, 0]}, \"properties\": {}}" dataUsingEncoding:NSUTF8StringEncoding];
|
NSData *data = [@"{\"type\": \"Feature\", \"geometry\": {\"type\": \"Point\", \"coordinates\": "
|
||||||
|
@"[0, 0]}, \"properties\": {}}" dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
NSError *error;
|
NSError *error;
|
||||||
MLNPointFeature *feature = (MLNPointFeature *)[MLNShape shapeWithData:data encoding:NSUTF8StringEncoding error:&error];
|
MLNPointFeature *feature = (MLNPointFeature *)[MLNShape shapeWithData:data
|
||||||
|
encoding:NSUTF8StringEncoding
|
||||||
|
error:&error];
|
||||||
XCTAssertNil(error, @"Valid GeoJSON data should produce no error on deserialization.");
|
XCTAssertNil(error, @"Valid GeoJSON data should produce no error on deserialization.");
|
||||||
XCTAssertNotNil(feature, @"Valid GeoJSON data should produce an object on deserialization.");
|
XCTAssertNotNil(feature, @"Valid GeoJSON data should produce an object on deserialization.");
|
||||||
XCTAssertTrue([feature isKindOfClass:[MLNPointFeature class]], @"Valid GeoJSON point feature data should produce an MLNPointFeature.");
|
XCTAssertTrue([feature isKindOfClass:[MLNPointFeature class]],
|
||||||
|
@"Valid GeoJSON point feature data should produce an MLNPointFeature.");
|
||||||
XCTAssertEqual(feature.attributes.count, 0UL);
|
XCTAssertEqual(feature.attributes.count, 0UL);
|
||||||
XCTAssertEqual(feature.coordinate.latitude, 0);
|
XCTAssertEqual(feature.coordinate.latitude, 0);
|
||||||
XCTAssertEqual(feature.coordinate.longitude, 0);
|
XCTAssertEqual(feature.coordinate.longitude, 0);
|
||||||
|
|
||||||
data = [@"{\"type\": \"Feature\", \"feature\": {\"type\": \"Point\", \"coordinates\": [0, 0]}}" dataUsingEncoding:NSUTF8StringEncoding];
|
data = [@"{\"type\": \"Feature\", \"feature\": {\"type\": \"Point\", \"coordinates\": [0, 0]}}"
|
||||||
|
dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
error = nil;
|
error = nil;
|
||||||
MLNShape *shape = [MLNShape shapeWithData:data encoding:NSUTF8StringEncoding error:&error];
|
MLNShape *shape = [MLNShape shapeWithData:data encoding:NSUTF8StringEncoding error:&error];
|
||||||
XCTAssertNotNil(error, @"Invalid GeoJSON data should produce an error on deserialization.");
|
XCTAssertNotNil(error, @"Invalid GeoJSON data should produce an error on deserialization.");
|
||||||
@@ -125,23 +153,27 @@
|
|||||||
NSData *data = [feature geoJSONDataUsingEncoding:NSUTF8StringEncoding];
|
NSData *data = [feature geoJSONDataUsingEncoding:NSUTF8StringEncoding];
|
||||||
XCTAssertNotNil(data, @"MLNPointFeature should serialize as an UTF-8 string data object.");
|
XCTAssertNotNil(data, @"MLNPointFeature should serialize as an UTF-8 string data object.");
|
||||||
NSError *error;
|
NSError *error;
|
||||||
NSDictionary *serializedGeoJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
NSDictionary *serializedGeoJSON = [NSJSONSerialization JSONObjectWithData:data
|
||||||
|
options:0
|
||||||
|
error:&error];
|
||||||
XCTAssertNil(error, @"Serialized GeoJSON data should be deserializable JSON.");
|
XCTAssertNil(error, @"Serialized GeoJSON data should be deserializable JSON.");
|
||||||
XCTAssertNotNil(serializedGeoJSON, @"Serialized GeoJSON data should be valid JSON.");
|
XCTAssertNotNil(serializedGeoJSON, @"Serialized GeoJSON data should be valid JSON.");
|
||||||
XCTAssertTrue([serializedGeoJSON isKindOfClass:[NSDictionary class]], @"Serialized GeoJSON data should be a JSON object.");
|
XCTAssertTrue([serializedGeoJSON isKindOfClass:[NSDictionary class]],
|
||||||
|
@"Serialized GeoJSON data should be a JSON object.");
|
||||||
NSDictionary *geoJSON = @{
|
NSDictionary *geoJSON = @{
|
||||||
@"type": @"Feature",
|
@"type" : @"Feature",
|
||||||
@"id": @504,
|
@"id" : @504,
|
||||||
@"geometry": @{
|
@"geometry" : @{
|
||||||
@"type": @"Point",
|
@"type" : @"Point",
|
||||||
@"coordinates": @[
|
@"coordinates" : @[
|
||||||
@(-90.066667),
|
@(-90.066667),
|
||||||
@29.95,
|
@29.95,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@"properties": @{},
|
@"properties" : @{},
|
||||||
};
|
};
|
||||||
XCTAssertEqualObjects(serializedGeoJSON, geoJSON, @"MLNPointFeature should serialize as a GeoJSON point feature.");
|
XCTAssertEqualObjects(serializedGeoJSON, geoJSON,
|
||||||
|
@"MLNPointFeature should serialize as a GeoJSON point feature.");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMLNCoordinateBoundsToMLNCoordinateQuad {
|
- (void)testMLNCoordinateBoundsToMLNCoordinateQuad {
|
||||||
@@ -156,10 +188,12 @@
|
|||||||
[NSValue valueWithMLNCoordinate:quad.topRight],
|
[NSValue valueWithMLNCoordinate:quad.topRight],
|
||||||
@"Bounds northeast should be top right of quad.");
|
@"Bounds northeast should be top right of quad.");
|
||||||
|
|
||||||
XCTAssertEqualObjects([NSValue valueWithMLNCoordinate:CLLocationCoordinate2DMake(46.437, -80.425)],
|
XCTAssertEqualObjects(
|
||||||
|
[NSValue valueWithMLNCoordinate:CLLocationCoordinate2DMake(46.437, -80.425)],
|
||||||
[NSValue valueWithMLNCoordinate:quad.topLeft],
|
[NSValue valueWithMLNCoordinate:quad.topLeft],
|
||||||
@"Quad top left should be computed correctly.");
|
@"Quad top left should be computed correctly.");
|
||||||
XCTAssertEqualObjects([NSValue valueWithMLNCoordinate:CLLocationCoordinate2DMake(37.936, -71.516)],
|
XCTAssertEqualObjects(
|
||||||
|
[NSValue valueWithMLNCoordinate:CLLocationCoordinate2DMake(37.936, -71.516)],
|
||||||
[NSValue valueWithMLNCoordinate:quad.bottomRight],
|
[NSValue valueWithMLNCoordinate:quad.bottomRight],
|
||||||
@"Quad bottom right should be computed correctly.");
|
@"Quad bottom right should be computed correctly.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,13 @@
|
|||||||
|
|
||||||
- (void)testProperties {
|
- (void)testProperties {
|
||||||
MLNPointFeature *feature = [[MLNPointFeature alloc] init];
|
MLNPointFeature *feature = [[MLNPointFeature alloc] init];
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"sourceID"
|
||||||
MLNHeatmapStyleLayer *layer = [[MLNHeatmapStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
|
shape:feature
|
||||||
|
options:nil];
|
||||||
|
MLNHeatmapStyleLayer *layer = [[MLNHeatmapStyleLayer alloc] initWithIdentifier:@"layerID"
|
||||||
|
source:source];
|
||||||
|
|
||||||
auto rawLayer = static_cast<mbgl::style::HeatmapLayer*>(layer.rawLayer);
|
auto rawLayer = static_cast<mbgl::style::HeatmapLayer *>(layer.rawLayer);
|
||||||
|
|
||||||
XCTAssertTrue(rawLayer->getHeatmapColor().isUndefined(),
|
XCTAssertTrue(rawLayer->getHeatmapColor().isUndefined(),
|
||||||
@"heatmap-color should be unset initially.");
|
@"heatmap-color should be unset initially.");
|
||||||
@@ -24,27 +27,35 @@
|
|||||||
NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MLNColor redColor]];
|
NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MLNColor redColor]];
|
||||||
layer.heatmapColor = constantExpression;
|
layer.heatmapColor = constantExpression;
|
||||||
|
|
||||||
|
mbgl::style::PropertyValue<float> propertyValue = {0xff};
|
||||||
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
|
XCTAssertEqual(
|
||||||
XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(0.0), mbgl::Color::red(),
|
rawLayer->getHeatmapColor().evaluate(0.0), mbgl::Color::red(),
|
||||||
@"Setting heatmapColor to a constant value expression should update heatmap-color.");
|
@"Setting heatmapColor to a constant value expression should update heatmap-color.");
|
||||||
XCTAssertEqualObjects(layer.heatmapColor, constantExpression,
|
XCTAssertEqualObjects(layer.heatmapColor, constantExpression,
|
||||||
@"heatmapColor should round-trip constant value expressions.");
|
@"heatmapColor should round-trip constant value expressions.");
|
||||||
|
|
||||||
constantExpression = [NSExpression expressionWithFormat:@"%@", [MLNColor redColor]];
|
constantExpression = [NSExpression expressionWithFormat:@"%@", [MLNColor redColor]];
|
||||||
NSExpression *constantExpression2 = [NSExpression expressionWithFormat:@"%@", [MLNColor blueColor]];
|
NSExpression *constantExpression2 =
|
||||||
|
[NSExpression expressionWithFormat:@"%@", [MLNColor blueColor]];
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
XCTExpectFailure(@"Awaiting unit test refactoring for https://github.com/maplibre/maplibre-native/issues/331");
|
XCTExpectFailure(
|
||||||
|
@"Awaiting unit test refactoring for https://github.com/maplibre/maplibre-native/issues/331");
|
||||||
#endif
|
#endif
|
||||||
NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($heatmapDensity, %@, %@)", constantExpression, @{@12: constantExpression2}];
|
NSExpression *functionExpression =
|
||||||
|
[NSExpression expressionWithFormat:@"mgl_step:from:stops:($heatmapDensity, %@, %@)",
|
||||||
|
constantExpression, @{@12 : constantExpression2}];
|
||||||
layer.heatmapColor = functionExpression;
|
layer.heatmapColor = functionExpression;
|
||||||
|
|
||||||
XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(11.0), mbgl::Color::red(),
|
XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(11.0), mbgl::Color::red(),
|
||||||
@"Setting heatmapColor to an expression depending on $heatmapDensity should update heatmap-color.");
|
@"Setting heatmapColor to an expression depending on $heatmapDensity should "
|
||||||
|
@"update heatmap-color.");
|
||||||
XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(12.0), mbgl::Color::blue(),
|
XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(12.0), mbgl::Color::blue(),
|
||||||
@"Setting heatmapColor to an expression depending on $heatmapDensity should update heatmap-color.");
|
@"Setting heatmapColor to an expression depending on $heatmapDensity should "
|
||||||
XCTExpectFailure(@"Awaiting unit test refactoring for https://github.com/maplibre/maplibre-native/issues/331");
|
@"update heatmap-color.");
|
||||||
XCTAssertEqualObjects(layer.heatmapColor, functionExpression,
|
XCTExpectFailure(
|
||||||
|
@"Awaiting unit test refactoring for https://github.com/maplibre/maplibre-native/issues/331");
|
||||||
|
XCTAssertEqualObjects(
|
||||||
|
layer.heatmapColor, functionExpression,
|
||||||
@"heatmapColor should round-trip expressions depending on $heatmapDensity.");
|
@"heatmapColor should round-trip expressions depending on $heatmapDensity.");
|
||||||
|
|
||||||
layer.heatmapColor = nil;
|
layer.heatmapColor = nil;
|
||||||
@@ -54,13 +65,29 @@
|
|||||||
XCTAssertEqualObjects(layer.heatmapColor.description, defaultExpression.description,
|
XCTAssertEqualObjects(layer.heatmapColor.description, defaultExpression.description,
|
||||||
@"heatmapColor should return the default value after being unset.");
|
@"heatmapColor should return the default value after being unset.");
|
||||||
|
|
||||||
functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
|
functionExpression =
|
||||||
XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MLNHeatmapLayer should raise an exception if a camera expression is applied to heatmapColor.");
|
[NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)",
|
||||||
|
constantExpression, @{@18 : constantExpression}];
|
||||||
|
XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException,
|
||||||
|
NSInvalidArgumentException,
|
||||||
|
@"MLNHeatmapLayer should raise an exception if a camera expression "
|
||||||
|
@"is applied to heatmapColor.");
|
||||||
functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
|
functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
|
||||||
XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MLNHeatmapLayer should raise an exception if a data expression is applied to heatmapColor.");
|
XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException,
|
||||||
functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
|
NSInvalidArgumentException,
|
||||||
functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
|
@"MLNHeatmapLayer should raise an exception if a data expression is "
|
||||||
XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MLNHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
|
@"applied to heatmapColor.");
|
||||||
|
functionExpression =
|
||||||
|
[NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression,
|
||||||
|
@{@18 : constantExpression}];
|
||||||
|
functionExpression = [NSExpression
|
||||||
|
expressionWithFormat:
|
||||||
|
@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)",
|
||||||
|
@{@10 : functionExpression}];
|
||||||
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException,
|
||||||
|
@"MLNHeatmapLayer should raise an exception if a camera-data expression is applied to a "
|
||||||
|
@"property that does not support key paths to feature attributes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
#import <XCTest/XCTest.h>
|
|
||||||
#import <Mapbox.h>
|
#import <Mapbox.h>
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
#import "UIImage+MLNAdditions.h"
|
#import "UIImage+MLNAdditions.h"
|
||||||
#define MLNImageResizingModeTile UIImageResizingModeTile
|
#define MLNImageResizingModeTile UIImageResizingModeTile
|
||||||
#define MLNImageResizingModeStretch UIImageResizingModeStretch
|
#define MLNImageResizingModeStretch UIImageResizingModeStretch
|
||||||
#else
|
#else
|
||||||
#import "NSImage+MLNAdditions.h"
|
#import "NSImage+MLNAdditions.h"
|
||||||
#define MLNImageResizingModeTile NSImageResizingModeTile
|
#define MLNImageResizingModeTile NSImageResizingModeTile
|
||||||
#define MLNImageResizingModeStretch NSImageResizingModeStretch
|
#define MLNImageResizingModeStretch NSImageResizingModeStretch
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@interface MLNImageTests : XCTestCase
|
@interface MLNImageTests : XCTestCase
|
||||||
@@ -27,11 +27,17 @@
|
|||||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
UIGraphicsEndImageContext();
|
UIGraphicsEndImageContext();
|
||||||
#else
|
#else
|
||||||
NSImage *image = [NSImage imageWithSize:NSMakeSize(24, 24) flipped:NO drawingHandler:^BOOL(NSRect dstRect) {
|
NSImage *image = [NSImage
|
||||||
// A little more fanciful than the iOS version, but we aren’t testing the actual contents of the image anyways.
|
imageWithSize:NSMakeSize(24, 24)
|
||||||
NSRectEdge allSides[] = {NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge, NSMaxXEdge};
|
flipped:NO
|
||||||
|
drawingHandler:^BOOL(NSRect dstRect) {
|
||||||
|
// A little more fanciful than the iOS version, but we aren’t testing the actual contents of
|
||||||
|
// the image anyways.
|
||||||
|
NSRectEdge allSides[] = {NSMinYEdge, NSMaxXEdge, NSMaxYEdge,
|
||||||
|
NSMinXEdge, NSMinYEdge, NSMaxXEdge};
|
||||||
CGFloat grays[] = {NSBlack, NSBlack, NSWhite, NSWhite, NSDarkGray, NSDarkGray};
|
CGFloat grays[] = {NSBlack, NSBlack, NSWhite, NSWhite, NSDarkGray, NSDarkGray};
|
||||||
dstRect = NSDrawTiledRects(dstRect, dstRect, allSides, grays, sizeof(grays) / sizeof(grays[0]));
|
dstRect =
|
||||||
|
NSDrawTiledRects(dstRect, dstRect, allSides, grays, sizeof(grays) / sizeof(grays[0]));
|
||||||
[NSColor.grayColor set];
|
[NSColor.grayColor set];
|
||||||
NSRectFill(dstRect);
|
NSRectFill(dstRect);
|
||||||
return YES;
|
return YES;
|
||||||
@@ -58,7 +64,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
image = [image resizableImageWithCapInsets:UIEdgeInsetsZero resizingMode:UIImageResizingModeStretch];
|
image = [image resizableImageWithCapInsets:UIEdgeInsetsZero
|
||||||
|
resizingMode:UIImageResizingModeStretch];
|
||||||
#else
|
#else
|
||||||
image.resizingMode = NSImageResizingModeStretch;
|
image.resizingMode = NSImageResizingModeStretch;
|
||||||
image.capInsets = NSEdgeInsetsZero;
|
image.capInsets = NSEdgeInsetsZero;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ using namespace std::chrono_literals;
|
|||||||
@implementation MLNNSDateAdditionsTests
|
@implementation MLNNSDateAdditionsTests
|
||||||
|
|
||||||
- (void)testDurationToNSTimeInterval {
|
- (void)testDurationToNSTimeInterval {
|
||||||
|
|
||||||
NSTimeInterval timeInterval = 5;
|
NSTimeInterval timeInterval = 5;
|
||||||
mbgl::Duration duration = MLNDurationFromTimeInterval(timeInterval);
|
mbgl::Duration duration = MLNDurationFromTimeInterval(timeInterval);
|
||||||
NSTimeInterval durationTimeInterval = MLNTimeIntervalFromDuration(duration);
|
NSTimeInterval durationTimeInterval = MLNTimeIntervalFromDuration(duration);
|
||||||
@@ -34,7 +33,6 @@ using namespace std::chrono_literals;
|
|||||||
XCTAssertEqual(NSTimeInterval(2.5), MLNTimeIntervalFromDuration(durationMiliSeconds));
|
XCTAssertEqual(NSTimeInterval(2.5), MLNTimeIntervalFromDuration(durationMiliSeconds));
|
||||||
XCTAssertEqual(NSTimeInterval(2.5), MLNTimeIntervalFromDuration(durationMicroSeconds));
|
XCTAssertEqual(NSTimeInterval(2.5), MLNTimeIntervalFromDuration(durationMicroSeconds));
|
||||||
XCTAssertEqual(NSTimeInterval(2.5), MLNTimeIntervalFromDuration(durationNanoSeconds));
|
XCTAssertEqual(NSTimeInterval(2.5), MLNTimeIntervalFromDuration(durationNanoSeconds));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -12,18 +12,30 @@
|
|||||||
- (void)testInvalidation {
|
- (void)testInvalidation {
|
||||||
MLNOfflinePack *invalidPack = [[MLNOfflinePack alloc] init];
|
MLNOfflinePack *invalidPack = [[MLNOfflinePack alloc] init];
|
||||||
|
|
||||||
XCTAssertEqual(invalidPack.state, MLNOfflinePackStateInvalid, @"Offline pack should be invalid when initialized independently of MLNOfflineStorage.");
|
XCTAssertEqual(
|
||||||
|
invalidPack.state, MLNOfflinePackStateInvalid,
|
||||||
|
@"Offline pack should be invalid when initialized independently of MLNOfflineStorage.");
|
||||||
|
|
||||||
XCTAssertThrowsSpecificNamed(invalidPack.region, NSException, MLNInvalidOfflinePackException, @"Invalid offline pack should raise an exception when accessing its region.");
|
XCTAssertThrowsSpecificNamed(
|
||||||
XCTAssertThrowsSpecificNamed(invalidPack.context, NSException, MLNInvalidOfflinePackException, @"Invalid offline pack should raise an exception when accessing its context.");
|
invalidPack.region, NSException, MLNInvalidOfflinePackException,
|
||||||
XCTAssertThrowsSpecificNamed([invalidPack resume], NSException, MLNInvalidOfflinePackException, @"Invalid offline pack should raise an exception when being resumed.");
|
@"Invalid offline pack should raise an exception when accessing its region.");
|
||||||
XCTAssertThrowsSpecificNamed([invalidPack suspend], NSException, MLNInvalidOfflinePackException, @"Invalid offline pack should raise an exception when being suspended.");
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
invalidPack.context, NSException, MLNInvalidOfflinePackException,
|
||||||
|
@"Invalid offline pack should raise an exception when accessing its context.");
|
||||||
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
[invalidPack resume], NSException, MLNInvalidOfflinePackException,
|
||||||
|
@"Invalid offline pack should raise an exception when being resumed.");
|
||||||
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
[invalidPack suspend], NSException, MLNInvalidOfflinePackException,
|
||||||
|
@"Invalid offline pack should raise an exception when being suspended.");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testInvalidatingAnInvalidPack {
|
- (void)testInvalidatingAnInvalidPack {
|
||||||
MLNOfflinePack *invalidPack = [[MLNOfflinePack alloc] init];
|
MLNOfflinePack *invalidPack = [[MLNOfflinePack alloc] init];
|
||||||
|
|
||||||
XCTAssertThrowsSpecificNamed([invalidPack invalidate], NSException, NSInternalInconsistencyException, @"Invalid offline pack should raise an exception when being invalidated.");
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
[invalidPack invalidate], NSException, NSInternalInconsistencyException,
|
||||||
|
@"Invalid offline pack should raise an exception when being invalidated.");
|
||||||
|
|
||||||
// Now try again, without asserts
|
// Now try again, without asserts
|
||||||
NSAssertionHandler *oldHandler = [NSAssertionHandler currentHandler];
|
NSAssertionHandler *oldHandler = [NSAssertionHandler currentHandler];
|
||||||
@@ -45,14 +57,21 @@
|
|||||||
.countOfTileBytesCompleted = 6,
|
.countOfTileBytesCompleted = 6,
|
||||||
.maximumResourcesExpected = UINT64_MAX,
|
.maximumResourcesExpected = UINT64_MAX,
|
||||||
};
|
};
|
||||||
MLNOfflinePackProgress roundTrippedProgress = [NSValue valueWithMLNOfflinePackProgress:progress].MLNOfflinePackProgressValue;
|
MLNOfflinePackProgress roundTrippedProgress =
|
||||||
|
[NSValue valueWithMLNOfflinePackProgress:progress].MLNOfflinePackProgressValue;
|
||||||
|
|
||||||
XCTAssertEqual(progress.countOfResourcesCompleted, roundTrippedProgress.countOfResourcesCompleted, @"Completed resources should round-trip.");
|
XCTAssertEqual(progress.countOfResourcesCompleted, roundTrippedProgress.countOfResourcesCompleted,
|
||||||
XCTAssertEqual(progress.countOfResourcesExpected, roundTrippedProgress.countOfResourcesExpected, @"Expected resources should round-trip.");
|
@"Completed resources should round-trip.");
|
||||||
XCTAssertEqual(progress.countOfBytesCompleted, roundTrippedProgress.countOfBytesCompleted, @"Completed bytes should round-trip.");
|
XCTAssertEqual(progress.countOfResourcesExpected, roundTrippedProgress.countOfResourcesExpected,
|
||||||
XCTAssertEqual(progress.countOfTilesCompleted, roundTrippedProgress.countOfTilesCompleted, @"Completed tiles should round-trip.");
|
@"Expected resources should round-trip.");
|
||||||
XCTAssertEqual(progress.countOfTileBytesCompleted, roundTrippedProgress.countOfTileBytesCompleted, @"Completed tile bytes should round-trip.");
|
XCTAssertEqual(progress.countOfBytesCompleted, roundTrippedProgress.countOfBytesCompleted,
|
||||||
XCTAssertEqual(progress.maximumResourcesExpected, roundTrippedProgress.maximumResourcesExpected, @"Maximum expected resources should round-trip.");
|
@"Completed bytes should round-trip.");
|
||||||
|
XCTAssertEqual(progress.countOfTilesCompleted, roundTrippedProgress.countOfTilesCompleted,
|
||||||
|
@"Completed tiles should round-trip.");
|
||||||
|
XCTAssertEqual(progress.countOfTileBytesCompleted, roundTrippedProgress.countOfTileBytesCompleted,
|
||||||
|
@"Completed tile bytes should round-trip.");
|
||||||
|
XCTAssertEqual(progress.maximumResourcesExpected, roundTrippedProgress.maximumResourcesExpected,
|
||||||
|
@"Maximum expected resources should round-trip.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -2,23 +2,23 @@
|
|||||||
#import <XCTest/XCTest.h>
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
#import "MLNOfflineStorage_Private.h"
|
#import "MLNOfflineStorage_Private.h"
|
||||||
|
#import "MLNTestAssertionHandler.h"
|
||||||
#import "NSBundle+MLNAdditions.h"
|
#import "NSBundle+MLNAdditions.h"
|
||||||
#import "NSDate+MLNAdditions.h"
|
#import "NSDate+MLNAdditions.h"
|
||||||
#import "MLNTestAssertionHandler.h"
|
|
||||||
|
|
||||||
#include <mbgl/storage/resource.hpp>
|
#include <mbgl/storage/resource.hpp>
|
||||||
#include <mbgl/util/run_loop.hpp>
|
#include <mbgl/util/run_loop.hpp>
|
||||||
|
|
||||||
#pragma clang diagnostic ignored "-Wshadow"
|
#pragma clang diagnostic ignored "-Wshadow"
|
||||||
|
|
||||||
|
|
||||||
@interface MLNOfflineStorageTests : XCTestCase <MLNOfflineStorageDelegate>
|
@interface MLNOfflineStorageTests : XCTestCase <MLNOfflineStorageDelegate>
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MLNOfflineStorageTests
|
@implementation MLNOfflineStorageTests
|
||||||
|
|
||||||
+ (void)tearDown {
|
+ (void)tearDown {
|
||||||
NSURL *cacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
|
NSURL *cacheDirectoryURL =
|
||||||
|
[[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
|
||||||
inDomain:NSUserDomainMask
|
inDomain:NSUserDomainMask
|
||||||
appropriateForURL:nil
|
appropriateForURL:nil
|
||||||
create:NO
|
create:NO
|
||||||
@@ -26,13 +26,15 @@
|
|||||||
NSString *bundleIdentifier = [NSBundle mgl_applicationBundleIdentifier];
|
NSString *bundleIdentifier = [NSBundle mgl_applicationBundleIdentifier];
|
||||||
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
|
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
|
||||||
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:@".mapbox"];
|
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:@".mapbox"];
|
||||||
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheDirectoryURL.path], @"Directory containing database should exist.");
|
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheDirectoryURL.path],
|
||||||
|
@"Directory containing database should exist.");
|
||||||
|
|
||||||
NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:@"cache.db"];
|
NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:@"cache.db"];
|
||||||
XCTAssertEqualObjects(cacheURL, MLNOfflineStorage.sharedOfflineStorage.databaseURL);
|
XCTAssertEqualObjects(cacheURL, MLNOfflineStorage.sharedOfflineStorage.databaseURL);
|
||||||
|
|
||||||
[[NSFileManager defaultManager] removeItemAtURL:cacheURL error:nil];
|
[[NSFileManager defaultManager] removeItemAtURL:cacheURL error:nil];
|
||||||
XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:cacheURL.path], @"Database should not exist.");
|
XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:cacheURL.path],
|
||||||
|
@"Database should not exist.");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setUp {
|
- (void)setUp {
|
||||||
@@ -41,8 +43,13 @@
|
|||||||
|
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
XCTestExpectation *expectation = [self keyValueObservingExpectationForObject:[MLNOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
|
XCTestExpectation *expectation = [self
|
||||||
const auto changeKind = static_cast<NSKeyValueChange>([change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
keyValueObservingExpectationForObject:[MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
keyPath:@"packs"
|
||||||
|
handler:^BOOL(id _Nonnull observedObject,
|
||||||
|
NSDictionary *_Nonnull change) {
|
||||||
|
const auto changeKind = static_cast<NSKeyValueChange>(
|
||||||
|
[change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
||||||
return changeKind == NSKeyValueChangeSetting;
|
return changeKind == NSKeyValueChangeSetting;
|
||||||
}];
|
}];
|
||||||
if ([MLNOfflineStorage sharedOfflineStorage].packs) {
|
if ([MLNOfflineStorage sharedOfflineStorage].packs) {
|
||||||
@@ -52,14 +59,16 @@
|
|||||||
[self waitForExpectationsWithTimeout:10 handler:nil];
|
[self waitForExpectationsWithTimeout:10 handler:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTAssertNotNil([MLNOfflineStorage sharedOfflineStorage].packs, @"Shared offline storage object should have a non-nil collection of packs by this point.");
|
XCTAssertNotNil(
|
||||||
|
[MLNOfflineStorage sharedOfflineStorage].packs,
|
||||||
|
@"Shared offline storage object should have a non-nil collection of packs by this point.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSURL *)offlineStorage:(MLNOfflineStorage *)storage
|
- (NSURL *)offlineStorage:(MLNOfflineStorage *)storage
|
||||||
URLForResourceOfKind:(MLNResourceKind)kind
|
URLForResourceOfKind:(MLNResourceKind)kind
|
||||||
withURL:(NSURL *)url {
|
withURL:(NSURL *)url {
|
||||||
if ([url.scheme isEqual: @"test"] && [url.host isEqual: @"api"]) {
|
if ([url.scheme isEqual:@"test"] && [url.host isEqual:@"api"]) {
|
||||||
return [NSURL URLWithString:@"https://api.mapbox.com"];
|
return [NSURL URLWithString:@"https://api.mapbox.com"];
|
||||||
} else {
|
} else {
|
||||||
return url;
|
return url;
|
||||||
@@ -67,7 +76,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testSharedObject {
|
- (void)testSharedObject {
|
||||||
XCTAssertEqual([MLNOfflineStorage sharedOfflineStorage], [MLNOfflineStorage sharedOfflineStorage], @"There should only be one shared offline storage object.");
|
XCTAssertEqual([MLNOfflineStorage sharedOfflineStorage], [MLNOfflineStorage sharedOfflineStorage],
|
||||||
|
@"There should only be one shared offline storage object.");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testAddPackForBounds {
|
- (void)testAddPackForBounds {
|
||||||
@@ -76,84 +86,135 @@
|
|||||||
NSURL *styleURL = [[MLNStyle predefinedStyle:@"Bright"] url];
|
NSURL *styleURL = [[MLNStyle predefinedStyle:@"Bright"] url];
|
||||||
/// Somewhere near Grape Grove, Ohio, United States.
|
/// Somewhere near Grape Grove, Ohio, United States.
|
||||||
MLNCoordinateBounds bounds = {
|
MLNCoordinateBounds bounds = {
|
||||||
{ .latitude = 39.70358155855172, .longitude = -83.69506472545841 },
|
{.latitude = 39.70358155855172, .longitude = -83.69506472545841},
|
||||||
{ .latitude = 39.703818870225376, .longitude = -83.69420641857361 },
|
{.latitude = 39.703818870225376, .longitude = -83.69420641857361},
|
||||||
};
|
};
|
||||||
double zoomLevel = 20;
|
double zoomLevel = 20;
|
||||||
MLNTilePyramidOfflineRegion *region = [[MLNTilePyramidOfflineRegion alloc] initWithStyleURL:styleURL bounds:bounds fromZoomLevel:zoomLevel toZoomLevel:zoomLevel];
|
MLNTilePyramidOfflineRegion *region =
|
||||||
|
[[MLNTilePyramidOfflineRegion alloc] initWithStyleURL:styleURL
|
||||||
|
bounds:bounds
|
||||||
|
fromZoomLevel:zoomLevel
|
||||||
|
toZoomLevel:zoomLevel];
|
||||||
|
|
||||||
NSString *nameKey = @"Name";
|
NSString *nameKey = @"Name";
|
||||||
NSString *name = @"🍇 Grape Grove";
|
NSString *name = @"🍇 Grape Grove";
|
||||||
|
|
||||||
NSData *context = [NSKeyedArchiver archivedDataWithRootObject:@{
|
NSData *context = [NSKeyedArchiver archivedDataWithRootObject:@{
|
||||||
nameKey: name,
|
nameKey : name,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
__block MLNOfflinePack *pack;
|
__block MLNOfflinePack *pack;
|
||||||
[self keyValueObservingExpectationForObject:[MLNOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
|
[self keyValueObservingExpectationForObject:[MLNOfflineStorage sharedOfflineStorage]
|
||||||
const auto changeKind = static_cast<NSKeyValueChange>([change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
keyPath:@"packs"
|
||||||
|
handler:^BOOL(id _Nonnull observedObject,
|
||||||
|
NSDictionary *_Nonnull change) {
|
||||||
|
const auto changeKind = static_cast<NSKeyValueChange>(
|
||||||
|
[change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
||||||
NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
|
NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
|
||||||
return changeKind == NSKeyValueChangeInsertion && indices.count == 1;
|
return changeKind == NSKeyValueChangeInsertion &&
|
||||||
|
indices.count == 1;
|
||||||
}];
|
}];
|
||||||
XCTestExpectation *additionCompletionHandlerExpectation = [self expectationWithDescription:@"add pack completion handler"];
|
XCTestExpectation *additionCompletionHandlerExpectation =
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] addPackForRegion:region withContext:context completionHandler:^(MLNOfflinePack * _Nullable completionHandlerPack, NSError * _Nullable error) {
|
[self expectationWithDescription:@"add pack completion handler"];
|
||||||
|
[[MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
addPackForRegion:region
|
||||||
|
withContext:context
|
||||||
|
completionHandler:^(MLNOfflinePack *_Nullable completionHandlerPack,
|
||||||
|
NSError *_Nullable error) {
|
||||||
XCTAssertNotNil(completionHandlerPack, @"Added pack should exist.");
|
XCTAssertNotNil(completionHandlerPack, @"Added pack should exist.");
|
||||||
XCTAssertEqual(completionHandlerPack.state, MLNOfflinePackStateInactive, @"New pack should initially have inactive state.");
|
XCTAssertEqual(completionHandlerPack.state, MLNOfflinePackStateInactive,
|
||||||
|
@"New pack should initially have inactive state.");
|
||||||
pack = completionHandlerPack;
|
pack = completionHandlerPack;
|
||||||
[additionCompletionHandlerExpectation fulfill];
|
[additionCompletionHandlerExpectation fulfill];
|
||||||
}];
|
}];
|
||||||
[self waitForExpectationsWithTimeout:5 handler:nil];
|
[self waitForExpectationsWithTimeout:5 handler:nil];
|
||||||
|
|
||||||
XCTAssertEqual([MLNOfflineStorage sharedOfflineStorage].packs.count, countOfPacks + 1, @"Added pack should have been added to the canonical collection of packs owned by the shared offline storage object. This assertion can fail if this test is run before -testAAALoadPacks.");
|
XCTAssertEqual([MLNOfflineStorage sharedOfflineStorage].packs.count, countOfPacks + 1,
|
||||||
|
@"Added pack should have been added to the canonical collection of packs owned by "
|
||||||
|
@"the shared offline storage object. This assertion can fail if this test is run "
|
||||||
|
@"before -testAAALoadPacks.");
|
||||||
|
|
||||||
XCTAssertEqual(pack, [MLNOfflineStorage sharedOfflineStorage].packs.lastObject, @"Pack should be appended to end of packs array.");
|
XCTAssertEqual(pack, [MLNOfflineStorage sharedOfflineStorage].packs.lastObject,
|
||||||
|
@"Pack should be appended to end of packs array.");
|
||||||
|
|
||||||
XCTAssertEqualObjects(pack.region, region, @"Added pack’s region has changed.");
|
XCTAssertEqualObjects(pack.region, region, @"Added pack’s region has changed.");
|
||||||
|
|
||||||
NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context];
|
NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context];
|
||||||
XCTAssert([userInfo isKindOfClass:[NSDictionary class]], @"Context of offline pack isn’t a dictionary.");
|
XCTAssert([userInfo isKindOfClass:[NSDictionary class]],
|
||||||
XCTAssert([userInfo[nameKey] isKindOfClass:[NSString class]], @"Name of offline pack isn’t a string.");
|
@"Context of offline pack isn’t a dictionary.");
|
||||||
|
XCTAssert([userInfo[nameKey] isKindOfClass:[NSString class]],
|
||||||
|
@"Name of offline pack isn’t a string.");
|
||||||
XCTAssertEqualObjects(userInfo[nameKey], name, @"Name of offline pack has changed.");
|
XCTAssertEqualObjects(userInfo[nameKey], name, @"Name of offline pack has changed.");
|
||||||
|
|
||||||
XCTAssertEqual(pack.state, MLNOfflinePackStateInactive, @"New pack should initially have inactive state.");
|
XCTAssertEqual(pack.state, MLNOfflinePackStateInactive,
|
||||||
|
@"New pack should initially have inactive state.");
|
||||||
|
|
||||||
[self keyValueObservingExpectationForObject:pack keyPath:@"state" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
|
[self keyValueObservingExpectationForObject:pack
|
||||||
const auto changeKind = static_cast<NSKeyValueChange>([change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
keyPath:@"state"
|
||||||
const auto state = static_cast<MLNOfflinePackState>([change[NSKeyValueChangeNewKey] longValue]);
|
handler:^BOOL(id _Nonnull observedObject,
|
||||||
return changeKind == NSKeyValueChangeSetting && state == MLNOfflinePackStateInactive;
|
NSDictionary *_Nonnull change) {
|
||||||
|
const auto changeKind = static_cast<NSKeyValueChange>(
|
||||||
|
[change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
||||||
|
const auto state = static_cast<MLNOfflinePackState>(
|
||||||
|
[change[NSKeyValueChangeNewKey] longValue]);
|
||||||
|
return changeKind == NSKeyValueChangeSetting &&
|
||||||
|
state == MLNOfflinePackStateInactive;
|
||||||
}];
|
}];
|
||||||
[self expectationForNotification:MLNOfflinePackProgressChangedNotification object:pack handler:^BOOL(NSNotification * _Nonnull notification) {
|
[self
|
||||||
|
expectationForNotification:MLNOfflinePackProgressChangedNotification
|
||||||
|
object:pack
|
||||||
|
handler:^BOOL(NSNotification *_Nonnull notification) {
|
||||||
MLNOfflinePack *notificationPack = notification.object;
|
MLNOfflinePack *notificationPack = notification.object;
|
||||||
XCTAssert([notificationPack isKindOfClass:[MLNOfflinePack class]], @"Object of notification should be an MLNOfflinePack.");
|
XCTAssert([notificationPack isKindOfClass:[MLNOfflinePack class]],
|
||||||
|
@"Object of notification should be an MLNOfflinePack.");
|
||||||
|
|
||||||
NSDictionary *userInfo = notification.userInfo;
|
NSDictionary *userInfo = notification.userInfo;
|
||||||
XCTAssertNotNil(userInfo, @"Progress change notification should have a userInfo dictionary.");
|
XCTAssertNotNil(
|
||||||
|
userInfo,
|
||||||
|
@"Progress change notification should have a userInfo dictionary.");
|
||||||
|
|
||||||
NSNumber *stateNumber = userInfo[MLNOfflinePackUserInfoKeyState];
|
NSNumber *stateNumber = userInfo[MLNOfflinePackUserInfoKeyState];
|
||||||
XCTAssert([stateNumber isKindOfClass:[NSNumber class]], @"Progress change notification’s state should be an NSNumber.");
|
XCTAssert(
|
||||||
XCTAssertEqual(stateNumber.integerValue, pack.state, @"State in a progress change notification should match the pack’s state.");
|
[stateNumber isKindOfClass:[NSNumber class]],
|
||||||
|
@"Progress change notification’s state should be an NSNumber.");
|
||||||
|
XCTAssertEqual(stateNumber.integerValue, pack.state,
|
||||||
|
@"State in a progress change notification should match "
|
||||||
|
@"the pack’s state.");
|
||||||
|
|
||||||
NSValue *progressValue = userInfo[MLNOfflinePackUserInfoKeyProgress];
|
NSValue *progressValue = userInfo[MLNOfflinePackUserInfoKeyProgress];
|
||||||
XCTAssert([progressValue isKindOfClass:[NSValue class]], @"Progress change notification’s progress should be an NSValue.");
|
XCTAssert(
|
||||||
XCTAssertEqualObjects(progressValue, [NSValue valueWithMLNOfflinePackProgress:pack.progress], @"Progress change notification’s progress should match pack’s progress.");
|
[progressValue isKindOfClass:[NSValue class]],
|
||||||
|
@"Progress change notification’s progress should be an NSValue.");
|
||||||
|
XCTAssertEqualObjects(
|
||||||
|
progressValue,
|
||||||
|
[NSValue valueWithMLNOfflinePackProgress:pack.progress],
|
||||||
|
@"Progress change notification’s progress should match pack’s "
|
||||||
|
@"progress.");
|
||||||
|
|
||||||
return notificationPack == pack && pack.state == MLNOfflinePackStateInactive;
|
return notificationPack == pack &&
|
||||||
|
pack.state == MLNOfflinePackStateInactive;
|
||||||
}];
|
}];
|
||||||
[pack requestProgress];
|
[pack requestProgress];
|
||||||
[self waitForExpectationsWithTimeout:5 handler:nil];
|
[self waitForExpectationsWithTimeout:5 handler:nil];
|
||||||
|
|
||||||
XCTAssertEqualObjects(pack.context, context, @"Offline pack context should match the context specified by the application.");
|
XCTAssertEqualObjects(
|
||||||
|
pack.context, context,
|
||||||
|
@"Offline pack context should match the context specified by the application.");
|
||||||
NSString *newName = @"🍑 Peach Grove";
|
NSString *newName = @"🍑 Peach Grove";
|
||||||
NSData *newContext = [NSKeyedArchiver archivedDataWithRootObject:@{
|
NSData *newContext = [NSKeyedArchiver archivedDataWithRootObject:@{
|
||||||
nameKey: newName,
|
nameKey : newName,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
XCTestExpectation *contextCompletionHandlerExpectation = [self expectationWithDescription:@"set pack completion context handler"];
|
XCTestExpectation *contextCompletionHandlerExpectation =
|
||||||
|
[self expectationWithDescription:@"set pack completion context handler"];
|
||||||
__weak MLNOfflinePack *weakPack = pack;
|
__weak MLNOfflinePack *weakPack = pack;
|
||||||
[pack setContext:newContext completionHandler:^(NSError * _Nullable error) {
|
[pack setContext:newContext
|
||||||
|
completionHandler:^(NSError *_Nullable error) {
|
||||||
XCTAssertNil(error);
|
XCTAssertNil(error);
|
||||||
XCTAssertNotNil(weakPack);
|
XCTAssertNotNil(weakPack);
|
||||||
XCTAssertEqualObjects(weakPack.context, newContext, @"Offline pack context should match the updated context specified by the application.");
|
XCTAssertEqualObjects(
|
||||||
|
weakPack.context, newContext,
|
||||||
|
@"Offline pack context should match the updated context specified by the application.");
|
||||||
[contextCompletionHandlerExpectation fulfill];
|
[contextCompletionHandlerExpectation fulfill];
|
||||||
}];
|
}];
|
||||||
[self waitForExpectationsWithTimeout:5 handler:nil];
|
[self waitForExpectationsWithTimeout:5 handler:nil];
|
||||||
@@ -164,67 +225,116 @@
|
|||||||
|
|
||||||
NSURL *styleURL = [[MLNStyle predefinedStyle:@"Bright"] url];
|
NSURL *styleURL = [[MLNStyle predefinedStyle:@"Bright"] url];
|
||||||
double zoomLevel = 20;
|
double zoomLevel = 20;
|
||||||
NSString *geojson = @"{ \"type\": \"Polygon\", \"coordinates\": [ [ [ 5.1299285888671875, 52.10365839097971 ], [ 5.103063583374023, 52.110037078604236 ], [ 5.080232620239258, 52.09548601177304 ], [ 5.106925964355469, 52.07987524347506 ], [ 5.1299285888671875, 52.10365839097971 ] ] ]}";
|
NSString *geojson =
|
||||||
|
@"{ \"type\": \"Polygon\", \"coordinates\": [ [ [ 5.1299285888671875, 52.10365839097971 ], [ "
|
||||||
|
@"5.103063583374023, 52.110037078604236 ], [ 5.080232620239258, 52.09548601177304 ], [ "
|
||||||
|
@"5.106925964355469, 52.07987524347506 ], [ 5.1299285888671875, 52.10365839097971 ] ] ]}";
|
||||||
NSError *error;
|
NSError *error;
|
||||||
MLNShape *shape = [MLNShape shapeWithData: [geojson dataUsingEncoding:NSUTF8StringEncoding] encoding: NSUTF8StringEncoding error:&error];
|
MLNShape *shape = [MLNShape shapeWithData:[geojson dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
|
encoding:NSUTF8StringEncoding
|
||||||
|
error:&error];
|
||||||
XCTAssertNil(error);
|
XCTAssertNil(error);
|
||||||
MLNShapeOfflineRegion *region = [[MLNShapeOfflineRegion alloc] initWithStyleURL:styleURL shape:shape fromZoomLevel:zoomLevel toZoomLevel:zoomLevel];
|
MLNShapeOfflineRegion *region = [[MLNShapeOfflineRegion alloc] initWithStyleURL:styleURL
|
||||||
|
shape:shape
|
||||||
|
fromZoomLevel:zoomLevel
|
||||||
|
toZoomLevel:zoomLevel];
|
||||||
region.includesIdeographicGlyphs = NO;
|
region.includesIdeographicGlyphs = NO;
|
||||||
|
|
||||||
NSString *nameKey = @"Name";
|
NSString *nameKey = @"Name";
|
||||||
NSString *name = @"Utrecht centrum";
|
NSString *name = @"Utrecht centrum";
|
||||||
|
|
||||||
NSData *context = [NSKeyedArchiver archivedDataWithRootObject:@{nameKey: name}];
|
NSData *context = [NSKeyedArchiver archivedDataWithRootObject:@{nameKey : name}];
|
||||||
|
|
||||||
__block MLNOfflinePack *pack;
|
__block MLNOfflinePack *pack;
|
||||||
[self keyValueObservingExpectationForObject:[MLNOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
|
[self keyValueObservingExpectationForObject:[MLNOfflineStorage sharedOfflineStorage]
|
||||||
const auto changeKind = static_cast<NSKeyValueChange>([change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
keyPath:@"packs"
|
||||||
|
handler:^BOOL(id _Nonnull observedObject,
|
||||||
|
NSDictionary *_Nonnull change) {
|
||||||
|
const auto changeKind = static_cast<NSKeyValueChange>(
|
||||||
|
[change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
||||||
NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
|
NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
|
||||||
return changeKind == NSKeyValueChangeInsertion && indices.count == 1;
|
return changeKind == NSKeyValueChangeInsertion &&
|
||||||
|
indices.count == 1;
|
||||||
}];
|
}];
|
||||||
XCTestExpectation *additionCompletionHandlerExpectation = [self expectationWithDescription:@"add pack completion handler"];
|
XCTestExpectation *additionCompletionHandlerExpectation =
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] addPackForRegion:region withContext:context completionHandler:^(MLNOfflinePack * _Nullable completionHandlerPack, NSError * _Nullable error) {
|
[self expectationWithDescription:@"add pack completion handler"];
|
||||||
|
[[MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
addPackForRegion:region
|
||||||
|
withContext:context
|
||||||
|
completionHandler:^(MLNOfflinePack *_Nullable completionHandlerPack,
|
||||||
|
NSError *_Nullable error) {
|
||||||
XCTAssertNotNil(completionHandlerPack, @"Added pack should exist.");
|
XCTAssertNotNil(completionHandlerPack, @"Added pack should exist.");
|
||||||
XCTAssertEqual(completionHandlerPack.state, MLNOfflinePackStateInactive, @"New pack should initially have inactive state.");
|
XCTAssertEqual(completionHandlerPack.state, MLNOfflinePackStateInactive,
|
||||||
|
@"New pack should initially have inactive state.");
|
||||||
pack = completionHandlerPack;
|
pack = completionHandlerPack;
|
||||||
[additionCompletionHandlerExpectation fulfill];
|
[additionCompletionHandlerExpectation fulfill];
|
||||||
}];
|
}];
|
||||||
[self waitForExpectationsWithTimeout:5 handler:nil];
|
[self waitForExpectationsWithTimeout:5 handler:nil];
|
||||||
|
|
||||||
XCTAssertEqual([MLNOfflineStorage sharedOfflineStorage].packs.count, countOfPacks + 1, @"Added pack should have been added to the canonical collection of packs owned by the shared offline storage object. This assertion can fail if this test is run before -testAAALoadPacks.");
|
XCTAssertEqual([MLNOfflineStorage sharedOfflineStorage].packs.count, countOfPacks + 1,
|
||||||
|
@"Added pack should have been added to the canonical collection of packs owned by "
|
||||||
|
@"the shared offline storage object. This assertion can fail if this test is run "
|
||||||
|
@"before -testAAALoadPacks.");
|
||||||
|
|
||||||
XCTAssertEqual(pack, [MLNOfflineStorage sharedOfflineStorage].packs.lastObject, @"Pack should be appended to end of packs array.");
|
XCTAssertEqual(pack, [MLNOfflineStorage sharedOfflineStorage].packs.lastObject,
|
||||||
|
@"Pack should be appended to end of packs array.");
|
||||||
|
|
||||||
XCTAssertEqualObjects(pack.region, region, @"Added pack’s region has changed.");
|
XCTAssertEqualObjects(pack.region, region, @"Added pack’s region has changed.");
|
||||||
|
|
||||||
NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context];
|
NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context];
|
||||||
XCTAssert([userInfo isKindOfClass:[NSDictionary class]], @"Context of offline pack isn’t a dictionary.");
|
XCTAssert([userInfo isKindOfClass:[NSDictionary class]],
|
||||||
XCTAssert([userInfo[nameKey] isKindOfClass:[NSString class]], @"Name of offline pack isn’t a string.");
|
@"Context of offline pack isn’t a dictionary.");
|
||||||
|
XCTAssert([userInfo[nameKey] isKindOfClass:[NSString class]],
|
||||||
|
@"Name of offline pack isn’t a string.");
|
||||||
XCTAssertEqualObjects(userInfo[nameKey], name, @"Name of offline pack has changed.");
|
XCTAssertEqualObjects(userInfo[nameKey], name, @"Name of offline pack has changed.");
|
||||||
|
|
||||||
XCTAssertEqual(pack.state, MLNOfflinePackStateInactive, @"New pack should initially have inactive state.");
|
XCTAssertEqual(pack.state, MLNOfflinePackStateInactive,
|
||||||
|
@"New pack should initially have inactive state.");
|
||||||
|
|
||||||
[self keyValueObservingExpectationForObject:pack keyPath:@"state" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
|
[self keyValueObservingExpectationForObject:pack
|
||||||
const auto changeKind = static_cast<NSKeyValueChange>([change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
keyPath:@"state"
|
||||||
const auto state = static_cast<MLNOfflinePackState>([change[NSKeyValueChangeNewKey] longValue]);
|
handler:^BOOL(id _Nonnull observedObject,
|
||||||
return changeKind == NSKeyValueChangeSetting && state == MLNOfflinePackStateInactive;
|
NSDictionary *_Nonnull change) {
|
||||||
|
const auto changeKind = static_cast<NSKeyValueChange>(
|
||||||
|
[change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
||||||
|
const auto state = static_cast<MLNOfflinePackState>(
|
||||||
|
[change[NSKeyValueChangeNewKey] longValue]);
|
||||||
|
return changeKind == NSKeyValueChangeSetting &&
|
||||||
|
state == MLNOfflinePackStateInactive;
|
||||||
}];
|
}];
|
||||||
[self expectationForNotification:MLNOfflinePackProgressChangedNotification object:pack handler:^BOOL(NSNotification * _Nonnull notification) {
|
[self
|
||||||
|
expectationForNotification:MLNOfflinePackProgressChangedNotification
|
||||||
|
object:pack
|
||||||
|
handler:^BOOL(NSNotification *_Nonnull notification) {
|
||||||
MLNOfflinePack *notificationPack = notification.object;
|
MLNOfflinePack *notificationPack = notification.object;
|
||||||
XCTAssert([notificationPack isKindOfClass:[MLNOfflinePack class]], @"Object of notification should be an MLNOfflinePack.");
|
XCTAssert([notificationPack isKindOfClass:[MLNOfflinePack class]],
|
||||||
|
@"Object of notification should be an MLNOfflinePack.");
|
||||||
|
|
||||||
NSDictionary *userInfo = notification.userInfo;
|
NSDictionary *userInfo = notification.userInfo;
|
||||||
XCTAssertNotNil(userInfo, @"Progress change notification should have a userInfo dictionary.");
|
XCTAssertNotNil(
|
||||||
|
userInfo,
|
||||||
|
@"Progress change notification should have a userInfo dictionary.");
|
||||||
|
|
||||||
NSNumber *stateNumber = userInfo[MLNOfflinePackUserInfoKeyState];
|
NSNumber *stateNumber = userInfo[MLNOfflinePackUserInfoKeyState];
|
||||||
XCTAssert([stateNumber isKindOfClass:[NSNumber class]], @"Progress change notification’s state should be an NSNumber.");
|
XCTAssert(
|
||||||
XCTAssertEqual(stateNumber.integerValue, pack.state, @"State in a progress change notification should match the pack’s state.");
|
[stateNumber isKindOfClass:[NSNumber class]],
|
||||||
|
@"Progress change notification’s state should be an NSNumber.");
|
||||||
|
XCTAssertEqual(stateNumber.integerValue, pack.state,
|
||||||
|
@"State in a progress change notification should match "
|
||||||
|
@"the pack’s state.");
|
||||||
|
|
||||||
NSValue *progressValue = userInfo[MLNOfflinePackUserInfoKeyProgress];
|
NSValue *progressValue = userInfo[MLNOfflinePackUserInfoKeyProgress];
|
||||||
XCTAssert([progressValue isKindOfClass:[NSValue class]], @"Progress change notification’s progress should be an NSValue.");
|
XCTAssert(
|
||||||
XCTAssertEqualObjects(progressValue, [NSValue valueWithMLNOfflinePackProgress:pack.progress], @"Progress change notification’s progress should match pack’s progress.");
|
[progressValue isKindOfClass:[NSValue class]],
|
||||||
|
@"Progress change notification’s progress should be an NSValue.");
|
||||||
|
XCTAssertEqualObjects(
|
||||||
|
progressValue,
|
||||||
|
[NSValue valueWithMLNOfflinePackProgress:pack.progress],
|
||||||
|
@"Progress change notification’s progress should match pack’s "
|
||||||
|
@"progress.");
|
||||||
|
|
||||||
return notificationPack == pack && pack.state == MLNOfflinePackStateInactive;
|
return notificationPack == pack &&
|
||||||
|
pack.state == MLNOfflinePackStateInactive;
|
||||||
}];
|
}];
|
||||||
[pack requestProgress];
|
[pack requestProgress];
|
||||||
[self waitForExpectationsWithTimeout:5 handler:nil];
|
[self waitForExpectationsWithTimeout:5 handler:nil];
|
||||||
@@ -232,22 +342,32 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testInvalidatePack {
|
- (void)testInvalidatePack {
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Expect offline pack to be invalidated without an error."];
|
XCTestExpectation *expectation =
|
||||||
|
[self expectationWithDescription:@"Expect offline pack to be invalidated without an error."];
|
||||||
MLNCoordinateBounds bounds = {
|
MLNCoordinateBounds bounds = {
|
||||||
{ .latitude = 48.8660, .longitude = 2.3306 },
|
{.latitude = 48.8660, .longitude = 2.3306},
|
||||||
{ .latitude = 48.8603, .longitude = 2.3213 },
|
{.latitude = 48.8603, .longitude = 2.3213},
|
||||||
};
|
};
|
||||||
|
|
||||||
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
|
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner"
|
||||||
MLNTilePyramidOfflineRegion *region = [[MLNTilePyramidOfflineRegion alloc] initWithStyleURL:styleURL bounds:bounds fromZoomLevel:10 toZoomLevel:11];
|
withExtension:@"json"];
|
||||||
|
MLNTilePyramidOfflineRegion *region =
|
||||||
|
[[MLNTilePyramidOfflineRegion alloc] initWithStyleURL:styleURL
|
||||||
|
bounds:bounds
|
||||||
|
fromZoomLevel:10
|
||||||
|
toZoomLevel:11];
|
||||||
|
|
||||||
NSString *nameKey = @"Name";
|
NSString *nameKey = @"Name";
|
||||||
NSString *name = @"Paris square";
|
NSString *name = @"Paris square";
|
||||||
|
|
||||||
NSData *context = [NSKeyedArchiver archivedDataWithRootObject:@{nameKey: name}];
|
NSData *context = [NSKeyedArchiver archivedDataWithRootObject:@{nameKey : name}];
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] addPackForRegion:region withContext:context completionHandler:^(MLNOfflinePack * _Nullable pack, NSError * _Nullable error) {
|
[[MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
addPackForRegion:region
|
||||||
|
withContext:context
|
||||||
|
completionHandler:^(MLNOfflinePack *_Nullable pack, NSError *_Nullable error) {
|
||||||
XCTAssertNotNil(pack);
|
XCTAssertNotNil(pack);
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] invalidatePack:pack withCompletionHandler:^(NSError * _Nullable) {
|
[[MLNOfflineStorage sharedOfflineStorage] invalidatePack:pack
|
||||||
|
withCompletionHandler:^(NSError *_Nullable) {
|
||||||
XCTAssertNotNil(pack);
|
XCTAssertNotNil(pack);
|
||||||
XCTAssertNil(error);
|
XCTAssertNil(error);
|
||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
@@ -262,25 +382,39 @@
|
|||||||
MLNOfflinePack *pack = [MLNOfflineStorage sharedOfflineStorage].packs.lastObject;
|
MLNOfflinePack *pack = [MLNOfflineStorage sharedOfflineStorage].packs.lastObject;
|
||||||
XCTAssertNotNil(pack, @"Added pack should still exist.");
|
XCTAssertNotNil(pack, @"Added pack should still exist.");
|
||||||
|
|
||||||
[self keyValueObservingExpectationForObject:[MLNOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
|
[self keyValueObservingExpectationForObject:[MLNOfflineStorage sharedOfflineStorage]
|
||||||
const auto changeKind = static_cast<NSKeyValueChange>([change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
keyPath:@"packs"
|
||||||
|
handler:^BOOL(id _Nonnull observedObject,
|
||||||
|
NSDictionary *_Nonnull change) {
|
||||||
|
const auto changeKind = static_cast<NSKeyValueChange>(
|
||||||
|
[change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
||||||
NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
|
NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
|
||||||
return changeKind == NSKeyValueChangeRemoval && indices.count == 1;
|
return changeKind == NSKeyValueChangeRemoval &&
|
||||||
|
indices.count == 1;
|
||||||
}];
|
}];
|
||||||
XCTestExpectation *completionHandlerExpectation = [self expectationWithDescription:@"remove pack completion handler"];
|
XCTestExpectation *completionHandlerExpectation =
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:^(NSError * _Nullable error) {
|
[self expectationWithDescription:@"remove pack completion handler"];
|
||||||
XCTAssertEqual(pack.state, MLNOfflinePackStateInvalid, @"Removed pack should be invalid in the completion handler.");
|
[[MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
removePack:pack
|
||||||
|
withCompletionHandler:^(NSError *_Nullable error) {
|
||||||
|
XCTAssertEqual(pack.state, MLNOfflinePackStateInvalid,
|
||||||
|
@"Removed pack should be invalid in the completion handler.");
|
||||||
[completionHandlerExpectation fulfill];
|
[completionHandlerExpectation fulfill];
|
||||||
}];
|
}];
|
||||||
[self waitForExpectationsWithTimeout:5 handler:nil];
|
[self waitForExpectationsWithTimeout:5 handler:nil];
|
||||||
|
|
||||||
XCTAssertEqual(pack.state, MLNOfflinePackStateInvalid, @"Removed pack should have been invalidated synchronously.");
|
XCTAssertEqual(pack.state, MLNOfflinePackStateInvalid,
|
||||||
|
@"Removed pack should have been invalidated synchronously.");
|
||||||
|
|
||||||
XCTAssertEqual([MLNOfflineStorage sharedOfflineStorage].packs.count, countOfPacks - 1, @"Removed pack should have been removed from the canonical collection of packs owned by the shared offline storage object. This assertion can fail if this test is run before -testAAALoadPacks or -testAddPack.");
|
XCTAssertEqual([MLNOfflineStorage sharedOfflineStorage].packs.count, countOfPacks - 1,
|
||||||
|
@"Removed pack should have been removed from the canonical collection of packs "
|
||||||
|
@"owned by the shared offline storage object. This assertion can fail if this "
|
||||||
|
@"test is run before -testAAALoadPacks or -testAddPack.");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testBackupExclusion {
|
- (void)testBackupExclusion {
|
||||||
NSURL *cacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
|
NSURL *cacheDirectoryURL =
|
||||||
|
[[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
|
||||||
inDomain:NSUserDomainMask
|
inDomain:NSUserDomainMask
|
||||||
appropriateForURL:nil
|
appropriateForURL:nil
|
||||||
create:NO
|
create:NO
|
||||||
@@ -289,22 +423,25 @@
|
|||||||
NSString *bundleIdentifier = [NSBundle mgl_applicationBundleIdentifier];
|
NSString *bundleIdentifier = [NSBundle mgl_applicationBundleIdentifier];
|
||||||
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
|
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
|
||||||
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:@".mapbox"];
|
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:@".mapbox"];
|
||||||
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheDirectoryURL.path], @"Cache subdirectory should exist.");
|
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheDirectoryURL.path],
|
||||||
|
@"Cache subdirectory should exist.");
|
||||||
|
|
||||||
NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:@"cache.db"];
|
NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:@"cache.db"];
|
||||||
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheURL.path], @"Cache database should exist.");
|
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheURL.path],
|
||||||
|
@"Cache database should exist.");
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
NSNumber *exclusionFlag = nil;
|
NSNumber *exclusionFlag = nil;
|
||||||
[cacheDirectoryURL getResourceValue:&exclusionFlag
|
[cacheDirectoryURL getResourceValue:&exclusionFlag
|
||||||
forKey:NSURLIsExcludedFromBackupKey
|
forKey:NSURLIsExcludedFromBackupKey
|
||||||
error:&error];
|
error:&error];
|
||||||
XCTAssertTrue(exclusionFlag && [exclusionFlag boolValue], @"Backup exclusion flag should be set for the directory containing the cache database.");
|
XCTAssertTrue(
|
||||||
|
exclusionFlag && [exclusionFlag boolValue],
|
||||||
|
@"Backup exclusion flag should be set for the directory containing the cache database.");
|
||||||
XCTAssertNil(error, @"No errors should be returned when checking backup exclusion flag.");
|
XCTAssertNil(error, @"No errors should be returned when checking backup exclusion flag.");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addPacks:(NSInteger)count {
|
- (void)addPacks:(NSInteger)count {
|
||||||
|
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"added packs"];
|
XCTestExpectation *expectation = [self expectationWithDescription:@"added packs"];
|
||||||
|
|
||||||
NSURL *styleURL = [[MLNStyle predefinedStyle:@"Bright"] url];
|
NSURL *styleURL = [[MLNStyle predefinedStyle:@"Bright"] url];
|
||||||
@@ -316,23 +453,25 @@
|
|||||||
{{37.7, -122.5}, {37.9, -122.4}} // SF
|
{{37.7, -122.5}, {37.9, -122.4}} // SF
|
||||||
};
|
};
|
||||||
|
|
||||||
int arraySize = sizeof(bounds)/sizeof(bounds[0]);
|
int arraySize = sizeof(bounds) / sizeof(bounds[0]);
|
||||||
|
|
||||||
count = MIN(count, arraySize);
|
count = MIN(count, arraySize);
|
||||||
|
|
||||||
dispatch_group_t group = dispatch_group_create();
|
dispatch_group_t group = dispatch_group_create();
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
|
|
||||||
dispatch_group_enter(group);
|
dispatch_group_enter(group);
|
||||||
MLNTilePyramidOfflineRegion *region = [[MLNTilePyramidOfflineRegion alloc] initWithStyleURL:styleURL bounds:bounds[i] fromZoomLevel:20 toZoomLevel:20];
|
MLNTilePyramidOfflineRegion *region =
|
||||||
NSData *context = [NSKeyedArchiver archivedDataWithRootObject:@{
|
[[MLNTilePyramidOfflineRegion alloc] initWithStyleURL:styleURL
|
||||||
@"index": @(i)
|
bounds:bounds[i]
|
||||||
}];
|
fromZoomLevel:20
|
||||||
|
toZoomLevel:20];
|
||||||
|
NSData *context = [NSKeyedArchiver archivedDataWithRootObject:@{@"index" : @(i)}];
|
||||||
|
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] addPackForRegion:region
|
[[MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
addPackForRegion:region
|
||||||
withContext:context
|
withContext:context
|
||||||
completionHandler:^(MLNOfflinePack * _Nullable pack, NSError * _Nullable error) {
|
completionHandler:^(MLNOfflinePack *_Nullable pack, NSError *_Nullable error) {
|
||||||
XCTAssertNotNil(pack);
|
XCTAssertNotNil(pack);
|
||||||
XCTAssertNil(error);
|
XCTAssertNil(error);
|
||||||
|
|
||||||
@@ -344,11 +483,10 @@
|
|||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
});
|
});
|
||||||
|
|
||||||
[self waitForExpectations:@[expectation] timeout:1.0];
|
[self waitForExpectations:@[ expectation ] timeout:1.0];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testRemovePackTwiceInSuccession {
|
- (void)testRemovePackTwiceInSuccession {
|
||||||
|
|
||||||
[self addPacks:1];
|
[self addPacks:1];
|
||||||
|
|
||||||
NSUInteger countOfPacks = [MLNOfflineStorage sharedOfflineStorage].packs.count;
|
NSUInteger countOfPacks = [MLNOfflineStorage sharedOfflineStorage].packs.count;
|
||||||
@@ -356,13 +494,19 @@
|
|||||||
MLNOfflinePack *pack = [MLNOfflineStorage sharedOfflineStorage].packs.lastObject;
|
MLNOfflinePack *pack = [MLNOfflineStorage sharedOfflineStorage].packs.lastObject;
|
||||||
XCTAssertNotNil(pack, @"Added pack should still exist.");
|
XCTAssertNotNil(pack, @"Added pack should still exist.");
|
||||||
|
|
||||||
[self keyValueObservingExpectationForObject:[MLNOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
|
[self keyValueObservingExpectationForObject:[MLNOfflineStorage sharedOfflineStorage]
|
||||||
const auto changeKind = static_cast<NSKeyValueChange>([change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
keyPath:@"packs"
|
||||||
|
handler:^BOOL(id _Nonnull observedObject,
|
||||||
|
NSDictionary *_Nonnull change) {
|
||||||
|
const auto changeKind = static_cast<NSKeyValueChange>(
|
||||||
|
[change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
||||||
NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
|
NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
|
||||||
return changeKind == NSKeyValueChangeRemoval && indices.count == 1;
|
return changeKind == NSKeyValueChangeRemoval &&
|
||||||
|
indices.count == 1;
|
||||||
}];
|
}];
|
||||||
|
|
||||||
XCTestExpectation *completionHandlerExpectation = [self expectationWithDescription:@"remove pack completion handler"];
|
XCTestExpectation *completionHandlerExpectation =
|
||||||
|
[self expectationWithDescription:@"remove pack completion handler"];
|
||||||
|
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:nil];
|
[[MLNOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:nil];
|
||||||
|
|
||||||
@@ -371,8 +515,11 @@
|
|||||||
|
|
||||||
[[[NSThread currentThread] threadDictionary] setValue:newHandler forKey:NSAssertionHandlerKey];
|
[[[NSThread currentThread] threadDictionary] setValue:newHandler forKey:NSAssertionHandlerKey];
|
||||||
|
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:^(NSError * _Nullable error) {
|
[[MLNOfflineStorage sharedOfflineStorage]
|
||||||
XCTAssertEqual(pack.state, MLNOfflinePackStateInvalid, @"Removed pack should be invalid in the completion handler.");
|
removePack:pack
|
||||||
|
withCompletionHandler:^(NSError *_Nullable error) {
|
||||||
|
XCTAssertEqual(pack.state, MLNOfflinePackStateInvalid,
|
||||||
|
@"Removed pack should be invalid in the completion handler.");
|
||||||
[completionHandlerExpectation fulfill];
|
[completionHandlerExpectation fulfill];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@@ -380,15 +527,18 @@
|
|||||||
|
|
||||||
[[[NSThread currentThread] threadDictionary] setValue:oldHandler forKey:NSAssertionHandlerKey];
|
[[[NSThread currentThread] threadDictionary] setValue:oldHandler forKey:NSAssertionHandlerKey];
|
||||||
|
|
||||||
XCTAssertEqual(pack.state, MLNOfflinePackStateInvalid, @"Removed pack should have been invalidated synchronously.");
|
XCTAssertEqual(pack.state, MLNOfflinePackStateInvalid,
|
||||||
|
@"Removed pack should have been invalidated synchronously.");
|
||||||
|
|
||||||
XCTAssertEqual([MLNOfflineStorage sharedOfflineStorage].packs.count, countOfPacks - 1, @"Removed pack should have been removed from the canonical collection of packs owned by the shared offline storage object. This assertion can fail if this test is run before -testAAALoadPacks or -testAddPack.");
|
XCTAssertEqual([MLNOfflineStorage sharedOfflineStorage].packs.count, countOfPacks - 1,
|
||||||
|
@"Removed pack should have been removed from the canonical collection of packs "
|
||||||
|
@"owned by the shared offline storage object. This assertion can fail if this "
|
||||||
|
@"test is run before -testAAALoadPacks or -testAddPack.");
|
||||||
|
|
||||||
NSLog(@"Test `%@` complete", NSStringFromSelector(_cmd));
|
NSLog(@"Test `%@` complete", NSStringFromSelector(_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)test15536RemovePacksWhileReloading {
|
- (void)test15536RemovePacksWhileReloading {
|
||||||
|
|
||||||
// This test triggers
|
// This test triggers
|
||||||
//
|
//
|
||||||
// throw std::runtime_error("Malformed offline region definition");
|
// throw std::runtime_error("Malformed offline region definition");
|
||||||
@@ -412,8 +562,12 @@
|
|||||||
// Simulate what happens the first time sharedOfflineStorage is accessed
|
// Simulate what happens the first time sharedOfflineStorage is accessed
|
||||||
[storage reloadPacks];
|
[storage reloadPacks];
|
||||||
|
|
||||||
NSArray *validPacks = [packs filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
|
NSArray *validPacks = [packs
|
||||||
MLNOfflinePack *pack = (MLNOfflinePack*)evaluatedObject;
|
filteredArrayUsingPredicate:[NSPredicate
|
||||||
|
predicateWithBlock:^BOOL(
|
||||||
|
id _Nullable evaluatedObject,
|
||||||
|
NSDictionary<NSString *, id> *_Nullable bindings) {
|
||||||
|
MLNOfflinePack *pack = (MLNOfflinePack *)evaluatedObject;
|
||||||
return pack.state != MLNOfflinePackStateInvalid;
|
return pack.state != MLNOfflinePackStateInvalid;
|
||||||
}]];
|
}]];
|
||||||
|
|
||||||
@@ -423,14 +577,15 @@
|
|||||||
[[[NSThread currentThread] threadDictionary] setValue:newHandler forKey:NSAssertionHandlerKey];
|
[[[NSThread currentThread] threadDictionary] setValue:newHandler forKey:NSAssertionHandlerKey];
|
||||||
|
|
||||||
for (MLNOfflinePack *pack in validPacks) {
|
for (MLNOfflinePack *pack in validPacks) {
|
||||||
[storage removePack:pack withCompletionHandler:^(NSError * _Nullable error) {
|
[storage removePack:pack
|
||||||
|
withCompletionHandler:^(NSError *_Nullable error) {
|
||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
[[[NSThread currentThread] threadDictionary] setValue:oldHandler forKey:NSAssertionHandlerKey];
|
[[[NSThread currentThread] threadDictionary] setValue:oldHandler forKey:NSAssertionHandlerKey];
|
||||||
|
|
||||||
[self waitForExpectations:@[expectation] timeout:10.0];
|
[self waitForExpectations:@[ expectation ] timeout:10.0];
|
||||||
|
|
||||||
// TODO: What should we expect here? All packs removed?
|
// TODO: What should we expect here? All packs removed?
|
||||||
|
|
||||||
@@ -439,14 +594,14 @@
|
|||||||
|
|
||||||
// Test to explore https://github.com/mapbox/mapbox-gl-native/issues/15536
|
// Test to explore https://github.com/mapbox/mapbox-gl-native/issues/15536
|
||||||
- (void)test15536RemovePacksOnBackgroundQueueWhileReloading {
|
- (void)test15536RemovePacksOnBackgroundQueueWhileReloading {
|
||||||
|
|
||||||
[self addPacks:4];
|
[self addPacks:4];
|
||||||
|
|
||||||
NSInteger countOfPacks = [MLNOfflineStorage sharedOfflineStorage].packs.count;
|
NSInteger countOfPacks = [MLNOfflineStorage sharedOfflineStorage].packs.count;
|
||||||
XCTAssert(countOfPacks > 0);
|
XCTAssert(countOfPacks > 0);
|
||||||
|
|
||||||
// Now delete packs one by one
|
// Now delete packs one by one
|
||||||
dispatch_queue_t queue = dispatch_queue_create("com.mapbox.testRemovePacks", DISPATCH_QUEUE_SERIAL);
|
dispatch_queue_t queue =
|
||||||
|
dispatch_queue_create("com.mapbox.testRemovePacks", DISPATCH_QUEUE_SERIAL);
|
||||||
|
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"all packs removed"];
|
XCTestExpectation *expectation = [self expectationWithDescription:@"all packs removed"];
|
||||||
expectation.expectedFulfillmentCount = countOfPacks;
|
expectation.expectedFulfillmentCount = countOfPacks;
|
||||||
@@ -456,7 +611,7 @@
|
|||||||
// Simulate what happens the first time sharedOfflineStorage is accessed
|
// Simulate what happens the first time sharedOfflineStorage is accessed
|
||||||
[storage reloadPacks];
|
[storage reloadPacks];
|
||||||
|
|
||||||
// NSArray *packs = [storage.packs copy];
|
// NSArray *packs = [storage.packs copy];
|
||||||
|
|
||||||
dispatch_async(queue, ^{
|
dispatch_async(queue, ^{
|
||||||
NSArray *packs = storage.packs;
|
NSArray *packs = storage.packs;
|
||||||
@@ -465,8 +620,12 @@
|
|||||||
|
|
||||||
[[[NSThread currentThread] threadDictionary] setValue:newHandler forKey:NSAssertionHandlerKey];
|
[[[NSThread currentThread] threadDictionary] setValue:newHandler forKey:NSAssertionHandlerKey];
|
||||||
|
|
||||||
NSArray *validPacks = [packs filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
|
NSArray *validPacks = [packs
|
||||||
MLNOfflinePack *pack = (MLNOfflinePack*)evaluatedObject;
|
filteredArrayUsingPredicate:[NSPredicate
|
||||||
|
predicateWithBlock:^BOOL(
|
||||||
|
id _Nullable evaluatedObject,
|
||||||
|
NSDictionary<NSString *, id> *_Nullable bindings) {
|
||||||
|
MLNOfflinePack *pack = (MLNOfflinePack *)evaluatedObject;
|
||||||
return pack.state != MLNOfflinePackStateInvalid;
|
return pack.state != MLNOfflinePackStateInvalid;
|
||||||
}]];
|
}]];
|
||||||
|
|
||||||
@@ -476,7 +635,8 @@
|
|||||||
|
|
||||||
dispatch_group_t group = dispatch_group_create();
|
dispatch_group_t group = dispatch_group_create();
|
||||||
dispatch_group_enter(group);
|
dispatch_group_enter(group);
|
||||||
[storage removePack:pack withCompletionHandler:^(NSError * _Nullable error) {
|
[storage removePack:pack
|
||||||
|
withCompletionHandler:^(NSError *_Nullable error) {
|
||||||
dispatch_group_leave(group);
|
dispatch_group_leave(group);
|
||||||
}];
|
}];
|
||||||
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
||||||
@@ -487,7 +647,7 @@
|
|||||||
[[[NSThread currentThread] threadDictionary] setValue:oldHandler forKey:NSAssertionHandlerKey];
|
[[[NSThread currentThread] threadDictionary] setValue:oldHandler forKey:NSAssertionHandlerKey];
|
||||||
});
|
});
|
||||||
|
|
||||||
[self waitForExpectations:@[expectation] timeout:60.0];
|
[self waitForExpectations:@[ expectation ] timeout:60.0];
|
||||||
|
|
||||||
// TODO: What should we expect here? All packs removed?
|
// TODO: What should we expect here? All packs removed?
|
||||||
|
|
||||||
@@ -505,7 +665,7 @@
|
|||||||
auto fs = os.mbglOnlineFileSource;
|
auto fs = os.mbglOnlineFileSource;
|
||||||
|
|
||||||
// Delegate returns "https://api.mapbox.com" as a replacement URL.
|
// Delegate returns "https://api.mapbox.com" as a replacement URL.
|
||||||
const mbgl::Resource resource { mbgl::Resource::Unknown, "test://api" };
|
const mbgl::Resource resource{mbgl::Resource::Unknown, "test://api"};
|
||||||
std::unique_ptr<mbgl::AsyncRequest> req;
|
std::unique_ptr<mbgl::AsyncRequest> req;
|
||||||
req = fs->request(resource, [&](mbgl::Response res) {
|
req = fs->request(resource, [&](mbgl::Response res) {
|
||||||
req.reset();
|
req.reset();
|
||||||
@@ -524,71 +684,126 @@
|
|||||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||||
|
|
||||||
// Valid database
|
// Valid database
|
||||||
[XCTContext runActivityNamed:@"Valid database" block:^(id<XCTActivity> activity) {
|
[XCTContext
|
||||||
NSURL *resourceURL = [NSURL fileURLWithPath:[[NSBundle bundleForClass:[self class]] pathForResource:@"sideload_sat" ofType:@"db"]];
|
runActivityNamed:@"Valid database"
|
||||||
|
block:^(id<XCTActivity> activity) {
|
||||||
|
NSURL *resourceURL =
|
||||||
|
[NSURL fileURLWithPath:[[NSBundle bundleForClass:[self class]]
|
||||||
|
pathForResource:@"sideload_sat"
|
||||||
|
ofType:@"db"]];
|
||||||
|
|
||||||
NSError *error;
|
NSError *error;
|
||||||
NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:resourceURL.path error:&error];
|
NSDictionary *fileAttributes =
|
||||||
XCTAssertNil(error, @"Getting the file's attributes should not return an error. (%@)", resourceURL.path);
|
[fileManager attributesOfItemAtPath:resourceURL.path error:&error];
|
||||||
|
XCTAssertNil(error,
|
||||||
|
@"Getting the file's attributes should not return an error. (%@)",
|
||||||
|
resourceURL.path);
|
||||||
|
|
||||||
NSNumber *fileSizeNumber = [fileAttributes objectForKey:NSFileSize];
|
NSNumber *fileSizeNumber = [fileAttributes objectForKey:NSFileSize];
|
||||||
long long fileSize = [fileSizeNumber longLongValue];
|
long long fileSize = [fileSizeNumber longLongValue];
|
||||||
long long databaseFileSize = 19218432;
|
long long databaseFileSize = 19218432;
|
||||||
// Merging databases creates an empty file if the file does not exist at the given path.
|
// Merging databases creates an empty file if the file does not exist at the
|
||||||
XCTAssertEqual(fileSize, databaseFileSize, @"The database file size must be:%lld actual size:%lld", databaseFileSize, fileSize);
|
// given path.
|
||||||
|
XCTAssertEqual(fileSize, databaseFileSize,
|
||||||
|
@"The database file size must be:%lld actual size:%lld",
|
||||||
|
databaseFileSize, fileSize);
|
||||||
|
|
||||||
NSUInteger countOfPacks = [MLNOfflineStorage sharedOfflineStorage].packs.count;
|
NSUInteger countOfPacks = [MLNOfflineStorage sharedOfflineStorage].packs.count;
|
||||||
|
|
||||||
[self keyValueObservingExpectationForObject:[MLNOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
|
[self
|
||||||
const auto changeKind = static_cast<NSKeyValueChange>([change[NSKeyValueChangeKindKey] unsignedLongValue]);
|
keyValueObservingExpectationForObject:[MLNOfflineStorage
|
||||||
NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
|
sharedOfflineStorage]
|
||||||
return changeKind == NSKeyValueChangeInsertion && indices.count == 1;
|
keyPath:@"packs"
|
||||||
|
handler:^BOOL(id _Nonnull observedObject,
|
||||||
|
NSDictionary *_Nonnull change) {
|
||||||
|
const auto changeKind =
|
||||||
|
static_cast<NSKeyValueChange>(
|
||||||
|
[change[NSKeyValueChangeKindKey]
|
||||||
|
unsignedLongValue]);
|
||||||
|
NSIndexSet *indices =
|
||||||
|
change[NSKeyValueChangeIndexesKey];
|
||||||
|
return changeKind ==
|
||||||
|
NSKeyValueChangeInsertion &&
|
||||||
|
indices.count == 1;
|
||||||
}];
|
}];
|
||||||
|
|
||||||
XCTestExpectation *fileAdditionCompletionHandlerExpectation = [self expectationWithDescription:@"add database content completion handler"];
|
XCTestExpectation *fileAdditionCompletionHandlerExpectation =
|
||||||
|
[self expectationWithDescription:@"add database content completion handler"];
|
||||||
MLNOfflineStorage *os = [MLNOfflineStorage sharedOfflineStorage];
|
MLNOfflineStorage *os = [MLNOfflineStorage sharedOfflineStorage];
|
||||||
[os addContentsOfURL:resourceURL withCompletionHandler:^(NSURL *fileURL, NSArray<MLNOfflinePack *> * _Nullable packs, NSError * _Nullable error) {
|
[os addContentsOfURL:resourceURL
|
||||||
|
withCompletionHandler:^(NSURL *fileURL,
|
||||||
|
NSArray<MLNOfflinePack *> *_Nullable packs,
|
||||||
|
NSError *_Nullable error) {
|
||||||
XCTAssertNotNil(fileURL, @"The fileURL should not be nil.");
|
XCTAssertNotNil(fileURL, @"The fileURL should not be nil.");
|
||||||
XCTAssertNotNil(packs, @"Adding the contents of the sideload_sat.db should update one pack.");
|
XCTAssertNotNil(
|
||||||
XCTAssertNil(error, @"Adding contents to a file should not return an error.");
|
packs,
|
||||||
for (MLNOfflinePack *pack in [MLNOfflineStorage sharedOfflineStorage].packs) {
|
@"Adding the contents of the sideload_sat.db should update one pack.");
|
||||||
|
XCTAssertNil(error,
|
||||||
|
@"Adding contents to a file should not return an error.");
|
||||||
|
for (MLNOfflinePack *pack in [MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
.packs) {
|
||||||
NSLog(@"PACK:%@", pack);
|
NSLog(@"PACK:%@", pack);
|
||||||
}
|
}
|
||||||
[fileAdditionCompletionHandlerExpectation fulfill];
|
[fileAdditionCompletionHandlerExpectation fulfill];
|
||||||
}];
|
}];
|
||||||
[self waitForExpectationsWithTimeout:10 handler:nil];
|
[self waitForExpectationsWithTimeout:10 handler:nil];
|
||||||
// Depending on the database it may update or add a pack. For this case specifically the offline database adds one pack.
|
// Depending on the database it may update or add a pack. For this case
|
||||||
XCTAssertEqual([MLNOfflineStorage sharedOfflineStorage].packs.count, countOfPacks + 1, @"Adding contents of sideload_sat.db should add one pack.");
|
// specifically the offline database adds one pack.
|
||||||
|
XCTAssertEqual([MLNOfflineStorage sharedOfflineStorage].packs.count,
|
||||||
|
countOfPacks + 1,
|
||||||
|
@"Adding contents of sideload_sat.db should add one pack.");
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// Invalid database type
|
// Invalid database type
|
||||||
[XCTContext runActivityNamed:@"Invalid database type" block:^(id<XCTActivity> activity) {
|
[XCTContext
|
||||||
NSURL *resourceURL = [NSURL fileURLWithPath:[[NSBundle bundleForClass:[self class]] pathForResource:@"one-liner" ofType:@"json"]];
|
runActivityNamed:@"Invalid database type"
|
||||||
|
block:^(id<XCTActivity> activity) {
|
||||||
|
NSURL *resourceURL =
|
||||||
|
[NSURL fileURLWithPath:[[NSBundle bundleForClass:[self class]]
|
||||||
|
pathForResource:@"one-liner"
|
||||||
|
ofType:@"json"]];
|
||||||
|
|
||||||
XCTestExpectation *invalidFileCompletionHandlerExpectation = [self expectationWithDescription:@"invalid content database completion handler"];
|
XCTestExpectation *invalidFileCompletionHandlerExpectation = [self
|
||||||
|
expectationWithDescription:@"invalid content database completion handler"];
|
||||||
MLNOfflineStorage *os = [MLNOfflineStorage sharedOfflineStorage];
|
MLNOfflineStorage *os = [MLNOfflineStorage sharedOfflineStorage];
|
||||||
[os addContentsOfFile:resourceURL.path withCompletionHandler:^(NSURL *fileURL, NSArray<MLNOfflinePack *> * _Nullable packs, NSError * _Nullable error) {
|
[os addContentsOfFile:resourceURL.path
|
||||||
XCTAssertNotNil(error, @"Passing an invalid offline database file should return an error.");
|
withCompletionHandler:^(NSURL *fileURL,
|
||||||
XCTAssertNil(packs, @"Passing an invalid offline database file should not add packs to the offline database.");
|
NSArray<MLNOfflinePack *> *_Nullable packs,
|
||||||
|
NSError *_Nullable error) {
|
||||||
|
XCTAssertNotNil(
|
||||||
|
error,
|
||||||
|
@"Passing an invalid offline database file should return an error.");
|
||||||
|
XCTAssertNil(packs, @"Passing an invalid offline database file should not "
|
||||||
|
@"add packs to the offline database.");
|
||||||
[invalidFileCompletionHandlerExpectation fulfill];
|
[invalidFileCompletionHandlerExpectation fulfill];
|
||||||
}];
|
}];
|
||||||
[self waitForExpectationsWithTimeout:10 handler:nil];
|
[self waitForExpectationsWithTimeout:10 handler:nil];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// File does not exist
|
// File does not exist
|
||||||
[XCTContext runActivityNamed:@"File does not exist" block:^(id<XCTActivity> activity) {
|
[XCTContext runActivityNamed:@"File does not exist"
|
||||||
|
block:^(id<XCTActivity> activity) {
|
||||||
NSURL *resourceURL = [NSURL URLWithString:@"nonexistent.db"];
|
NSURL *resourceURL = [NSURL URLWithString:@"nonexistent.db"];
|
||||||
|
|
||||||
MLNOfflineStorage *os = [MLNOfflineStorage sharedOfflineStorage];
|
MLNOfflineStorage *os = [MLNOfflineStorage sharedOfflineStorage];
|
||||||
XCTAssertThrowsSpecificNamed([os addContentsOfURL:resourceURL withCompletionHandler:nil], NSException, NSInvalidArgumentException, "MLNOfflineStorage should rise an exception if an invalid database file is passed.");
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
[os addContentsOfURL:resourceURL withCompletionHandler:nil],
|
||||||
|
NSException, NSInvalidArgumentException,
|
||||||
|
"MLNOfflineStorage should rise an exception if an invalid database "
|
||||||
|
"file is passed.");
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// URL to a non-file
|
// URL to a non-file
|
||||||
[XCTContext runActivityNamed:@"URL to a non-file" block:^(id<XCTActivity> activity) {
|
[XCTContext runActivityNamed:@"URL to a non-file"
|
||||||
|
block:^(id<XCTActivity> activity) {
|
||||||
NSURL *resourceURL = [NSURL URLWithString:@"https://www.mapbox.com"];
|
NSURL *resourceURL = [NSURL URLWithString:@"https://www.mapbox.com"];
|
||||||
|
|
||||||
MLNOfflineStorage *os = [MLNOfflineStorage sharedOfflineStorage];
|
MLNOfflineStorage *os = [MLNOfflineStorage sharedOfflineStorage];
|
||||||
XCTAssertThrowsSpecificNamed([os addContentsOfURL:resourceURL withCompletionHandler:nil], NSException, NSInvalidArgumentException, "MLNOfflineStorage should rise an exception if an invalid URL file is passed.");
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
[os addContentsOfURL:resourceURL withCompletionHandler:nil],
|
||||||
|
NSException, NSInvalidArgumentException,
|
||||||
|
"MLNOfflineStorage should rise an exception if an invalid URL file "
|
||||||
|
"is passed.");
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,12 +814,18 @@
|
|||||||
std::string testData("test data");
|
std::string testData("test data");
|
||||||
NSData *data = [NSData dataWithBytes:testData.c_str() length:testData.length()];
|
NSData *data = [NSData dataWithBytes:testData.c_str() length:testData.length()];
|
||||||
__block std::unique_ptr<mbgl::AsyncRequest> req;
|
__block std::unique_ptr<mbgl::AsyncRequest> req;
|
||||||
[os preloadData:data forURL:styleURL modificationDate:nil expirationDate:nil eTag:nil mustRevalidate:NO completionHandler:^(NSURL * url, NSError * _Nullable error) {
|
[os preloadData:data
|
||||||
|
forURL:styleURL
|
||||||
|
modificationDate:nil
|
||||||
|
expirationDate:nil
|
||||||
|
eTag:nil
|
||||||
|
mustRevalidate:NO
|
||||||
|
completionHandler:^(NSURL *url, NSError *_Nullable error) {
|
||||||
XCTAssertFalse(error, @"preloadData should not return an error");
|
XCTAssertFalse(error, @"preloadData should not return an error");
|
||||||
XCTAssertEqual(styleURL, url, @"Preloaded resource url is invalid");
|
XCTAssertEqual(styleURL, url, @"Preloaded resource url is invalid");
|
||||||
|
|
||||||
auto fs = os.mbglDatabaseFileSource;
|
auto fs = os.mbglDatabaseFileSource;
|
||||||
const mbgl::Resource resource { mbgl::Resource::Unknown, "https://api.mapbox.com/some/thing" };
|
const mbgl::Resource resource{mbgl::Resource::Unknown, "https://api.mapbox.com/some/thing"};
|
||||||
req = fs->request(resource, [&](mbgl::Response res) {
|
req = fs->request(resource, [&](mbgl::Response res) {
|
||||||
XCTAssertFalse(res.error.get(), @"Request should not return an error");
|
XCTAssertFalse(res.error.get(), @"Request should not return an error");
|
||||||
XCTAssertTrue(res.data.get(), @"Request should return data");
|
XCTAssertTrue(res.data.get(), @"Request should return data");
|
||||||
@@ -629,19 +850,29 @@
|
|||||||
__block NSDate *now = [NSDate date];
|
__block NSDate *now = [NSDate date];
|
||||||
__block NSDate *future = [now dateByAddingTimeInterval:600];
|
__block NSDate *future = [now dateByAddingTimeInterval:600];
|
||||||
__block std::unique_ptr<mbgl::AsyncRequest> req;
|
__block std::unique_ptr<mbgl::AsyncRequest> req;
|
||||||
[os preloadData:data forURL:styleURL modificationDate:now expirationDate:future eTag:@"some etag" mustRevalidate:YES completionHandler:^(NSURL * url, NSError * _Nullable error){
|
[os preloadData:data
|
||||||
|
forURL:styleURL
|
||||||
|
modificationDate:now
|
||||||
|
expirationDate:future
|
||||||
|
eTag:@"some etag"
|
||||||
|
mustRevalidate:YES
|
||||||
|
completionHandler:^(NSURL *url, NSError *_Nullable error) {
|
||||||
XCTAssertFalse(error, @"preloadData should not return an error");
|
XCTAssertFalse(error, @"preloadData should not return an error");
|
||||||
XCTAssertEqual(styleURL, url, @"Preloaded resource url is invalid");
|
XCTAssertEqual(styleURL, url, @"Preloaded resource url is invalid");
|
||||||
|
|
||||||
auto fs = os.mbglDatabaseFileSource;
|
auto fs = os.mbglDatabaseFileSource;
|
||||||
const mbgl::Resource resource { mbgl::Resource::Unknown, "https://api.mapbox.com/some/thing1" };
|
const mbgl::Resource resource{mbgl::Resource::Unknown,
|
||||||
req = fs->request(resource, [&, tNow = now.timeIntervalSince1970, tFuture = future.timeIntervalSince1970](mbgl::Response res) {
|
"https://api.mapbox.com/some/thing1"};
|
||||||
|
req = fs->request(resource, [&, tNow = now.timeIntervalSince1970,
|
||||||
|
tFuture = future.timeIntervalSince1970](mbgl::Response res) {
|
||||||
XCTAssertFalse(res.error.get(), @"Request should not return an error");
|
XCTAssertFalse(res.error.get(), @"Request should not return an error");
|
||||||
XCTAssertTrue(res.data.get(), @"Request should return data");
|
XCTAssertTrue(res.data.get(), @"Request should return data");
|
||||||
XCTAssertTrue(res.modified, @"Request should have a modification timestamp");
|
XCTAssertTrue(res.modified, @"Request should have a modification timestamp");
|
||||||
XCTAssertEqual(MLNTimeIntervalFromDuration(res.modified->time_since_epoch()), floor(tNow), @"Modification timestamp should roundtrip");
|
XCTAssertEqual(MLNTimeIntervalFromDuration(res.modified->time_since_epoch()), floor(tNow),
|
||||||
|
@"Modification timestamp should roundtrip");
|
||||||
XCTAssertTrue(res.expires, @"Request should have an expiration timestamp");
|
XCTAssertTrue(res.expires, @"Request should have an expiration timestamp");
|
||||||
XCTAssertEqual(MLNTimeIntervalFromDuration(res.expires->time_since_epoch()), floor(tFuture), @"Expiration timestamp should roundtrip");
|
XCTAssertEqual(MLNTimeIntervalFromDuration(res.expires->time_since_epoch()),
|
||||||
|
floor(tFuture), @"Expiration timestamp should roundtrip");
|
||||||
XCTAssertTrue(res.etag, @"Request should have an entity tag");
|
XCTAssertTrue(res.etag, @"Request should have an entity tag");
|
||||||
XCTAssertEqual(*res.etag, "some etag", @"Entity tag should roundtrip");
|
XCTAssertEqual(*res.etag, "some etag", @"Entity tag should roundtrip");
|
||||||
XCTAssertTrue(res.mustRevalidate, @"Request should require revalidation");
|
XCTAssertTrue(res.mustRevalidate, @"Request should require revalidation");
|
||||||
@@ -654,10 +885,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testSetMaximumAmbientCache {
|
- (void)testSetMaximumAmbientCache {
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Expect maximum cache size to be raised without an error."];
|
XCTestExpectation *expectation =
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] setMaximumAmbientCacheSize:0 withCompletionHandler:^(NSError * _Nullable error) {
|
[self expectationWithDescription:@"Expect maximum cache size to be raised without an error."];
|
||||||
|
[[MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
setMaximumAmbientCacheSize:0
|
||||||
|
withCompletionHandler:^(NSError *_Nullable error) {
|
||||||
XCTAssertNil(error);
|
XCTAssertNil(error);
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] setMaximumAmbientCacheSize:50*1024*1024 withCompletionHandler:^(NSError * _Nullable error) {
|
[[MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
setMaximumAmbientCacheSize:50 * 1024 * 1024
|
||||||
|
withCompletionHandler:^(NSError *_Nullable error) {
|
||||||
XCTAssertNil(error);
|
XCTAssertNil(error);
|
||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
}];
|
}];
|
||||||
@@ -667,8 +903,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testInvalidateAmbientCache {
|
- (void)testInvalidateAmbientCache {
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Expect cache to be invalidated without an error."];
|
XCTestExpectation *expectation =
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] invalidateAmbientCacheWithCompletionHandler:^(NSError * _Nullable error) {
|
[self expectationWithDescription:@"Expect cache to be invalidated without an error."];
|
||||||
|
[[MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
invalidateAmbientCacheWithCompletionHandler:^(NSError *_Nullable error) {
|
||||||
XCTAssertNil(error);
|
XCTAssertNil(error);
|
||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
}];
|
}];
|
||||||
@@ -676,8 +914,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testClearCache {
|
- (void)testClearCache {
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Expect cache to be cleared without an error."];
|
XCTestExpectation *expectation =
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] clearAmbientCacheWithCompletionHandler:^(NSError * _Nullable error) {
|
[self expectationWithDescription:@"Expect cache to be cleared without an error."];
|
||||||
|
[[MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
clearAmbientCacheWithCompletionHandler:^(NSError *_Nullable error) {
|
||||||
XCTAssertNil(error);
|
XCTAssertNil(error);
|
||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
}];
|
}];
|
||||||
@@ -685,8 +925,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testResetDatabase {
|
- (void)testResetDatabase {
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Expect database to be reset without an error."];
|
XCTestExpectation *expectation =
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] resetDatabaseWithCompletionHandler:^(NSError * _Nullable error) {
|
[self expectationWithDescription:@"Expect database to be reset without an error."];
|
||||||
|
[[MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
resetDatabaseWithCompletionHandler:^(NSError *_Nullable error) {
|
||||||
XCTAssertNil(error);
|
XCTAssertNil(error);
|
||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
}];
|
}];
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#import <XCTest/XCTest.h>
|
|
||||||
#import <Mapbox.h>
|
#import <Mapbox.h>
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
#import "NSPredicate+MLNPrivateAdditions.h"
|
|
||||||
#import "MLNValueEvaluator.h"
|
#import "MLNValueEvaluator.h"
|
||||||
|
#import "NSPredicate+MLNPrivateAdditions.h"
|
||||||
|
|
||||||
@implementation NSString (MLNAdditions)
|
@implementation NSString (MLNAdditions)
|
||||||
|
|
||||||
@@ -21,23 +21,34 @@
|
|||||||
@implementation MLNPredicateTests
|
@implementation MLNPredicateTests
|
||||||
|
|
||||||
- (void)testUnsupportedFilterPredicates {
|
- (void)testUnsupportedFilterPredicates {
|
||||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BEGINSWITH 'L'"].mgl_filter, NSException, NSInvalidArgumentException);
|
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BEGINSWITH 'L'"].mgl_filter,
|
||||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a ENDSWITH 'itude'"].mgl_filter, NSException, NSInvalidArgumentException);
|
NSException, NSInvalidArgumentException);
|
||||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a LIKE 'glob?trotter'"].mgl_filter, NSException, NSInvalidArgumentException);
|
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a ENDSWITH 'itude'"].mgl_filter,
|
||||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a MATCHES 'i\\w{18}n'"].mgl_filter, NSException, NSInvalidArgumentException);
|
NSException, NSInvalidArgumentException);
|
||||||
NSPredicate *selectorPredicate = [NSPredicate predicateWithFormat:@"(SELF isKindOfClass: %@)", [MLNPolyline class]];
|
XCTAssertThrowsSpecificNamed(
|
||||||
XCTAssertThrowsSpecificNamed(selectorPredicate.mgl_filter, NSException, NSInvalidArgumentException);
|
[NSPredicate predicateWithFormat:@"a LIKE 'glob?trotter'"].mgl_filter, NSException,
|
||||||
|
NSInvalidArgumentException);
|
||||||
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
[NSPredicate predicateWithFormat:@"a MATCHES 'i\\w{18}n'"].mgl_filter, NSException,
|
||||||
|
NSInvalidArgumentException);
|
||||||
|
NSPredicate *selectorPredicate =
|
||||||
|
[NSPredicate predicateWithFormat:@"(SELF isKindOfClass: %@)", [MLNPolyline class]];
|
||||||
|
XCTAssertThrowsSpecificNamed(selectorPredicate.mgl_filter, NSException,
|
||||||
|
NSInvalidArgumentException);
|
||||||
|
|
||||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *, id> * _Nullable bindings) {
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject,
|
||||||
|
NSDictionary<NSString *, id> *_Nullable bindings) {
|
||||||
XCTAssertTrue(NO, @"Predicate block should not be evaluated.");
|
XCTAssertTrue(NO, @"Predicate block should not be evaluated.");
|
||||||
return NO;
|
return NO;
|
||||||
}].mgl_filter, NSException, NSInvalidArgumentException);
|
}].mgl_filter,
|
||||||
|
NSException, NSInvalidArgumentException);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testComparisonPredicates {
|
- (void)testComparisonPredicates {
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x == YES"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x == YES"];
|
||||||
NSArray *jsonExpression = @[@"==", @[@"get", @"x"], @YES];
|
NSArray *jsonExpression = @[ @"==", @[ @"get", @"x" ], @YES ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
@@ -45,7 +56,7 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') < 5"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') < 5"];
|
||||||
NSArray *jsonExpression = @[@"<", @[@"to-number", @[@"get", @"x"]], @5];
|
NSArray *jsonExpression = @[ @"<", @[ @"to-number", @[ @"get", @"x" ] ], @5 ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
@@ -53,7 +64,7 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') > 5"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') > 5"];
|
||||||
NSArray *jsonExpression = @[@">", @[@"to-number", @[@"get", @"x"]], @5];
|
NSArray *jsonExpression = @[ @">", @[ @"to-number", @[ @"get", @"x" ] ], @5 ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
@@ -61,7 +72,7 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') <= 5"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') <= 5"];
|
||||||
NSArray *jsonExpression = @[@"<=", @[@"to-number", @[@"get", @"x"]], @5];
|
NSArray *jsonExpression = @[ @"<=", @[ @"to-number", @[ @"get", @"x" ] ], @5 ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
@@ -69,7 +80,7 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') >= 5"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') >= 5"];
|
||||||
NSArray *jsonExpression = @[@">=", @[@"to-number", @[@"get", @"x"]], @5];
|
NSArray *jsonExpression = @[ @">=", @[ @"to-number", @[ @"get", @"x" ] ], @5 ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
@@ -77,7 +88,7 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSString') > 'value'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSString') > 'value'"];
|
||||||
NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"x"]], @"value"];
|
NSArray *jsonExpression = @[ @">", @[ @"to-string", @[ @"get", @"x" ] ], @"value" ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
@@ -85,77 +96,67 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a = 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a = 'b'"];
|
||||||
NSArray *jsonExpression = @[@"==", @[@"get", @"a"], @"b"];
|
NSArray *jsonExpression = @[ @"==", @[ @"get", @"a" ], @"b" ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$geometryType = 'Point'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$geometryType = 'Point'"];
|
||||||
NSArray *jsonExpression = @[@"==", @[@"geometry-type"], @"Point"];
|
NSArray *jsonExpression = @[ @"==", @[ @"geometry-type" ], @"Point" ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 67086180"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 67086180"];
|
||||||
NSArray *jsonExpression = @[@"==", @[@"id"], @67086180];
|
NSArray *jsonExpression = @[ @"==", @[ @"id" ], @67086180 ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = nil"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = nil"];
|
||||||
NSArray *jsonExpression = @[@"==", @[@"id"], [NSNull null]];
|
NSArray *jsonExpression = @[ @"==", @[ @"id" ], [NSNull null] ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a = nil"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a = nil"];
|
||||||
NSArray *jsonExpression = @[@"==", @[@"get", @"a"], [NSNull null]];
|
NSArray *jsonExpression = @[ @"==", @[ @"get", @"a" ], [NSNull null] ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$geometryType != 'Point'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$geometryType != 'Point'"];
|
||||||
NSArray *jsonExpression = @[@"!=", @[@"geometry-type"], @"Point"];
|
NSArray *jsonExpression = @[ @"!=", @[ @"geometry-type" ], @"Point" ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier != 67086180"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier != 67086180"];
|
||||||
NSArray *jsonExpression = @[@"!=", @[@"id"], @67086180];
|
NSArray *jsonExpression = @[ @"!=", @[ @"id" ], @67086180 ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier != nil"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier != nil"];
|
||||||
NSArray *jsonExpression = @[@"!=", @[@"id"], [NSNull null]];
|
NSArray *jsonExpression = @[ @"!=", @[ @"id" ], [NSNull null] ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a != 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a != 'b'"];
|
||||||
NSArray *jsonExpression = @[@"!=", @[@"get", @"a"], @"b"];
|
NSArray *jsonExpression = @[ @"!=", @[ @"get", @"a" ], @"b" ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a != nil"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a != nil"];
|
||||||
NSArray *jsonExpression = @[@"!=", @[@"get", @"a"], [NSNull null]];
|
NSArray *jsonExpression = @[ @"!=", @[ @"get", @"a" ], [NSNull null] ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') < 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') < 'b'"];
|
||||||
NSArray *jsonExpression = @[@"<", @[@"to-string", @[@"get", @"a"]], @"b"];
|
NSArray *jsonExpression = @[ @"<", @[ @"to-string", @[ @"get", @"a" ] ], @"b" ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
@@ -163,7 +164,7 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') <= 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') <= 'b'"];
|
||||||
NSArray *jsonExpression = @[@"<=", @[@"to-string", @[@"get", @"a"]], @"b"];
|
NSArray *jsonExpression = @[ @"<=", @[ @"to-string", @[ @"get", @"a" ] ], @"b" ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
@@ -171,7 +172,7 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') > 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') > 'b'"];
|
||||||
NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"a"]], @"b"];
|
NSArray *jsonExpression = @[ @">", @[ @"to-string", @[ @"get", @"a" ] ], @"b" ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
@@ -179,71 +180,106 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') >= 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') >= 'b'"];
|
||||||
NSArray *jsonExpression = @[@">=", @[@"to-string", @[@"get", @"a"]], @"b"];
|
NSArray *jsonExpression = @[ @">=", @[ @"to-string", @[ @"get", @"a" ] ], @"b" ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
mustRoundTrip:NO];
|
mustRoundTrip:NO];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') BETWEEN {'b', 'z'}"];
|
NSPredicate *predicate =
|
||||||
NSArray *jsonExpression =@[@"all", @[@"<=", @"b", @[@"to-string", @[@"get", @"a"]]], @[@"<=", @[@"to-string", @[@"get", @"a"]], @"z"]];
|
[NSPredicate predicateWithFormat:@"CAST(a, 'NSString') BETWEEN {'b', 'z'}"];
|
||||||
|
NSArray *jsonExpression = @[
|
||||||
|
@"all", @[ @"<=", @"b", @[ @"to-string", @[ @"get", @"a" ] ] ],
|
||||||
|
@[ @"<=", @[ @"to-string", @[ @"get", @"a" ] ], @"z" ]
|
||||||
|
];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
mustRoundTrip:NO];
|
mustRoundTrip:NO];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
|
NSExpression *limits = [NSExpression expressionForAggregate:@[
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
|
[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]
|
||||||
NSArray *jsonExpression = @[@"all", @[@">=", @[@"to-number", @[@"get", @"x"]], @10], @[@"<=", @[@"to-number", @[@"get", @"x"]], @100]];
|
]];
|
||||||
|
NSPredicate *predicate =
|
||||||
|
[NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
|
||||||
|
NSArray *jsonExpression = @[
|
||||||
|
@"all", @[ @">=", @[ @"to-number", @[ @"get", @"x" ] ], @10 ],
|
||||||
|
@[ @"<=", @[ @"to-number", @[ @"get", @"x" ] ], @100 ]
|
||||||
|
];
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
mustRoundTrip:NO];
|
mustRoundTrip:NO];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"all", @[@"<=", @10, @[@"to-number", @[@"get", @"x"]]], @[@"<=", @[@"to-number", @[@"get", @"x"]], @100]];
|
NSArray *expected = @[
|
||||||
NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
|
@"all", @[ @"<=", @10, @[ @"to-number", @[ @"get", @"x" ] ] ],
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
|
@[ @"<=", @[ @"to-number", @[ @"get", @"x" ] ], @100 ]
|
||||||
|
];
|
||||||
|
NSExpression *limits = [NSExpression expressionForAggregate:@[
|
||||||
|
[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]
|
||||||
|
]];
|
||||||
|
NSPredicate *predicate =
|
||||||
|
[NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
||||||
mustRoundTrip:NO];
|
mustRoundTrip:NO];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"all", @[@"<=", @10, @[@"to-number", @[@"get", @"x"]]], @[@">=", @100, @[@"to-number", @[@"get", @"x"]]]];
|
NSArray *expected = @[
|
||||||
NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
|
@"all", @[ @"<=", @10, @[ @"to-number", @[ @"get", @"x" ] ] ],
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
|
@[ @">=", @100, @[ @"to-number", @[ @"get", @"x" ] ] ]
|
||||||
|
];
|
||||||
|
NSExpression *limits = [NSExpression expressionForAggregate:@[
|
||||||
|
[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]
|
||||||
|
]];
|
||||||
|
NSPredicate *predicate =
|
||||||
|
[NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
||||||
mustRoundTrip:NO];
|
mustRoundTrip:NO];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"all", @[@">=", @[@"to-number", @[@"get", @"x"]], @10], @[@">=", @100, @[@"to-number", @[@"get", @"x"]]]];
|
NSArray *expected = @[
|
||||||
NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
|
@"all", @[ @">=", @[ @"to-number", @[ @"get", @"x" ] ], @10 ],
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
|
@[ @">=", @100, @[ @"to-number", @[ @"get", @"x" ] ] ]
|
||||||
|
];
|
||||||
|
NSExpression *limits = [NSExpression expressionForAggregate:@[
|
||||||
|
[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]
|
||||||
|
]];
|
||||||
|
NSPredicate *predicate =
|
||||||
|
[NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
||||||
mustRoundTrip:NO];
|
mustRoundTrip:NO];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"in", @[@"id"], @[@"literal", @[@6, @5, @4, @3]]];
|
NSArray *expected = @[ @"in", @[ @"id" ], @[ @"literal", @[ @6, @5, @4, @3 ] ] ];
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier IN { 6, 5, 4, 3}"];
|
NSPredicate *predicate =
|
||||||
|
[NSPredicate predicateWithFormat:@"$featureIdentifier IN { 6, 5, 4, 3}"];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
||||||
mustRoundTrip:YES];
|
mustRoundTrip:YES];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"in", @[@"to-number", @[@"id"]], @[@"literal", @[@3002970001, @3004140052, @3002950027, @3002970033]]];
|
NSArray *expected = @[
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST($featureIdentifier, 'NSNumber') IN { 3002970001, 3004140052, 3002950027, 3002970033 }"];
|
@"in", @[ @"to-number", @[ @"id" ] ],
|
||||||
|
@[ @"literal", @[ @3002970001, @3004140052, @3002950027, @3002970033 ] ]
|
||||||
|
];
|
||||||
|
NSPredicate *predicate =
|
||||||
|
[NSPredicate predicateWithFormat:@"CAST($featureIdentifier, 'NSNumber') IN { 3002970001, "
|
||||||
|
@"3004140052, 3002950027, 3002970033 }"];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
||||||
mustRoundTrip:YES];
|
mustRoundTrip:YES];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"!", @[@"in", @[@"get", @"x"], @[@"literal", @[@6, @5, @4, @3]]]];
|
NSArray *expected =
|
||||||
|
@[ @"!", @[ @"in", @[ @"get", @"x" ], @[ @"literal", @[ @6, @5, @4, @3 ] ] ] ];
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT x IN { 6, 5, 4, 3}"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT x IN { 6, 5, 4, 3}"];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
||||||
@@ -251,7 +287,7 @@
|
|||||||
mustRoundTrip:YES];
|
mustRoundTrip:YES];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"in", @[@"get", @"a"], @[@"literal", @[@"b", @"c"]]];
|
NSArray *expected = @[ @"in", @[ @"get", @"a" ], @[ @"literal", @[ @"b", @"c" ] ] ];
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a IN { 'b', 'c' }"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a IN { 'b', 'c' }"];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
||||||
@@ -259,16 +295,21 @@
|
|||||||
mustRoundTrip:YES];
|
mustRoundTrip:YES];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"in", @[@"geometry-type"], @[@"literal", @[@"LineString", @"Polygon"]]];
|
NSArray *expected =
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ IN %@", [NSExpression expressionForVariable:@"geometryType"], @[@"LineString", @"Polygon"]];
|
@[ @"in", @[ @"geometry-type" ], @[ @"literal", @[ @"LineString", @"Polygon" ] ] ];
|
||||||
|
NSPredicate *predicate = [NSPredicate
|
||||||
|
predicateWithFormat:@"%@ IN %@", [NSExpression expressionForVariable:@"geometryType"],
|
||||||
|
@[ @"LineString", @"Polygon" ]];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"%@ IN {'LineString', 'Polygon'}", [NSExpression expressionForVariable:@"geometryType"]];
|
NSPredicate *predicateAfter =
|
||||||
|
[NSPredicate predicateWithFormat:@"%@ IN {'LineString', 'Polygon'}",
|
||||||
|
[NSExpression expressionForVariable:@"geometryType"]];
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
||||||
mustRoundTrip:YES];
|
mustRoundTrip:YES];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"in", @[@"get", @"x"], @[@"literal", @[@6, @5, @4, @3]]];
|
NSArray *expected = @[ @"in", @[ @"get", @"x" ], @[ @"literal", @[ @6, @5, @4, @3 ] ] ];
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3 } CONTAINS x"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3 } CONTAINS x"];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"x IN { 6, 5, 4, 3 }"];
|
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"x IN { 6, 5, 4, 3 }"];
|
||||||
@@ -277,25 +318,32 @@
|
|||||||
mustRoundTrip:YES];
|
mustRoundTrip:YES];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"in", @[@"geometry-type"], @[@"literal", @[@"LineString", @"Polygon"]]];
|
NSArray *expected =
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ CONTAINS %@", @[@"LineString", @"Polygon"], [NSExpression expressionForVariable:@"geometryType"]];
|
@[ @"in", @[ @"geometry-type" ], @[ @"literal", @[ @"LineString", @"Polygon" ] ] ];
|
||||||
|
NSPredicate *predicate =
|
||||||
|
[NSPredicate predicateWithFormat:@"%@ CONTAINS %@", @[ @"LineString", @"Polygon" ],
|
||||||
|
[NSExpression expressionForVariable:@"geometryType"]];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"%@ IN {'LineString', 'Polygon'}", [NSExpression expressionForVariable:@"geometryType"]];
|
NSPredicate *predicateAfter =
|
||||||
|
[NSPredicate predicateWithFormat:@"%@ IN {'LineString', 'Polygon'}",
|
||||||
|
[NSExpression expressionForVariable:@"geometryType"]];
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
||||||
mustRoundTrip:YES];
|
mustRoundTrip:YES];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"in", @[@"id"], @[@"literal", @[@6, @5, @4, @3]]];
|
NSArray *expected = @[ @"in", @[ @"id" ], @[ @"literal", @[ @6, @5, @4, @3 ] ] ];
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS $featureIdentifier"];
|
NSPredicate *predicate =
|
||||||
|
[NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS $featureIdentifier"];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"$featureIdentifier IN {6, 5, 4, 3}"];
|
NSPredicate *predicateAfter =
|
||||||
|
[NSPredicate predicateWithFormat:@"$featureIdentifier IN {6, 5, 4, 3}"];
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
||||||
mustRoundTrip:YES];
|
mustRoundTrip:YES];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"in", @"📍", @[@"get", @"name"]];
|
NSArray *expected = @[ @"in", @"📍", @[ @"get", @"name" ] ];
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"'📍' IN name"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"'📍' IN name"];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
||||||
@@ -303,7 +351,7 @@
|
|||||||
mustRoundTrip:YES];
|
mustRoundTrip:YES];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"in", @"📍", @[@"get", @"name"]];
|
NSArray *expected = @[ @"in", @"📍", @[ @"get", @"name" ] ];
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name CONTAINS '📍'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name CONTAINS '📍'"];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"'📍' IN name"];
|
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"'📍' IN name"];
|
||||||
@@ -312,92 +360,113 @@
|
|||||||
mustRoundTrip:YES];
|
mustRoundTrip:YES];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"in", @"📍", @[@"literal", @"Pinole"]];
|
NSArray *expected = @[ @"in", @"📍", @[ @"literal", @"Pinole" ] ];
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"'📍' IN 'Pinole'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"'📍' IN 'Pinole'"];
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
XCTExpectFailure(@"Awaiting unit test refactoring for https://github.com/maplibre/maplibre-native/issues/331");
|
XCTExpectFailure(@"Awaiting unit test refactoring for "
|
||||||
|
@"https://github.com/maplibre/maplibre-native/issues/331");
|
||||||
#endif
|
#endif
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
||||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:predicate.mgl_filter], [NSPredicate predicateWithValue:NO]);
|
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:predicate.mgl_filter],
|
||||||
|
[NSPredicate predicateWithValue:NO]);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"in", @"📍", @[@"literal", @"Pinole"]];
|
NSArray *expected = @[ @"in", @"📍", @[ @"literal", @"Pinole" ] ];
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"'Pinole' CONTAINS '📍'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"'Pinole' CONTAINS '📍'"];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"'📍' IN 'Pinole'"];
|
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"'📍' IN 'Pinole'"];
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
||||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:predicate.mgl_filter], [NSPredicate predicateWithValue:NO]);
|
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:predicate.mgl_filter],
|
||||||
|
[NSPredicate predicateWithValue:NO]);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"in", @[@"id"], @[@"literal", @[@6, @5, @4, @3]]];
|
NSArray *expected = @[ @"in", @[ @"id" ], @[ @"literal", @[ @6, @5, @4, @3 ] ] ];
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY {6, 5, 4, 3} = $featureIdentifier"];
|
NSPredicate *predicate =
|
||||||
|
[NSPredicate predicateWithFormat:@"ANY {6, 5, 4, 3} = $featureIdentifier"];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"$featureIdentifier IN {6, 5, 4, 3}"];
|
NSPredicate *predicateAfter =
|
||||||
|
[NSPredicate predicateWithFormat:@"$featureIdentifier IN {6, 5, 4, 3}"];
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
||||||
mustRoundTrip:YES];
|
mustRoundTrip:YES];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"!", @[@"in", @[@"id"], @[@"literal", @[@6, @5, @4, @3]]]];
|
NSArray *expected = @[ @"!", @[ @"in", @[ @"id" ], @[ @"literal", @[ @6, @5, @4, @3 ] ] ] ];
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NONE {6, 5, 4, 3} = $featureIdentifier"];
|
NSPredicate *predicate =
|
||||||
|
[NSPredicate predicateWithFormat:@"NONE {6, 5, 4, 3} = $featureIdentifier"];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT $featureIdentifier IN {6, 5, 4, 3}"];
|
NSPredicate *predicateAfter =
|
||||||
|
[NSPredicate predicateWithFormat:@"NOT $featureIdentifier IN {6, 5, 4, 3}"];
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
||||||
mustRoundTrip:YES];
|
mustRoundTrip:YES];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[@"!", @[@"in", @[@"id"], @[@"literal", @[@6, @5, @4, @3]]]];
|
NSArray *expected = @[ @"!", @[ @"in", @[ @"id" ], @[ @"literal", @[ @6, @5, @4, @3 ] ] ] ];
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ALL {6, 5, 4, 3} != $featureIdentifier"];
|
NSPredicate *predicate =
|
||||||
|
[NSPredicate predicateWithFormat:@"ALL {6, 5, 4, 3} != $featureIdentifier"];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT $featureIdentifier IN {6, 5, 4, 3}"];
|
NSPredicate *predicateAfter =
|
||||||
|
[NSPredicate predicateWithFormat:@"NOT $featureIdentifier IN {6, 5, 4, 3}"];
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicateAfter);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:expected]
|
||||||
mustRoundTrip:YES];
|
mustRoundTrip:YES];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY {6, 5, 4, 3} != $featureIdentifier"];
|
NSPredicate *predicate =
|
||||||
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException);
|
[NSPredicate predicateWithFormat:@"ANY {6, 5, 4, 3} != $featureIdentifier"];
|
||||||
|
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException,
|
||||||
|
NSInvalidArgumentException);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY {6, 5, 4, 3} > $featureIdentifier"];
|
NSPredicate *predicate =
|
||||||
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException);
|
[NSPredicate predicateWithFormat:@"ANY {6, 5, 4, 3} > $featureIdentifier"];
|
||||||
|
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException,
|
||||||
|
NSInvalidArgumentException);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NONE {6, 5, 4, 3} != $featureIdentifier"];
|
NSPredicate *predicate =
|
||||||
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException);
|
[NSPredicate predicateWithFormat:@"NONE {6, 5, 4, 3} != $featureIdentifier"];
|
||||||
|
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException,
|
||||||
|
NSInvalidArgumentException);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ALL {6, 5, 4, 3} = $featureIdentifier"];
|
NSPredicate *predicate =
|
||||||
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException);
|
[NSPredicate predicateWithFormat:@"ALL {6, 5, 4, 3} = $featureIdentifier"];
|
||||||
|
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException,
|
||||||
|
NSInvalidArgumentException);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ALL {6, 5, 4, 3} < $featureIdentifier"];
|
NSPredicate *predicate =
|
||||||
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException);
|
[NSPredicate predicateWithFormat:@"ALL {6, 5, 4, 3} < $featureIdentifier"];
|
||||||
|
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException,
|
||||||
|
NSInvalidArgumentException);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *expected = @[
|
NSArray *expected = @[
|
||||||
@"within",
|
@"within",
|
||||||
@{
|
@{
|
||||||
@"type": @"Polygon",
|
@"type" : @"Polygon",
|
||||||
@"coordinates": @[
|
@"coordinates" : @[
|
||||||
@[
|
@[
|
||||||
@[@0, @0],
|
@[ @0, @0 ],
|
||||||
@[@0, @1],
|
@[ @0, @1 ],
|
||||||
@[@1, @1],
|
@[ @1, @1 ],
|
||||||
@[@1, @0],
|
@[ @1, @0 ],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {
|
||||||
{ .latitude = 0, .longitude = 0 },
|
{.latitude = 0, .longitude = 0},
|
||||||
{ .latitude = 1, .longitude = 0 },
|
{.latitude = 1, .longitude = 0},
|
||||||
{ .latitude = 1, .longitude = 1 },
|
{.latitude = 1, .longitude = 1},
|
||||||
{ .latitude = 0, .longitude = 1 },
|
{.latitude = 0, .longitude = 1},
|
||||||
};
|
};
|
||||||
MLNPolygon *shape = [MLNPolygon polygonWithCoordinates:coordinates count:sizeof(coordinates) / sizeof(coordinates[0])];
|
MLNPolygon *shape =
|
||||||
|
[MLNPolygon polygonWithCoordinates:coordinates
|
||||||
|
count:sizeof(coordinates) / sizeof(coordinates[0])];
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF IN %@", shape];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF IN %@", shape];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:expected], predicate);
|
||||||
@@ -408,29 +477,31 @@
|
|||||||
NSArray *expected = @[
|
NSArray *expected = @[
|
||||||
@"within",
|
@"within",
|
||||||
@{
|
@{
|
||||||
@"type": @"Feature",
|
@"type" : @"Feature",
|
||||||
@"id": @"unit",
|
@"id" : @"unit",
|
||||||
@"properties": @{},
|
@"properties" : @{},
|
||||||
@"geometry": @{
|
@"geometry" : @{
|
||||||
@"type": @"Polygon",
|
@"type" : @"Polygon",
|
||||||
@"coordinates": @[
|
@"coordinates" : @[
|
||||||
@[
|
@[
|
||||||
@[@0, @0],
|
@[ @0, @0 ],
|
||||||
@[@0, @1],
|
@[ @0, @1 ],
|
||||||
@[@1, @1],
|
@[ @1, @1 ],
|
||||||
@[@1, @0],
|
@[ @1, @0 ],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {
|
||||||
{ .latitude = 0, .longitude = 0 },
|
{.latitude = 0, .longitude = 0},
|
||||||
{ .latitude = 1, .longitude = 0 },
|
{.latitude = 1, .longitude = 0},
|
||||||
{ .latitude = 1, .longitude = 1 },
|
{.latitude = 1, .longitude = 1},
|
||||||
{ .latitude = 0, .longitude = 1 },
|
{.latitude = 0, .longitude = 1},
|
||||||
};
|
};
|
||||||
MLNPolygonFeature *feature = [MLNPolygonFeature polygonWithCoordinates:coordinates count:sizeof(coordinates) / sizeof(coordinates[0])];
|
MLNPolygonFeature *feature =
|
||||||
|
[MLNPolygonFeature polygonWithCoordinates:coordinates
|
||||||
|
count:sizeof(coordinates) / sizeof(coordinates[0])];
|
||||||
feature.identifier = @"unit";
|
feature.identifier = @"unit";
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ CONTAINS SELF", feature];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ CONTAINS SELF", feature];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
|
||||||
@@ -444,97 +515,140 @@
|
|||||||
- (void)testComparisonPredicatesWithOptions {
|
- (void)testComparisonPredicatesWithOptions {
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a =[c] 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a =[c] 'b'"];
|
||||||
NSArray *jsonExpression = @[@"==", @[@"get", @"a"], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @YES}]];
|
NSArray *jsonExpression = @[
|
||||||
|
@"==", @[ @"get", @"a" ], @"b",
|
||||||
|
@[ @"collator",
|
||||||
|
@{@"case-sensitive" : @NO,
|
||||||
|
@"diacritic-sensitive" : @YES} ]
|
||||||
|
];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
XCTExpectFailure(@"Awaiting unit test refactoring for https://github.com/maplibre/maplibre-native/issues/331");
|
XCTExpectFailure(@"Awaiting unit test refactoring for "
|
||||||
|
@"https://github.com/maplibre/maplibre-native/issues/331");
|
||||||
#endif
|
#endif
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a =[d] 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a =[d] 'b'"];
|
||||||
NSArray *jsonExpression = @[@"==", @[@"get", @"a"], @"b", @[@"collator", @{@"case-sensitive": @YES, @"diacritic-sensitive": @NO}]];
|
NSArray *jsonExpression = @[
|
||||||
|
@"==", @[ @"get", @"a" ], @"b",
|
||||||
|
@[ @"collator",
|
||||||
|
@{@"case-sensitive" : @YES,
|
||||||
|
@"diacritic-sensitive" : @NO} ]
|
||||||
|
];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a =[cd] 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a =[cd] 'b'"];
|
||||||
NSArray *jsonExpression = @[@"==", @[@"get", @"a"], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @NO}]];
|
NSArray *jsonExpression = @[
|
||||||
|
@"==", @[ @"get", @"a" ], @"b",
|
||||||
|
@[ @"collator",
|
||||||
|
@{@"case-sensitive" : @NO,
|
||||||
|
@"diacritic-sensitive" : @NO} ]
|
||||||
|
];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a !=[cd] 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a !=[cd] 'b'"];
|
||||||
NSArray *jsonExpression = @[@"!=", @[@"get", @"a"], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @NO}]];
|
NSArray *jsonExpression = @[
|
||||||
|
@"!=", @[ @"get", @"a" ], @"b",
|
||||||
|
@[ @"collator",
|
||||||
|
@{@"case-sensitive" : @NO,
|
||||||
|
@"diacritic-sensitive" : @NO} ]
|
||||||
|
];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') <[cd] 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') <[cd] 'b'"];
|
||||||
NSArray *jsonExpression = @[@"<", @[@"to-string", @[@"get", @"a"]], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @NO}]];
|
NSArray *jsonExpression = @[
|
||||||
|
@"<", @[ @"to-string", @[ @"get", @"a" ] ], @"b",
|
||||||
|
@[ @"collator",
|
||||||
|
@{@"case-sensitive" : @NO,
|
||||||
|
@"diacritic-sensitive" : @NO} ]
|
||||||
|
];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') <=[cd] 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') <=[cd] 'b'"];
|
||||||
NSArray *jsonExpression = @[@"<=", @[@"to-string", @[@"get", @"a"]], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @NO}]];
|
NSArray *jsonExpression = @[
|
||||||
|
@"<=", @[ @"to-string", @[ @"get", @"a" ] ], @"b",
|
||||||
|
@[ @"collator",
|
||||||
|
@{@"case-sensitive" : @NO,
|
||||||
|
@"diacritic-sensitive" : @NO} ]
|
||||||
|
];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') >[cd] 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') >[cd] 'b'"];
|
||||||
NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"a"]], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @NO}]];
|
NSArray *jsonExpression = @[
|
||||||
|
@">", @[ @"to-string", @[ @"get", @"a" ] ], @"b",
|
||||||
|
@[ @"collator",
|
||||||
|
@{@"case-sensitive" : @NO,
|
||||||
|
@"diacritic-sensitive" : @NO} ]
|
||||||
|
];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') >=[cd] 'b'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') >=[cd] 'b'"];
|
||||||
NSArray *jsonExpression = @[@">=", @[@"to-string", @[@"get", @"a"]], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @NO}]];
|
NSArray *jsonExpression = @[
|
||||||
|
@">=", @[ @"to-string", @[ @"get", @"a" ] ], @"b",
|
||||||
|
@[ @"collator",
|
||||||
|
@{@"case-sensitive" : @NO,
|
||||||
|
@"diacritic-sensitive" : @NO} ]
|
||||||
|
];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"TRUE = MLN_FUNCTION('==', a, 'b', MLN_FUNCTION('collator', %@))", @{
|
NSPredicate *predicate = [NSPredicate
|
||||||
@"case-sensitive": @NO,
|
predicateWithFormat:@"TRUE = MLN_FUNCTION('==', a, 'b', MLN_FUNCTION('collator', %@))", @{
|
||||||
@"diacritic-sensitive": @NO,
|
@"case-sensitive" : @NO,
|
||||||
@"locale": @"tlh",
|
@"diacritic-sensitive" : @NO,
|
||||||
|
@"locale" : @"tlh",
|
||||||
}];
|
}];
|
||||||
NSArray *jsonExpression = @[@"==", @[@"get", @"a"], @"b",
|
NSArray *jsonExpression = @[
|
||||||
@[@"collator",
|
@"==", @[ @"get", @"a" ], @"b",
|
||||||
@{@"case-sensitive": @NO,
|
@[ @"collator",
|
||||||
@"diacritic-sensitive": @NO,
|
@{@"case-sensitive" : @NO,
|
||||||
@"locale": @"tlh"}]];
|
@"diacritic-sensitive" : @NO,
|
||||||
|
@"locale" : @"tlh"} ]
|
||||||
|
];
|
||||||
XCTAssertEqualObjects([predicate.mgl_jsonExpressionObject lastObject], jsonExpression);
|
XCTAssertEqualObjects([predicate.mgl_jsonExpressionObject lastObject], jsonExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/mapbox/mapbox-gl-js/issues/9339
|
// https://github.com/mapbox/mapbox-gl-js/issues/9339
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY {6, 5, 4, 3} =[c] $featureIdentifier"];
|
NSPredicate *predicate =
|
||||||
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException);
|
[NSPredicate predicateWithFormat:@"ANY {6, 5, 4, 3} =[c] $featureIdentifier"];
|
||||||
|
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException,
|
||||||
|
NSInvalidArgumentException);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NONE {6, 5, 4, 3} =[d] $featureIdentifier"];
|
NSPredicate *predicate =
|
||||||
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException);
|
[NSPredicate predicateWithFormat:@"NONE {6, 5, 4, 3} =[d] $featureIdentifier"];
|
||||||
|
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException,
|
||||||
|
NSInvalidArgumentException);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ALL {6, 5, 4, 3} !=[cd] $featureIdentifier"];
|
NSPredicate *predicate =
|
||||||
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException);
|
[NSPredicate predicateWithFormat:@"ALL {6, 5, 4, 3} !=[cd] $featureIdentifier"];
|
||||||
|
XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException,
|
||||||
|
NSInvalidArgumentException);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testCompoundPredicates {
|
- (void)testCompoundPredicates {
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"];
|
||||||
NSArray *jsonExpression = @[@"all", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]];
|
NSArray *jsonExpression =
|
||||||
|
@[ @"all", @[ @"==", @[ @"get", @"a" ], @"b" ], @[ @"==", @[ @"get", @"c" ], @"d" ] ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
@@ -542,7 +656,8 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"];
|
||||||
NSArray *jsonExpression = @[@"any", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]];
|
NSArray *jsonExpression =
|
||||||
|
@[ @"any", @[ @"==", @[ @"get", @"a" ], @"b" ], @[ @"==", @[ @"get", @"c" ], @"d" ] ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
@@ -550,7 +665,9 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' AND c == 'd')"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' AND c == 'd')"];
|
||||||
NSArray *jsonExpression = @[@"!", @[@"all", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]];
|
NSArray *jsonExpression = @[
|
||||||
|
@"!", @[ @"all", @[ @"==", @[ @"get", @"a" ], @"b" ], @[ @"==", @[ @"get", @"c" ], @"d" ] ]
|
||||||
|
];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
@@ -558,7 +675,9 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"];
|
||||||
NSArray *jsonExpression = @[@"!", @[@"any", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]];
|
NSArray *jsonExpression = @[
|
||||||
|
@"!", @[ @"any", @[ @"==", @[ @"get", @"a" ], @"b" ], @[ @"==", @[ @"get", @"c" ], @"d" ] ]
|
||||||
|
];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
XCTAssertEqualObjects([NSPredicate predicateWithMLNJSONObject:jsonExpression], predicate);
|
||||||
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
[self testSymmetryWithPredicate:[NSPredicate predicateWithMLNJSONObject:jsonExpression]
|
||||||
@@ -566,27 +685,27 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT a == nil"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT a == nil"];
|
||||||
NSArray *jsonExpression = @[@"!", @[@"==", @[@"get", @"a"], [NSNull null]]];
|
NSArray *jsonExpression = @[ @"!", @[ @"==", @[ @"get", @"a" ], [NSNull null] ] ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT a != nil"];
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT a != nil"];
|
||||||
NSArray *jsonExpression = @[@"!", @[@"!=", @[@"get", @"a"], [NSNull null]]];
|
NSArray *jsonExpression = @[ @"!", @[ @"!=", @[ @"get", @"a" ], [NSNull null] ] ];
|
||||||
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
|
||||||
[self testSymmetryWithPredicate:predicate
|
[self testSymmetryWithPredicate:predicate mustRoundTrip:NO];
|
||||||
mustRoundTrip:NO];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testSymmetryWithPredicate:(NSPredicate *)forwardPredicate mustRoundTrip:(BOOL)mustRoundTrip {
|
- (void)testSymmetryWithPredicate:(NSPredicate *)forwardPredicate
|
||||||
|
mustRoundTrip:(BOOL)mustRoundTrip {
|
||||||
auto forwardFilter = forwardPredicate.mgl_filter;
|
auto forwardFilter = forwardPredicate.mgl_filter;
|
||||||
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
|
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
|
||||||
if (mustRoundTrip) {
|
if (mustRoundTrip) {
|
||||||
// A collection of ints may turn into an aggregate of longs, for
|
// A collection of ints may turn into an aggregate of longs, for
|
||||||
// example, so compare formats instead of the predicates themselves.
|
// example, so compare formats instead of the predicates themselves.
|
||||||
XCTAssertEqualObjects(forwardPredicate.predicateFormat.stringByRemovingPointerAddresses, forwardPredicateAfter.predicateFormat.stringByRemovingPointerAddresses);
|
XCTAssertEqualObjects(forwardPredicate.predicateFormat.stringByRemovingPointerAddresses,
|
||||||
|
forwardPredicateAfter.predicateFormat.stringByRemovingPointerAddresses);
|
||||||
} else {
|
} else {
|
||||||
XCTAssertEqualObjects(forwardPredicate, forwardPredicateAfter);
|
XCTAssertEqualObjects(forwardPredicate, forwardPredicateAfter);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
#import <mbgl/storage/resource.hpp>
|
#import <mbgl/storage/resource.hpp>
|
||||||
|
|
||||||
namespace mbgl {
|
namespace mbgl {
|
||||||
extern NSURL *resourceURL(const Resource& resource);
|
extern NSURL *resourceURL(const Resource &resource);
|
||||||
extern BOOL isValidMapboxEndpoint(NSURL *url);
|
extern BOOL isValidMapboxEndpoint(NSURL *url);
|
||||||
}
|
} // namespace mbgl
|
||||||
|
|
||||||
@interface MLNResourceTests : XCTestCase
|
@interface MLNResourceTests : XCTestCase
|
||||||
@end
|
@end
|
||||||
@@ -27,13 +27,13 @@ namespace mbgl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)internalTestOfflineQueryParameterIsAddedForOfflineResource:(std::string)testURL {
|
- (void)internalTestOfflineQueryParameterIsAddedForOfflineResource:(std::string)testURL {
|
||||||
|
|
||||||
using namespace mbgl;
|
using namespace mbgl;
|
||||||
|
|
||||||
// Is our test URL "correct" for subsequent checks?
|
// Is our test URL "correct" for subsequent checks?
|
||||||
{
|
{
|
||||||
NSURL *url = [NSURL URLWithString:@(testURL.c_str())];
|
NSURL *url = [NSURL URLWithString:@(testURL.c_str())];
|
||||||
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
|
NSURLComponents *components = [NSURLComponents componentsWithURL:url
|
||||||
|
resolvingAgainstBaseURL:NO];
|
||||||
NSArray<NSURLQueryItem *> *items = components.queryItems;
|
NSArray<NSURLQueryItem *> *items = components.queryItems;
|
||||||
XCTAssert(items.count == 2);
|
XCTAssert(items.count == 2);
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,8 @@ namespace mbgl {
|
|||||||
|
|
||||||
{
|
{
|
||||||
NSURL *url = resourceURL(resource);
|
NSURL *url = resourceURL(resource);
|
||||||
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
|
NSURLComponents *components = [NSURLComponents componentsWithURL:url
|
||||||
|
resolvingAgainstBaseURL:NO];
|
||||||
|
|
||||||
// For offline, we expect a single offline query item
|
// For offline, we expect a single offline query item
|
||||||
NSInteger foundCount = 0;
|
NSInteger foundCount = 0;
|
||||||
|
|||||||
@@ -13,19 +13,26 @@
|
|||||||
@implementation MLNShapeSourceTests
|
@implementation MLNShapeSourceTests
|
||||||
|
|
||||||
- (void)testGeoJSONOptionsFromDictionary {
|
- (void)testGeoJSONOptionsFromDictionary {
|
||||||
NSExpression *reduceExpression = [NSExpression expressionForFunction:@"sum:" arguments:@[[NSExpression expressionForKeyPath:@"featureAccumulated"], [NSExpression expressionForKeyPath:@"sumValue"]]];
|
NSExpression *reduceExpression =
|
||||||
|
[NSExpression expressionForFunction:@"sum:"
|
||||||
|
arguments:@[
|
||||||
|
[NSExpression expressionForKeyPath:@"featureAccumulated"],
|
||||||
|
[NSExpression expressionForKeyPath:@"sumValue"]
|
||||||
|
]];
|
||||||
NSExpression *mapExpression = [NSExpression expressionForKeyPath:@"mag"];
|
NSExpression *mapExpression = [NSExpression expressionForKeyPath:@"mag"];
|
||||||
NSArray *clusterPropertyArray = @[reduceExpression, mapExpression];
|
NSArray *clusterPropertyArray = @[ reduceExpression, mapExpression ];
|
||||||
NSDictionary *options = @{MLNShapeSourceOptionClustered: @YES,
|
NSDictionary *options = @{
|
||||||
MLNShapeSourceOptionClusterRadius: @42,
|
MLNShapeSourceOptionClustered : @YES,
|
||||||
MLNShapeSourceOptionClusterMinPoints: @3,
|
MLNShapeSourceOptionClusterRadius : @42,
|
||||||
MLNShapeSourceOptionClusterProperties: @{@"sumValue": clusterPropertyArray},
|
MLNShapeSourceOptionClusterMinPoints : @3,
|
||||||
MLNShapeSourceOptionMaximumZoomLevelForClustering: @98,
|
MLNShapeSourceOptionClusterProperties : @{@"sumValue" : clusterPropertyArray},
|
||||||
MLNShapeSourceOptionMaximumZoomLevel: @99,
|
MLNShapeSourceOptionMaximumZoomLevelForClustering : @98,
|
||||||
MLNShapeSourceOptionBuffer: @1976,
|
MLNShapeSourceOptionMaximumZoomLevel : @99,
|
||||||
MLNShapeSourceOptionSimplificationTolerance: @0.42,
|
MLNShapeSourceOptionBuffer : @1976,
|
||||||
MLNShapeSourceOptionLineDistanceMetrics: @YES,
|
MLNShapeSourceOptionSimplificationTolerance : @0.42,
|
||||||
MLNShapeSourceOptionSynchronousUpdate: @YES};
|
MLNShapeSourceOptionLineDistanceMetrics : @YES,
|
||||||
|
MLNShapeSourceOptionSynchronousUpdate : @YES
|
||||||
|
};
|
||||||
|
|
||||||
auto mbglOptions = MLNGeoJSONOptionsFromDictionary(options);
|
auto mbglOptions = MLNGeoJSONOptionsFromDictionary(options);
|
||||||
XCTAssertTrue(mbglOptions->cluster);
|
XCTAssertTrue(mbglOptions->cluster);
|
||||||
@@ -39,7 +46,7 @@
|
|||||||
XCTAssertTrue(!mbglOptions->clusterProperties.empty());
|
XCTAssertTrue(!mbglOptions->clusterProperties.empty());
|
||||||
XCTAssertTrue(mbglOptions->synchronousUpdate);
|
XCTAssertTrue(mbglOptions->synchronousUpdate);
|
||||||
|
|
||||||
options = @{MLNShapeSourceOptionClustered: @"number 1"};
|
options = @{MLNShapeSourceOptionClustered : @"number 1"};
|
||||||
XCTAssertThrows(MLNGeoJSONOptionsFromDictionary(options));
|
XCTAssertThrows(MLNGeoJSONOptionsFromDictionary(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,10 +57,12 @@
|
|||||||
|
|
||||||
- (void)testUnclusterableShape {
|
- (void)testUnclusterableShape {
|
||||||
NSDictionary *options = @{
|
NSDictionary *options = @{
|
||||||
MLNShapeSourceOptionClustered: @YES,
|
MLNShapeSourceOptionClustered : @YES,
|
||||||
};
|
};
|
||||||
|
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"id" shape:[[MLNPointFeature alloc] init] options:options];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"id"
|
||||||
|
shape:[[MLNPointFeature alloc] init]
|
||||||
|
options:options];
|
||||||
XCTAssertTrue([source.shape isKindOfClass:[MLNPointFeature class]]);
|
XCTAssertTrue([source.shape isKindOfClass:[MLNPointFeature class]]);
|
||||||
|
|
||||||
MLNShapeCollectionFeature *feature = [MLNShapeCollectionFeature shapeCollectionWithShapes:@[]];
|
MLNShapeCollectionFeature *feature = [MLNShapeCollectionFeature shapeCollectionWithShapes:@[]];
|
||||||
@@ -62,15 +71,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMLNShapeSourceWithDataMultipleFeatures {
|
- (void)testMLNShapeSourceWithDataMultipleFeatures {
|
||||||
|
NSString *geoJSON =
|
||||||
NSString *geoJSON = @"{\"type\": \"FeatureCollection\",\"features\": [{\"type\": \"Feature\",\"properties\": {},\"geometry\": {\"type\": \"LineString\",\"coordinates\": [[-107.75390625,40.329795743702064],[-104.34814453125,37.64903402157866]]}}]}";
|
@"{\"type\": \"FeatureCollection\",\"features\": [{\"type\": \"Feature\",\"properties\": "
|
||||||
|
@"{},\"geometry\": {\"type\": \"LineString\",\"coordinates\": "
|
||||||
|
@"[[-107.75390625,40.329795743702064],[-104.34814453125,37.64903402157866]]}}]}";
|
||||||
|
|
||||||
NSData *data = [geoJSON dataUsingEncoding:NSUTF8StringEncoding];
|
NSData *data = [geoJSON dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
NSError *error;
|
NSError *error;
|
||||||
MLNShape *shape = [MLNShape shapeWithData:data encoding:NSUTF8StringEncoding error:&error];
|
MLNShape *shape = [MLNShape shapeWithData:data encoding:NSUTF8StringEncoding error:&error];
|
||||||
XCTAssertNil(error);
|
XCTAssertNil(error);
|
||||||
XCTAssertNotNil(shape);
|
XCTAssertNotNil(shape);
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id" shape:shape options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id"
|
||||||
|
shape:shape
|
||||||
|
options:nil];
|
||||||
|
|
||||||
MLNShapeCollection *collection = (MLNShapeCollection *)source.shape;
|
MLNShapeCollection *collection = (MLNShapeCollection *)source.shape;
|
||||||
XCTAssertNotNil(collection);
|
XCTAssertNotNil(collection);
|
||||||
@@ -79,24 +92,31 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMLNShapeSourceWithSingleGeometry {
|
- (void)testMLNShapeSourceWithSingleGeometry {
|
||||||
NSData *data = [@"{\"type\": \"Point\", \"coordinates\": [0, 0]}" dataUsingEncoding:NSUTF8StringEncoding];
|
NSData *data =
|
||||||
|
[@"{\"type\": \"Point\", \"coordinates\": [0, 0]}" dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
NSError *error;
|
NSError *error;
|
||||||
MLNShape *shape = [MLNShape shapeWithData:data encoding:NSUTF8StringEncoding error:&error];
|
MLNShape *shape = [MLNShape shapeWithData:data encoding:NSUTF8StringEncoding error:&error];
|
||||||
XCTAssertNil(error);
|
XCTAssertNil(error);
|
||||||
XCTAssertNotNil(shape);
|
XCTAssertNotNil(shape);
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"geojson" shape:shape options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"geojson"
|
||||||
|
shape:shape
|
||||||
|
options:nil];
|
||||||
XCTAssertNotNil(source.shape);
|
XCTAssertNotNil(source.shape);
|
||||||
XCTAssert([source.shape isKindOfClass:[MLNPointAnnotation class]]);
|
XCTAssert([source.shape isKindOfClass:[MLNPointAnnotation class]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMLNGeoJSONSourceWithSingleFeature {
|
- (void)testMLNGeoJSONSourceWithSingleFeature {
|
||||||
NSString *geoJSON = @"{\"type\": \"Feature\", \"properties\": {\"color\": \"green\"}, \"geometry\": { \"type\": \"Point\", \"coordinates\": [ -114.06847000122069, 51.050459433092655 ] }}";
|
NSString *geoJSON =
|
||||||
|
@"{\"type\": \"Feature\", \"properties\": {\"color\": \"green\"}, \"geometry\": { \"type\": "
|
||||||
|
@"\"Point\", \"coordinates\": [ -114.06847000122069, 51.050459433092655 ] }}";
|
||||||
NSData *data = [geoJSON dataUsingEncoding:NSUTF8StringEncoding];
|
NSData *data = [geoJSON dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
NSError *error;
|
NSError *error;
|
||||||
MLNShape *shape = [MLNShape shapeWithData:data encoding:NSUTF8StringEncoding error:&error];
|
MLNShape *shape = [MLNShape shapeWithData:data encoding:NSUTF8StringEncoding error:&error];
|
||||||
XCTAssertNil(error);
|
XCTAssertNil(error);
|
||||||
XCTAssertNotNil(shape);
|
XCTAssertNotNil(shape);
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"geojson" shape:shape options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"geojson"
|
||||||
|
shape:shape
|
||||||
|
options:nil];
|
||||||
XCTAssertNotNil(source.shape);
|
XCTAssertNotNil(source.shape);
|
||||||
XCTAssert([source.shape isKindOfClass:[MLNPointFeature class]]);
|
XCTAssert([source.shape isKindOfClass:[MLNPointFeature class]]);
|
||||||
MLNPointFeature *feature = (MLNPointFeature *)source.shape;
|
MLNPointFeature *feature = (MLNPointFeature *)source.shape;
|
||||||
@@ -104,10 +124,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMLNShapeSourceWithPolylineFeatures {
|
- (void)testMLNShapeSourceWithPolylineFeatures {
|
||||||
CLLocationCoordinate2D coordinates[] = { CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10)};
|
CLLocationCoordinate2D coordinates[] = {CLLocationCoordinate2DMake(0, 0),
|
||||||
MLNPolylineFeature *polylineFeature = [MLNPolylineFeature polylineWithCoordinates:coordinates count:2];
|
CLLocationCoordinate2DMake(10, 10)};
|
||||||
|
MLNPolylineFeature *polylineFeature = [MLNPolylineFeature polylineWithCoordinates:coordinates
|
||||||
|
count:2];
|
||||||
|
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id" shape:polylineFeature options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id"
|
||||||
|
shape:polylineFeature
|
||||||
|
options:nil];
|
||||||
|
|
||||||
XCTAssertNotNil(source.shape);
|
XCTAssertNotNil(source.shape);
|
||||||
XCTAssertTrue([source.shape isMemberOfClass:[MLNPolylineFeature class]]);
|
XCTAssertTrue([source.shape isMemberOfClass:[MLNPolylineFeature class]]);
|
||||||
@@ -115,35 +139,40 @@
|
|||||||
|
|
||||||
- (void)testMLNShapeSourceWithPolygonFeatures {
|
- (void)testMLNShapeSourceWithPolygonFeatures {
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0),
|
CLLocationCoordinate2DMake(0.0, 100.0), CLLocationCoordinate2DMake(0.0, 101.0),
|
||||||
CLLocationCoordinate2DMake(0.0, 101.0),
|
CLLocationCoordinate2DMake(1.0, 101.0), CLLocationCoordinate2DMake(1.0, 100.0),
|
||||||
CLLocationCoordinate2DMake(1.0, 101.0),
|
|
||||||
CLLocationCoordinate2DMake(1.0, 100.0),
|
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0)};
|
CLLocationCoordinate2DMake(0.0, 100.0)};
|
||||||
|
|
||||||
MLNPolygonFeature *polygonFeature = [MLNPolygonFeature polygonWithCoordinates:coordinates count:5];
|
MLNPolygonFeature *polygonFeature = [MLNPolygonFeature polygonWithCoordinates:coordinates
|
||||||
|
count:5];
|
||||||
polygonFeature.identifier = @"feature-id";
|
polygonFeature.identifier = @"feature-id";
|
||||||
NSString *stringAttribute = @"string";
|
NSString *stringAttribute = @"string";
|
||||||
NSNumber *boolAttribute = [NSNumber numberWithBool:YES];
|
NSNumber *boolAttribute = [NSNumber numberWithBool:YES];
|
||||||
NSNumber *doubleAttribute = [NSNumber numberWithDouble:1.23];
|
NSNumber *doubleAttribute = [NSNumber numberWithDouble:1.23];
|
||||||
NSDictionary *nestedDictionaryValue = @{@"nested-key-1": @"nested-string-value"};
|
NSDictionary *nestedDictionaryValue = @{@"nested-key-1" : @"nested-string-value"};
|
||||||
NSArray *arrayValue = @[@"string-value", @2];
|
NSArray *arrayValue = @[ @"string-value", @2 ];
|
||||||
NSDictionary *dictionaryValue = @{@"key-1": @"string-value",
|
NSDictionary *dictionaryValue = @{
|
||||||
@"key-2": @1,
|
@"key-1" : @"string-value",
|
||||||
@"key-3": nestedDictionaryValue,
|
@"key-2" : @1,
|
||||||
@"key-4": arrayValue};
|
@"key-3" : nestedDictionaryValue,
|
||||||
NSArray *arrayOfArrays = @[@[@1, @"string-value", @[@"jagged"]]];
|
@"key-4" : arrayValue
|
||||||
NSArray *arrayOfDictionaries = @[@{@"key": @"value"}];
|
};
|
||||||
|
NSArray *arrayOfArrays = @[ @[ @1, @"string-value", @[ @"jagged" ] ] ];
|
||||||
|
NSArray *arrayOfDictionaries = @[ @{@"key" : @"value"} ];
|
||||||
|
|
||||||
polygonFeature.attributes = @{@"name": stringAttribute,
|
polygonFeature.attributes = @{
|
||||||
@"bool": boolAttribute,
|
@"name" : stringAttribute,
|
||||||
@"double": doubleAttribute,
|
@"bool" : boolAttribute,
|
||||||
@"dictionary-attribute": dictionaryValue,
|
@"double" : doubleAttribute,
|
||||||
@"array-attribute": arrayValue,
|
@"dictionary-attribute" : dictionaryValue,
|
||||||
@"array-of-array-attribute": arrayOfArrays,
|
@"array-attribute" : arrayValue,
|
||||||
@"array-of-dictionary-attribute": arrayOfDictionaries};
|
@"array-of-array-attribute" : arrayOfArrays,
|
||||||
|
@"array-of-dictionary-attribute" : arrayOfDictionaries
|
||||||
|
};
|
||||||
|
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id" shape:polygonFeature options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id"
|
||||||
|
shape:polygonFeature
|
||||||
|
options:nil];
|
||||||
|
|
||||||
XCTAssertNotNil(source.shape);
|
XCTAssertNotNil(source.shape);
|
||||||
MLNPolygonFeature *expectedPolygonFeature = (MLNPolygonFeature *)source.shape;
|
MLNPolygonFeature *expectedPolygonFeature = (MLNPolygonFeature *)source.shape;
|
||||||
@@ -153,45 +182,55 @@
|
|||||||
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"name"], stringAttribute);
|
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"name"], stringAttribute);
|
||||||
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"bool"], boolAttribute);
|
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"bool"], boolAttribute);
|
||||||
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"double"], doubleAttribute);
|
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"double"], doubleAttribute);
|
||||||
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"dictionary-attribute"], dictionaryValue);
|
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"dictionary-attribute"],
|
||||||
|
dictionaryValue);
|
||||||
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"array-attribute"], arrayValue);
|
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"array-attribute"], arrayValue);
|
||||||
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"array-of-array-attribute"], arrayOfArrays);
|
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"array-of-array-attribute"],
|
||||||
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"array-of-dictionary-attribute"], arrayOfDictionaries);
|
arrayOfArrays);
|
||||||
|
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"array-of-dictionary-attribute"],
|
||||||
|
arrayOfDictionaries);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMLNShapeSourceWithPolygonFeaturesInculdingInteriorPolygons {
|
- (void)testMLNShapeSourceWithPolygonFeaturesInculdingInteriorPolygons {
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0),
|
CLLocationCoordinate2DMake(0.0, 100.0), CLLocationCoordinate2DMake(0.0, 101.0),
|
||||||
CLLocationCoordinate2DMake(0.0, 101.0),
|
CLLocationCoordinate2DMake(1.0, 101.0), CLLocationCoordinate2DMake(1.0, 100.0),
|
||||||
CLLocationCoordinate2DMake(1.0, 101.0),
|
|
||||||
CLLocationCoordinate2DMake(1.0, 100.0),
|
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0)};
|
CLLocationCoordinate2DMake(0.0, 100.0)};
|
||||||
|
|
||||||
CLLocationCoordinate2D interiorCoordinates[] = {
|
CLLocationCoordinate2D interiorCoordinates[] = {
|
||||||
CLLocationCoordinate2DMake(0.2, 100.2),
|
CLLocationCoordinate2DMake(0.2, 100.2), CLLocationCoordinate2DMake(0.2, 100.8),
|
||||||
CLLocationCoordinate2DMake(0.2, 100.8),
|
CLLocationCoordinate2DMake(0.8, 100.8), CLLocationCoordinate2DMake(0.8, 100.2),
|
||||||
CLLocationCoordinate2DMake(0.8, 100.8),
|
|
||||||
CLLocationCoordinate2DMake(0.8, 100.2),
|
|
||||||
CLLocationCoordinate2DMake(0.2, 100.2)};
|
CLLocationCoordinate2DMake(0.2, 100.2)};
|
||||||
|
|
||||||
MLNPolygon *polygon = [MLNPolygon polygonWithCoordinates:interiorCoordinates count:5];
|
MLNPolygon *polygon = [MLNPolygon polygonWithCoordinates:interiorCoordinates count:5];
|
||||||
|
|
||||||
MLNPolygonFeature *polygonFeature = [MLNPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]];
|
MLNPolygonFeature *polygonFeature = [MLNPolygonFeature polygonWithCoordinates:coordinates
|
||||||
|
count:5
|
||||||
|
interiorPolygons:@[ polygon ]];
|
||||||
|
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id" shape:polygonFeature options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id"
|
||||||
|
shape:polygonFeature
|
||||||
|
options:nil];
|
||||||
|
|
||||||
XCTAssertNotNil(source.shape);
|
XCTAssertNotNil(source.shape);
|
||||||
XCTAssertTrue([source.shape isMemberOfClass:[MLNPolygonFeature class]]);
|
XCTAssertTrue([source.shape isMemberOfClass:[MLNPolygonFeature class]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMLNShapeSourceWithMultiPolylineFeatures {
|
- (void)testMLNShapeSourceWithMultiPolylineFeatures {
|
||||||
CLLocationCoordinate2D firstCoordinates[] = { CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10)};
|
CLLocationCoordinate2D firstCoordinates[] = {CLLocationCoordinate2DMake(0, 0),
|
||||||
MLNPolylineFeature *firstPolylineFeature = [MLNPolylineFeature polylineWithCoordinates:firstCoordinates count:2];
|
CLLocationCoordinate2DMake(10, 10)};
|
||||||
CLLocationCoordinate2D secondCoordinates[] = { CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10)};
|
MLNPolylineFeature *firstPolylineFeature =
|
||||||
MLNPolylineFeature *secondPolylineFeature = [MLNPolylineFeature polylineWithCoordinates:secondCoordinates count:2];
|
[MLNPolylineFeature polylineWithCoordinates:firstCoordinates count:2];
|
||||||
MLNMultiPolylineFeature *multiPolylineFeature = [MLNMultiPolylineFeature multiPolylineWithPolylines:@[firstPolylineFeature, secondPolylineFeature]];
|
CLLocationCoordinate2D secondCoordinates[] = {CLLocationCoordinate2DMake(0, 0),
|
||||||
|
CLLocationCoordinate2DMake(10, 10)};
|
||||||
|
MLNPolylineFeature *secondPolylineFeature =
|
||||||
|
[MLNPolylineFeature polylineWithCoordinates:secondCoordinates count:2];
|
||||||
|
MLNMultiPolylineFeature *multiPolylineFeature = [MLNMultiPolylineFeature
|
||||||
|
multiPolylineWithPolylines:@[ firstPolylineFeature, secondPolylineFeature ]];
|
||||||
|
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id" shape:multiPolylineFeature options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id"
|
||||||
|
shape:multiPolylineFeature
|
||||||
|
options:nil];
|
||||||
|
|
||||||
XCTAssertNotNil(source.shape);
|
XCTAssertNotNil(source.shape);
|
||||||
XCTAssertTrue([source.shape isMemberOfClass:[MLNMultiPolylineFeature class]]);
|
XCTAssertTrue([source.shape isMemberOfClass:[MLNMultiPolylineFeature class]]);
|
||||||
@@ -199,27 +238,30 @@
|
|||||||
|
|
||||||
- (void)testMLNShapeSourceWithMultiPolygonFeatures {
|
- (void)testMLNShapeSourceWithMultiPolygonFeatures {
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0),
|
CLLocationCoordinate2DMake(0.0, 100.0), CLLocationCoordinate2DMake(0.0, 101.0),
|
||||||
CLLocationCoordinate2DMake(0.0, 101.0),
|
CLLocationCoordinate2DMake(1.0, 101.0), CLLocationCoordinate2DMake(1.0, 100.0),
|
||||||
CLLocationCoordinate2DMake(1.0, 101.0),
|
|
||||||
CLLocationCoordinate2DMake(1.0, 100.0),
|
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0)};
|
CLLocationCoordinate2DMake(0.0, 100.0)};
|
||||||
|
|
||||||
CLLocationCoordinate2D interiorCoordinates[] = {
|
CLLocationCoordinate2D interiorCoordinates[] = {
|
||||||
CLLocationCoordinate2DMake(0.2, 100.2),
|
CLLocationCoordinate2DMake(0.2, 100.2), CLLocationCoordinate2DMake(0.2, 100.8),
|
||||||
CLLocationCoordinate2DMake(0.2, 100.8),
|
CLLocationCoordinate2DMake(0.8, 100.8), CLLocationCoordinate2DMake(0.8, 100.2),
|
||||||
CLLocationCoordinate2DMake(0.8, 100.8),
|
|
||||||
CLLocationCoordinate2DMake(0.8, 100.2),
|
|
||||||
CLLocationCoordinate2DMake(0.2, 100.2)};
|
CLLocationCoordinate2DMake(0.2, 100.2)};
|
||||||
|
|
||||||
MLNPolygon *polygon = [MLNPolygon polygonWithCoordinates:interiorCoordinates count:5];
|
MLNPolygon *polygon = [MLNPolygon polygonWithCoordinates:interiorCoordinates count:5];
|
||||||
|
|
||||||
MLNPolygonFeature *firstPolygon = [MLNPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]];
|
MLNPolygonFeature *firstPolygon = [MLNPolygonFeature polygonWithCoordinates:coordinates
|
||||||
MLNPolygonFeature *secondPolygon = [MLNPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]];
|
count:5
|
||||||
|
interiorPolygons:@[ polygon ]];
|
||||||
|
MLNPolygonFeature *secondPolygon = [MLNPolygonFeature polygonWithCoordinates:coordinates
|
||||||
|
count:5
|
||||||
|
interiorPolygons:@[ polygon ]];
|
||||||
|
|
||||||
MLNMultiPolygonFeature *multiPolygonFeature = [MLNMultiPolygonFeature multiPolygonWithPolygons:@[firstPolygon, secondPolygon]];
|
MLNMultiPolygonFeature *multiPolygonFeature =
|
||||||
|
[MLNMultiPolygonFeature multiPolygonWithPolygons:@[ firstPolygon, secondPolygon ]];
|
||||||
|
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id" shape:multiPolygonFeature options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id"
|
||||||
|
shape:multiPolygonFeature
|
||||||
|
options:nil];
|
||||||
|
|
||||||
XCTAssertNotNil(source.shape);
|
XCTAssertNotNil(source.shape);
|
||||||
XCTAssertTrue([source.shape isMemberOfClass:[MLNMultiPolygonFeature class]]);
|
XCTAssertTrue([source.shape isMemberOfClass:[MLNMultiPolygonFeature class]]);
|
||||||
@@ -229,7 +271,9 @@
|
|||||||
MLNPointFeature *pointFeature = [MLNPointFeature new];
|
MLNPointFeature *pointFeature = [MLNPointFeature new];
|
||||||
pointFeature.coordinate = CLLocationCoordinate2DMake(0.2, 100.2);
|
pointFeature.coordinate = CLLocationCoordinate2DMake(0.2, 100.2);
|
||||||
|
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"souce-id" shape:pointFeature options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"souce-id"
|
||||||
|
shape:pointFeature
|
||||||
|
options:nil];
|
||||||
|
|
||||||
XCTAssertNotNil(source.shape);
|
XCTAssertNotNil(source.shape);
|
||||||
XCTAssertTrue([source.shape isMemberOfClass:[MLNPointFeature class]]);
|
XCTAssertTrue([source.shape isMemberOfClass:[MLNPointFeature class]]);
|
||||||
@@ -237,13 +281,14 @@
|
|||||||
|
|
||||||
- (void)testMLNShapeSourceWithPointCollectionFeature {
|
- (void)testMLNShapeSourceWithPointCollectionFeature {
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0),
|
CLLocationCoordinate2DMake(0.0, 100.0), CLLocationCoordinate2DMake(0.0, 101.0),
|
||||||
CLLocationCoordinate2DMake(0.0, 101.0),
|
CLLocationCoordinate2DMake(1.0, 101.0), CLLocationCoordinate2DMake(1.0, 100.0),
|
||||||
CLLocationCoordinate2DMake(1.0, 101.0),
|
|
||||||
CLLocationCoordinate2DMake(1.0, 100.0),
|
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0)};
|
CLLocationCoordinate2DMake(0.0, 100.0)};
|
||||||
MLNPointCollectionFeature *pointCollectionFeature = [MLNPointCollectionFeature pointCollectionWithCoordinates:coordinates count:5];
|
MLNPointCollectionFeature *pointCollectionFeature =
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"souce-id" shape:pointCollectionFeature options:nil];
|
[MLNPointCollectionFeature pointCollectionWithCoordinates:coordinates count:5];
|
||||||
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"souce-id"
|
||||||
|
shape:pointCollectionFeature
|
||||||
|
options:nil];
|
||||||
|
|
||||||
XCTAssertNotNil(source.shape);
|
XCTAssertNotNil(source.shape);
|
||||||
XCTAssertTrue([source.shape isMemberOfClass:[MLNPointCollectionFeature class]]);
|
XCTAssertTrue([source.shape isMemberOfClass:[MLNPointCollectionFeature class]]);
|
||||||
@@ -251,38 +296,47 @@
|
|||||||
|
|
||||||
- (void)testMLNShapeSourceWithShapeCollectionFeatures {
|
- (void)testMLNShapeSourceWithShapeCollectionFeatures {
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0),
|
CLLocationCoordinate2DMake(0.0, 100.0), CLLocationCoordinate2DMake(0.0, 101.0),
|
||||||
CLLocationCoordinate2DMake(0.0, 101.0),
|
CLLocationCoordinate2DMake(1.0, 101.0), CLLocationCoordinate2DMake(1.0, 100.0),
|
||||||
CLLocationCoordinate2DMake(1.0, 101.0),
|
|
||||||
CLLocationCoordinate2DMake(1.0, 100.0),
|
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0)};
|
CLLocationCoordinate2DMake(0.0, 100.0)};
|
||||||
|
|
||||||
CLLocationCoordinate2D interiorCoordinates[] = {
|
CLLocationCoordinate2D interiorCoordinates[] = {
|
||||||
CLLocationCoordinate2DMake(0.2, 100.2),
|
CLLocationCoordinate2DMake(0.2, 100.2), CLLocationCoordinate2DMake(0.2, 100.8),
|
||||||
CLLocationCoordinate2DMake(0.2, 100.8),
|
CLLocationCoordinate2DMake(0.8, 100.8), CLLocationCoordinate2DMake(0.8, 100.2),
|
||||||
CLLocationCoordinate2DMake(0.8, 100.8),
|
|
||||||
CLLocationCoordinate2DMake(0.8, 100.2),
|
|
||||||
CLLocationCoordinate2DMake(0.2, 100.2)};
|
CLLocationCoordinate2DMake(0.2, 100.2)};
|
||||||
|
|
||||||
MLNPolygon *polygon = [MLNPolygon polygonWithCoordinates:interiorCoordinates count:5];
|
MLNPolygon *polygon = [MLNPolygon polygonWithCoordinates:interiorCoordinates count:5];
|
||||||
|
|
||||||
MLNPolygonFeature *polygonFeature = [MLNPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]];
|
MLNPolygonFeature *polygonFeature = [MLNPolygonFeature polygonWithCoordinates:coordinates
|
||||||
|
count:5
|
||||||
|
interiorPolygons:@[ polygon ]];
|
||||||
|
|
||||||
CLLocationCoordinate2D coordinates_2[] = { CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10)};
|
CLLocationCoordinate2D coordinates_2[] = {CLLocationCoordinate2DMake(0, 0),
|
||||||
MLNPolylineFeature *polylineFeature = [MLNPolylineFeature polylineWithCoordinates:coordinates_2 count:2];
|
CLLocationCoordinate2DMake(10, 10)};
|
||||||
|
MLNPolylineFeature *polylineFeature = [MLNPolylineFeature polylineWithCoordinates:coordinates_2
|
||||||
|
count:2];
|
||||||
|
|
||||||
MLNMultiPolygonFeature *multiPolygonFeature = [MLNMultiPolygonFeature multiPolygonWithPolygons:@[polygonFeature, polygonFeature]];
|
MLNMultiPolygonFeature *multiPolygonFeature =
|
||||||
|
[MLNMultiPolygonFeature multiPolygonWithPolygons:@[ polygonFeature, polygonFeature ]];
|
||||||
|
|
||||||
MLNMultiPolylineFeature *multiPolylineFeature = [MLNMultiPolylineFeature multiPolylineWithPolylines:@[polylineFeature, polylineFeature]];
|
MLNMultiPolylineFeature *multiPolylineFeature =
|
||||||
|
[MLNMultiPolylineFeature multiPolylineWithPolylines:@[ polylineFeature, polylineFeature ]];
|
||||||
|
|
||||||
MLNPointCollectionFeature *pointCollectionFeature = [MLNPointCollectionFeature pointCollectionWithCoordinates:coordinates count:5];
|
MLNPointCollectionFeature *pointCollectionFeature =
|
||||||
|
[MLNPointCollectionFeature pointCollectionWithCoordinates:coordinates count:5];
|
||||||
|
|
||||||
MLNPointFeature *pointFeature = [MLNPointFeature new];
|
MLNPointFeature *pointFeature = [MLNPointFeature new];
|
||||||
pointFeature.coordinate = CLLocationCoordinate2DMake(0.2, 100.2);
|
pointFeature.coordinate = CLLocationCoordinate2DMake(0.2, 100.2);
|
||||||
|
|
||||||
MLNShapeCollectionFeature *shapeCollectionFeature = [MLNShapeCollectionFeature shapeCollectionWithShapes:@[polygonFeature, polylineFeature, multiPolygonFeature, multiPolylineFeature, pointCollectionFeature, pointFeature]];
|
MLNShapeCollectionFeature *shapeCollectionFeature =
|
||||||
|
[MLNShapeCollectionFeature shapeCollectionWithShapes:@[
|
||||||
|
polygonFeature, polylineFeature, multiPolygonFeature, multiPolylineFeature,
|
||||||
|
pointCollectionFeature, pointFeature
|
||||||
|
]];
|
||||||
|
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id" shape:shapeCollectionFeature options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id"
|
||||||
|
shape:shapeCollectionFeature
|
||||||
|
options:nil];
|
||||||
|
|
||||||
MLNShapeCollectionFeature *shape = (MLNShapeCollectionFeature *)source.shape;
|
MLNShapeCollectionFeature *shape = (MLNShapeCollectionFeature *)source.shape;
|
||||||
XCTAssertNotNil(shape);
|
XCTAssertNotNil(shape);
|
||||||
@@ -291,40 +345,54 @@
|
|||||||
|
|
||||||
- (void)testMLNShapeSourceWithFeaturesConvenienceInitializer {
|
- (void)testMLNShapeSourceWithFeaturesConvenienceInitializer {
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0),
|
CLLocationCoordinate2DMake(0.0, 100.0), CLLocationCoordinate2DMake(0.0, 101.0),
|
||||||
CLLocationCoordinate2DMake(0.0, 101.0),
|
CLLocationCoordinate2DMake(1.0, 101.0), CLLocationCoordinate2DMake(1.0, 100.0),
|
||||||
CLLocationCoordinate2DMake(1.0, 101.0),
|
|
||||||
CLLocationCoordinate2DMake(1.0, 100.0),
|
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0)};
|
CLLocationCoordinate2DMake(0.0, 100.0)};
|
||||||
|
|
||||||
MLNPolygonFeature *polygonFeature = [MLNPolygonFeature polygonWithCoordinates:coordinates count:sizeof(coordinates)/sizeof(coordinates[0]) interiorPolygons:nil];
|
MLNPolygonFeature *polygonFeature =
|
||||||
|
[MLNPolygonFeature polygonWithCoordinates:coordinates
|
||||||
|
count:sizeof(coordinates) / sizeof(coordinates[0])
|
||||||
|
interiorPolygons:nil];
|
||||||
|
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id" features:@[polygonFeature] options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id"
|
||||||
|
features:@[ polygonFeature ]
|
||||||
|
options:nil];
|
||||||
MLNShapeCollectionFeature *shape = (MLNShapeCollectionFeature *)source.shape;
|
MLNShapeCollectionFeature *shape = (MLNShapeCollectionFeature *)source.shape;
|
||||||
|
|
||||||
XCTAssertTrue([shape isKindOfClass:[MLNShapeCollectionFeature class]]);
|
XCTAssertTrue([shape isKindOfClass:[MLNShapeCollectionFeature class]]);
|
||||||
XCTAssertEqual(shape.shapes.count, 1UL, @"Shape collection should contain 1 shape");
|
XCTAssertEqual(shape.shapes.count, 1UL, @"Shape collection should contain 1 shape");
|
||||||
|
|
||||||
// when a shape is included in the features array
|
// when a shape is included in the features array
|
||||||
MLNPolygon *polygon = [MLNPolygon polygonWithCoordinates:coordinates count:sizeof(coordinates)/sizeof(coordinates[0]) interiorPolygons:nil];
|
MLNPolygon *polygon =
|
||||||
|
[MLNPolygon polygonWithCoordinates:coordinates
|
||||||
|
count:sizeof(coordinates) / sizeof(coordinates[0])
|
||||||
|
interiorPolygons:nil];
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wobjc-literal-conversion"
|
#pragma clang diagnostic ignored "-Wobjc-literal-conversion"
|
||||||
XCTAssertThrowsSpecificNamed([[MLNShapeSource alloc] initWithIdentifier:@"source-id-invalid" features:@[polygon] options:nil], NSException, NSInvalidArgumentException, @"Shape source should raise an exception if a shape is sent to the features initializer");
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
[[MLNShapeSource alloc] initWithIdentifier:@"source-id-invalid"
|
||||||
|
features:@[ polygon ]
|
||||||
|
options:nil],
|
||||||
|
NSException, NSInvalidArgumentException,
|
||||||
|
@"Shape source should raise an exception if a shape is sent to the features initializer");
|
||||||
#pragma clang diagnostic pop
|
#pragma clang diagnostic pop
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMLNShapeSourceWithShapesConvenienceInitializer {
|
- (void)testMLNShapeSourceWithShapesConvenienceInitializer {
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0),
|
CLLocationCoordinate2DMake(0.0, 100.0), CLLocationCoordinate2DMake(0.0, 101.0),
|
||||||
CLLocationCoordinate2DMake(0.0, 101.0),
|
CLLocationCoordinate2DMake(1.0, 101.0), CLLocationCoordinate2DMake(1.0, 100.0),
|
||||||
CLLocationCoordinate2DMake(1.0, 101.0),
|
|
||||||
CLLocationCoordinate2DMake(1.0, 100.0),
|
|
||||||
CLLocationCoordinate2DMake(0.0, 100.0)};
|
CLLocationCoordinate2DMake(0.0, 100.0)};
|
||||||
|
|
||||||
MLNPolygon *polygon = [MLNPolygon polygonWithCoordinates:coordinates count:sizeof(coordinates)/sizeof(coordinates[0]) interiorPolygons:nil];
|
MLNPolygon *polygon =
|
||||||
|
[MLNPolygon polygonWithCoordinates:coordinates
|
||||||
|
count:sizeof(coordinates) / sizeof(coordinates[0])
|
||||||
|
interiorPolygons:nil];
|
||||||
|
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id" shapes:@[polygon] options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"source-id"
|
||||||
|
shapes:@[ polygon ]
|
||||||
|
options:nil];
|
||||||
MLNShapeCollectionFeature *shape = (MLNShapeCollectionFeature *)source.shape;
|
MLNShapeCollectionFeature *shape = (MLNShapeCollectionFeature *)source.shape;
|
||||||
|
|
||||||
XCTAssertTrue([shape isKindOfClass:[MLNShapeCollection class]]);
|
XCTAssertTrue([shape isKindOfClass:[MLNShapeCollection class]]);
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
#import <Mapbox.h>
|
#import <Mapbox.h>
|
||||||
|
|
||||||
#import "NSBundle+MLNAdditions.h"
|
|
||||||
#import "MLNVectorTileSource_Private.h"
|
#import "MLNVectorTileSource_Private.h"
|
||||||
|
#import "NSBundle+MLNAdditions.h"
|
||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
#import <XCTest/XCTest.h>
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#else
|
#else
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#endif
|
#endif
|
||||||
#import <objc/runtime.h>
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
@@ -28,11 +28,13 @@
|
|||||||
[MLNSettings useWellKnownTileServer:MLNMapTiler];
|
[MLNSettings useWellKnownTileServer:MLNMapTiler];
|
||||||
[MLNSettings setApiKey:@"pk.feedcafedeadbeefbadebede"];
|
[MLNSettings setApiKey:@"pk.feedcafedeadbeefbadebede"];
|
||||||
|
|
||||||
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
|
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner"
|
||||||
|
withExtension:@"json"];
|
||||||
self.mapView = [[MLNMapView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) styleURL:styleURL];
|
self.mapView = [[MLNMapView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) styleURL:styleURL];
|
||||||
self.mapView.delegate = self;
|
self.mapView.delegate = self;
|
||||||
if (!self.mapView.style) {
|
if (!self.mapView.style) {
|
||||||
_styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
|
_styleLoadingExpectation =
|
||||||
|
[self expectationWithDescription:@"Map view should finish loading style."];
|
||||||
[self waitForExpectationsWithTimeout:10 handler:nil];
|
[self waitForExpectationsWithTimeout:10 handler:nil];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,7 +72,9 @@
|
|||||||
} else {
|
} else {
|
||||||
XCTAssertEqual(self.style.sources.count, 0UL);
|
XCTAssertEqual(self.style.sources.count, 0UL);
|
||||||
}
|
}
|
||||||
MLNShapeSource *shapeSource = [[MLNShapeSource alloc] initWithIdentifier:@"shapeSource" shape:nil options:nil];
|
MLNShapeSource *shapeSource = [[MLNShapeSource alloc] initWithIdentifier:@"shapeSource"
|
||||||
|
shape:nil
|
||||||
|
options:nil];
|
||||||
[self.style addSource:shapeSource];
|
[self.style addSource:shapeSource];
|
||||||
XCTAssertEqual(self.style.sources.count, initialSources.count + 1);
|
XCTAssertEqual(self.style.sources.count, initialSources.count + 1);
|
||||||
XCTAssertEqual(shapeSource, [self.style sourceWithIdentifier:@"shapeSource"]);
|
XCTAssertEqual(shapeSource, [self.style sourceWithIdentifier:@"shapeSource"]);
|
||||||
@@ -79,39 +83,62 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testAddingSourcesTwice {
|
- (void)testAddingSourcesTwice {
|
||||||
MLNShapeSource *shapeSource = [[MLNShapeSource alloc] initWithIdentifier:@"shapeSource" shape:nil options:nil];
|
MLNShapeSource *shapeSource = [[MLNShapeSource alloc] initWithIdentifier:@"shapeSource"
|
||||||
|
shape:nil
|
||||||
|
options:nil];
|
||||||
[self.style addSource:shapeSource];
|
[self.style addSource:shapeSource];
|
||||||
XCTAssertThrowsSpecificNamed([self.style addSource:shapeSource], NSException, MLNRedundantSourceException);
|
XCTAssertThrowsSpecificNamed([self.style addSource:shapeSource], NSException,
|
||||||
|
MLNRedundantSourceException);
|
||||||
|
|
||||||
MLNRasterTileSource *rasterTileSource = [[MLNRasterTileSource alloc] initWithIdentifier:@"rasterTileSource" configurationURL:[NSURL URLWithString:@".json"] tileSize:42];
|
MLNRasterTileSource *rasterTileSource =
|
||||||
|
[[MLNRasterTileSource alloc] initWithIdentifier:@"rasterTileSource"
|
||||||
|
configurationURL:[NSURL URLWithString:@".json"]
|
||||||
|
tileSize:42];
|
||||||
[self.style addSource:rasterTileSource];
|
[self.style addSource:rasterTileSource];
|
||||||
XCTAssertThrowsSpecificNamed([self.style addSource:rasterTileSource], NSException, MLNRedundantSourceException);
|
XCTAssertThrowsSpecificNamed([self.style addSource:rasterTileSource], NSException,
|
||||||
|
MLNRedundantSourceException);
|
||||||
|
|
||||||
MLNVectorTileSource *vectorTileSource = [[MLNVectorTileSource alloc] initWithIdentifier:@"vectorTileSource" configurationURL:[NSURL URLWithString:@".json"]];
|
MLNVectorTileSource *vectorTileSource =
|
||||||
|
[[MLNVectorTileSource alloc] initWithIdentifier:@"vectorTileSource"
|
||||||
|
configurationURL:[NSURL URLWithString:@".json"]];
|
||||||
[self.style addSource:vectorTileSource];
|
[self.style addSource:vectorTileSource];
|
||||||
XCTAssertThrowsSpecificNamed([self.style addSource:vectorTileSource], NSException, MLNRedundantSourceException);
|
XCTAssertThrowsSpecificNamed([self.style addSource:vectorTileSource], NSException,
|
||||||
|
MLNRedundantSourceException);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testAddingSourcesWithDuplicateIdentifiers {
|
- (void)testAddingSourcesWithDuplicateIdentifiers {
|
||||||
MLNVectorTileSource *source1 = [[MLNVectorTileSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"maptiler://sources/hillshades"]];
|
MLNVectorTileSource *source1 = [[MLNVectorTileSource alloc]
|
||||||
MLNVectorTileSource *source2 = [[MLNVectorTileSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"maptiler://sources/hillshades"]];
|
initWithIdentifier:@"my-source"
|
||||||
|
configurationURL:[NSURL URLWithString:@"maptiler://sources/hillshades"]];
|
||||||
|
MLNVectorTileSource *source2 = [[MLNVectorTileSource alloc]
|
||||||
|
initWithIdentifier:@"my-source"
|
||||||
|
configurationURL:[NSURL URLWithString:@"maptiler://sources/hillshades"]];
|
||||||
|
|
||||||
[self.style addSource: source1];
|
[self.style addSource:source1];
|
||||||
XCTAssertThrowsSpecificNamed([self.style addSource: source2], NSException, MLNRedundantSourceIdentifierException);
|
XCTAssertThrowsSpecificNamed([self.style addSource:source2], NSException,
|
||||||
|
MLNRedundantSourceIdentifierException);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testRemovingSourcesBeforeAddingThem {
|
- (void)testRemovingSourcesBeforeAddingThem {
|
||||||
MLNRasterTileSource *rasterTileSource = [[MLNRasterTileSource alloc] initWithIdentifier:@"raster-tile-source" tileURLTemplates:@[] options:nil];
|
MLNRasterTileSource *rasterTileSource =
|
||||||
|
[[MLNRasterTileSource alloc] initWithIdentifier:@"raster-tile-source"
|
||||||
|
tileURLTemplates:@[]
|
||||||
|
options:nil];
|
||||||
[self.style removeSource:rasterTileSource];
|
[self.style removeSource:rasterTileSource];
|
||||||
[self.style addSource:rasterTileSource];
|
[self.style addSource:rasterTileSource];
|
||||||
XCTAssertNotNil([self.style sourceWithIdentifier:rasterTileSource.identifier]);
|
XCTAssertNotNil([self.style sourceWithIdentifier:rasterTileSource.identifier]);
|
||||||
|
|
||||||
MLNShapeSource *shapeSource = [[MLNShapeSource alloc] initWithIdentifier:@"shape-source" shape:nil options:nil];
|
MLNShapeSource *shapeSource = [[MLNShapeSource alloc] initWithIdentifier:@"shape-source"
|
||||||
|
shape:nil
|
||||||
|
options:nil];
|
||||||
[self.style removeSource:shapeSource];
|
[self.style removeSource:shapeSource];
|
||||||
[self.style addSource:shapeSource];
|
[self.style addSource:shapeSource];
|
||||||
XCTAssertNotNil([self.style sourceWithIdentifier:shapeSource.identifier]);
|
XCTAssertNotNil([self.style sourceWithIdentifier:shapeSource.identifier]);
|
||||||
|
|
||||||
MLNVectorTileSource *vectorTileSource = [[MLNVectorTileSource alloc] initWithIdentifier:@"vector-tile-source" tileURLTemplates:@[] options:nil];
|
MLNVectorTileSource *vectorTileSource =
|
||||||
|
[[MLNVectorTileSource alloc] initWithIdentifier:@"vector-tile-source"
|
||||||
|
tileURLTemplates:@[]
|
||||||
|
options:nil];
|
||||||
[self.style removeSource:vectorTileSource];
|
[self.style removeSource:vectorTileSource];
|
||||||
[self.style addSource:vectorTileSource];
|
[self.style addSource:vectorTileSource];
|
||||||
XCTAssertNotNil([self.style sourceWithIdentifier:vectorTileSource.identifier]);
|
XCTAssertNotNil([self.style sourceWithIdentifier:vectorTileSource.identifier]);
|
||||||
@@ -119,14 +146,21 @@
|
|||||||
|
|
||||||
- (void)testAddingSourceOfTypeABeforeSourceOfTypeBWithSameIdentifier {
|
- (void)testAddingSourceOfTypeABeforeSourceOfTypeBWithSameIdentifier {
|
||||||
// Add a raster tile source
|
// Add a raster tile source
|
||||||
MLNRasterTileSource *rasterTileSource = [[MLNRasterTileSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
|
MLNRasterTileSource *rasterTileSource =
|
||||||
|
[[MLNRasterTileSource alloc] initWithIdentifier:@"some-identifier"
|
||||||
|
tileURLTemplates:@[]
|
||||||
|
options:nil];
|
||||||
[self.style addSource:rasterTileSource];
|
[self.style addSource:rasterTileSource];
|
||||||
|
|
||||||
// Attempt to remove an image source with the same identifier as the raster tile source
|
// Attempt to remove an image source with the same identifier as the raster tile source
|
||||||
MLNImageSource *imageSource = [[MLNImageSource alloc] initWithIdentifier:@"some-identifier" coordinateQuad: { } URL:[NSURL URLWithString:@"http://host/image.png"]];
|
MLNImageSource *imageSource =
|
||||||
|
[[MLNImageSource alloc] initWithIdentifier:@"some-identifier"
|
||||||
|
coordinateQuad:{}
|
||||||
|
URL:[NSURL URLWithString:@"http://host/image.png"]];
|
||||||
[self.style removeSource:imageSource];
|
[self.style removeSource:imageSource];
|
||||||
// The raster tile source should still be added
|
// The raster tile source should still be added
|
||||||
XCTAssertTrue([[self.style sourceWithIdentifier:rasterTileSource.identifier] isMemberOfClass:[MLNRasterTileSource class]]);
|
XCTAssertTrue([[self.style sourceWithIdentifier:rasterTileSource.identifier]
|
||||||
|
isMemberOfClass:[MLNRasterTileSource class]]);
|
||||||
|
|
||||||
// Remove the raster tile source
|
// Remove the raster tile source
|
||||||
[self.style removeSource:rasterTileSource];
|
[self.style removeSource:rasterTileSource];
|
||||||
@@ -135,10 +169,14 @@
|
|||||||
[self.style addSource:imageSource];
|
[self.style addSource:imageSource];
|
||||||
|
|
||||||
// Attempt to remove a vector tile source with the same identifer as the shape source
|
// Attempt to remove a vector tile source with the same identifer as the shape source
|
||||||
MLNVectorTileSource *vectorTileSource = [[MLNVectorTileSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
|
MLNVectorTileSource *vectorTileSource =
|
||||||
|
[[MLNVectorTileSource alloc] initWithIdentifier:@"some-identifier"
|
||||||
|
tileURLTemplates:@[]
|
||||||
|
options:nil];
|
||||||
[self.style removeSource:vectorTileSource];
|
[self.style removeSource:vectorTileSource];
|
||||||
// The image source should still be added
|
// The image source should still be added
|
||||||
XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier] isMemberOfClass:[MLNImageSource class]]);
|
XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier]
|
||||||
|
isMemberOfClass:[MLNImageSource class]]);
|
||||||
|
|
||||||
// Remove the image source
|
// Remove the image source
|
||||||
[self.style removeSource:imageSource];
|
[self.style removeSource:imageSource];
|
||||||
@@ -146,19 +184,25 @@
|
|||||||
// Add the vector tile source
|
// Add the vector tile source
|
||||||
[self.style addSource:vectorTileSource];
|
[self.style addSource:vectorTileSource];
|
||||||
|
|
||||||
// Attempt to remove the previously created raster tile source that has the same identifer as the shape source
|
// Attempt to remove the previously created raster tile source that has the same identifer as the
|
||||||
|
// shape source
|
||||||
[self.style removeSource:rasterTileSource];
|
[self.style removeSource:rasterTileSource];
|
||||||
// The vector tile source should still be added
|
// The vector tile source should still be added
|
||||||
XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier] isMemberOfClass:[MLNVectorTileSource class]]);
|
XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier]
|
||||||
|
isMemberOfClass:[MLNVectorTileSource class]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testRemovingSourceInUse {
|
- (void)testRemovingSourceInUse {
|
||||||
// Add a raster tile source
|
// Add a raster tile source
|
||||||
MLNVectorTileSource *vectorTileSource = [[MLNVectorTileSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
|
MLNVectorTileSource *vectorTileSource =
|
||||||
|
[[MLNVectorTileSource alloc] initWithIdentifier:@"some-identifier"
|
||||||
|
tileURLTemplates:@[]
|
||||||
|
options:nil];
|
||||||
[self.style addSource:vectorTileSource];
|
[self.style addSource:vectorTileSource];
|
||||||
|
|
||||||
// Add a layer using it
|
// Add a layer using it
|
||||||
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"fillLayer" source:vectorTileSource];
|
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"fillLayer"
|
||||||
|
source:vectorTileSource];
|
||||||
[self.style addLayer:fillLayer];
|
[self.style addLayer:fillLayer];
|
||||||
|
|
||||||
// Attempt to remove the raster tile source
|
// Attempt to remove the raster tile source
|
||||||
@@ -170,7 +214,8 @@
|
|||||||
XCTAssertEqual(error.code, MLNErrorCodeSourceIsInUseCannotRemove);
|
XCTAssertEqual(error.code, MLNErrorCodeSourceIsInUseCannotRemove);
|
||||||
|
|
||||||
// Ensure it is still there
|
// Ensure it is still there
|
||||||
XCTAssertTrue([[self.style sourceWithIdentifier:vectorTileSource.identifier] isMemberOfClass:[MLNVectorTileSource class]]);
|
XCTAssertTrue([[self.style sourceWithIdentifier:vectorTileSource.identifier]
|
||||||
|
isMemberOfClass:[MLNVectorTileSource class]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testLayers {
|
- (void)testLayers {
|
||||||
@@ -180,9 +225,12 @@
|
|||||||
} else {
|
} else {
|
||||||
XCTAssertEqual(self.style.layers.count, 0UL);
|
XCTAssertEqual(self.style.layers.count, 0UL);
|
||||||
}
|
}
|
||||||
MLNShapeSource *shapeSource = [[MLNShapeSource alloc] initWithIdentifier:@"shapeSource" shape:nil options:nil];
|
MLNShapeSource *shapeSource = [[MLNShapeSource alloc] initWithIdentifier:@"shapeSource"
|
||||||
|
shape:nil
|
||||||
|
options:nil];
|
||||||
[self.style addSource:shapeSource];
|
[self.style addSource:shapeSource];
|
||||||
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"fillLayer" source:shapeSource];
|
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"fillLayer"
|
||||||
|
source:shapeSource];
|
||||||
[self.style addLayer:fillLayer];
|
[self.style addLayer:fillLayer];
|
||||||
XCTAssertEqual(self.style.layers.count, initialLayers.count + 1);
|
XCTAssertEqual(self.style.layers.count, initialLayers.count + 1);
|
||||||
XCTAssertEqual(fillLayer, [self.style layerWithIdentifier:@"fillLayer"]);
|
XCTAssertEqual(fillLayer, [self.style layerWithIdentifier:@"fillLayer"]);
|
||||||
@@ -191,88 +239,136 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testAddingLayersTwice {
|
- (void)testAddingLayersTwice {
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"shapeSource" shape:nil options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"shapeSource"
|
||||||
|
shape:nil
|
||||||
|
options:nil];
|
||||||
|
|
||||||
MLNBackgroundStyleLayer *backgroundLayer = [[MLNBackgroundStyleLayer alloc] initWithIdentifier:@"backgroundLayer"];
|
MLNBackgroundStyleLayer *backgroundLayer =
|
||||||
|
[[MLNBackgroundStyleLayer alloc] initWithIdentifier:@"backgroundLayer"];
|
||||||
[self.style addLayer:backgroundLayer];
|
[self.style addLayer:backgroundLayer];
|
||||||
XCTAssertThrowsSpecificNamed([self.style addLayer:backgroundLayer], NSException, MLNRedundantLayerException);
|
XCTAssertThrowsSpecificNamed([self.style addLayer:backgroundLayer], NSException,
|
||||||
|
MLNRedundantLayerException);
|
||||||
|
|
||||||
MLNCircleStyleLayer *circleLayer = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"circleLayer" source:source];
|
MLNCircleStyleLayer *circleLayer = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"circleLayer"
|
||||||
|
source:source];
|
||||||
[self.style addLayer:circleLayer];
|
[self.style addLayer:circleLayer];
|
||||||
XCTAssertThrowsSpecificNamed([self.style addLayer:circleLayer], NSException, MLNRedundantLayerException);
|
XCTAssertThrowsSpecificNamed([self.style addLayer:circleLayer], NSException,
|
||||||
|
MLNRedundantLayerException);
|
||||||
|
|
||||||
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"fillLayer" source:source];
|
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"fillLayer"
|
||||||
|
source:source];
|
||||||
[self.style addLayer:fillLayer];
|
[self.style addLayer:fillLayer];
|
||||||
XCTAssertThrowsSpecificNamed([self.style addLayer:fillLayer], NSException, MLNRedundantLayerException);
|
XCTAssertThrowsSpecificNamed([self.style addLayer:fillLayer], NSException,
|
||||||
|
MLNRedundantLayerException);
|
||||||
|
|
||||||
MLNLineStyleLayer *lineLayer = [[MLNLineStyleLayer alloc] initWithIdentifier:@"lineLayer" source:source];
|
MLNLineStyleLayer *lineLayer = [[MLNLineStyleLayer alloc] initWithIdentifier:@"lineLayer"
|
||||||
|
source:source];
|
||||||
[self.style addLayer:lineLayer];
|
[self.style addLayer:lineLayer];
|
||||||
XCTAssertThrowsSpecificNamed([self.style addLayer:lineLayer], NSException, MLNRedundantLayerException);
|
XCTAssertThrowsSpecificNamed([self.style addLayer:lineLayer], NSException,
|
||||||
|
MLNRedundantLayerException);
|
||||||
|
|
||||||
MLNRasterStyleLayer *rasterLayer = [[MLNRasterStyleLayer alloc] initWithIdentifier:@"rasterLayer" source:source];
|
MLNRasterStyleLayer *rasterLayer = [[MLNRasterStyleLayer alloc] initWithIdentifier:@"rasterLayer"
|
||||||
|
source:source];
|
||||||
[self.style addLayer:rasterLayer];
|
[self.style addLayer:rasterLayer];
|
||||||
XCTAssertThrowsSpecificNamed([self.style addLayer:rasterLayer], NSException, MLNRedundantLayerException);
|
XCTAssertThrowsSpecificNamed([self.style addLayer:rasterLayer], NSException,
|
||||||
|
MLNRedundantLayerException);
|
||||||
|
|
||||||
MLNSymbolStyleLayer *symbolLayer = [[MLNSymbolStyleLayer alloc] initWithIdentifier:@"symbolLayer" source:source];
|
MLNSymbolStyleLayer *symbolLayer = [[MLNSymbolStyleLayer alloc] initWithIdentifier:@"symbolLayer"
|
||||||
|
source:source];
|
||||||
[self.style addLayer:symbolLayer];
|
[self.style addLayer:symbolLayer];
|
||||||
XCTAssertThrowsSpecificNamed([self.style addLayer:symbolLayer], NSException, MLNRedundantLayerException);
|
XCTAssertThrowsSpecificNamed([self.style addLayer:symbolLayer], NSException,
|
||||||
|
MLNRedundantLayerException);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testAddingLayersWithDuplicateIdentifiers {
|
- (void)testAddingLayersWithDuplicateIdentifiers {
|
||||||
// Just some source
|
// Just some source
|
||||||
MLNVectorTileSource *source = [[MLNVectorTileSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"maptiler://sources/hillshades"]];
|
MLNVectorTileSource *source = [[MLNVectorTileSource alloc]
|
||||||
[self.style addSource: source];
|
initWithIdentifier:@"my-source"
|
||||||
|
configurationURL:[NSURL URLWithString:@"maptiler://sources/hillshades"]];
|
||||||
|
[self.style addSource:source];
|
||||||
|
|
||||||
// Add initial layer
|
// Add initial layer
|
||||||
MLNFillStyleLayer *initial = [[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source];
|
MLNFillStyleLayer *initial = [[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer"
|
||||||
|
source:source];
|
||||||
[self.style addLayer:initial];
|
[self.style addLayer:initial];
|
||||||
|
|
||||||
// Try to add the duplicate
|
// Try to add the duplicate
|
||||||
XCTAssertThrowsSpecificNamed([self.style addLayer:[[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source]], NSException, @"MLNRedundantLayerIdentifierException");
|
XCTAssertThrowsSpecificNamed(
|
||||||
XCTAssertThrowsSpecificNamed([self.style insertLayer:[[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] belowLayer:initial],NSException, @"MLNRedundantLayerIdentifierException");
|
[self.style addLayer:[[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer"
|
||||||
XCTAssertThrowsSpecificNamed([self.style insertLayer:[[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] aboveLayer:initial], NSException, @"MLNRedundantLayerIdentifierException");
|
source:source]],
|
||||||
XCTAssertThrowsSpecificNamed([self.style insertLayer:[[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] atIndex:0], NSException, @"MLNRedundantLayerIdentifierException");
|
NSException, @"MLNRedundantLayerIdentifierException");
|
||||||
XCTAssertThrowsSpecificNamed([self.style insertLayer:[[MLNCustomStyleLayer alloc] initWithIdentifier:@"my-layer"] atIndex:0], NSException, @"MLNRedundantLayerIdentifierException");
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
[self.style insertLayer:[[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer"
|
||||||
|
source:source]
|
||||||
|
belowLayer:initial],
|
||||||
|
NSException, @"MLNRedundantLayerIdentifierException");
|
||||||
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
[self.style insertLayer:[[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer"
|
||||||
|
source:source]
|
||||||
|
aboveLayer:initial],
|
||||||
|
NSException, @"MLNRedundantLayerIdentifierException");
|
||||||
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
[self.style insertLayer:[[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer"
|
||||||
|
source:source]
|
||||||
|
atIndex:0],
|
||||||
|
NSException, @"MLNRedundantLayerIdentifierException");
|
||||||
|
XCTAssertThrowsSpecificNamed(
|
||||||
|
[self.style insertLayer:[[MLNCustomStyleLayer alloc] initWithIdentifier:@"my-layer"]
|
||||||
|
atIndex:0],
|
||||||
|
NSException, @"MLNRedundantLayerIdentifierException");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testRemovingLayerBeforeAddingSameLayer {
|
- (void)testRemovingLayerBeforeAddingSameLayer {
|
||||||
{
|
{
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"shape-source-removing-before-adding" shape:nil options:nil];
|
MLNShapeSource *source =
|
||||||
|
[[MLNShapeSource alloc] initWithIdentifier:@"shape-source-removing-before-adding"
|
||||||
|
shape:nil
|
||||||
|
options:nil];
|
||||||
|
|
||||||
// Attempting to find a layer with identifier will trigger an exception if the source associated with the layer is not added
|
// Attempting to find a layer with identifier will trigger an exception if the source associated
|
||||||
|
// with the layer is not added
|
||||||
[self.style addSource:source];
|
[self.style addSource:source];
|
||||||
|
|
||||||
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"fill-layer" source:source];
|
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"fill-layer"
|
||||||
|
source:source];
|
||||||
[self.style removeLayer:fillLayer];
|
[self.style removeLayer:fillLayer];
|
||||||
[self.style addLayer:fillLayer];
|
[self.style addLayer:fillLayer];
|
||||||
XCTAssertNotNil([self.style layerWithIdentifier:fillLayer.identifier]);
|
XCTAssertNotNil([self.style layerWithIdentifier:fillLayer.identifier]);
|
||||||
|
|
||||||
MLNSymbolStyleLayer *symbolLayer = [[MLNSymbolStyleLayer alloc] initWithIdentifier:@"symbol-layer" source:source];
|
MLNSymbolStyleLayer *symbolLayer =
|
||||||
|
[[MLNSymbolStyleLayer alloc] initWithIdentifier:@"symbol-layer" source:source];
|
||||||
[self.style removeLayer:symbolLayer];
|
[self.style removeLayer:symbolLayer];
|
||||||
[self.style addLayer:symbolLayer];
|
[self.style addLayer:symbolLayer];
|
||||||
XCTAssertNotNil([self.style layerWithIdentifier:symbolLayer.identifier]);
|
XCTAssertNotNil([self.style layerWithIdentifier:symbolLayer.identifier]);
|
||||||
|
|
||||||
MLNLineStyleLayer *lineLayer = [[MLNLineStyleLayer alloc] initWithIdentifier:@"line-layer" source:source];
|
MLNLineStyleLayer *lineLayer = [[MLNLineStyleLayer alloc] initWithIdentifier:@"line-layer"
|
||||||
|
source:source];
|
||||||
[self.style removeLayer:lineLayer];
|
[self.style removeLayer:lineLayer];
|
||||||
[self.style addLayer:lineLayer];
|
[self.style addLayer:lineLayer];
|
||||||
XCTAssertNotNil([self.style layerWithIdentifier:lineLayer.identifier]);
|
XCTAssertNotNil([self.style layerWithIdentifier:lineLayer.identifier]);
|
||||||
|
|
||||||
MLNCircleStyleLayer *circleLayer = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"circle-layer" source:source];
|
MLNCircleStyleLayer *circleLayer =
|
||||||
|
[[MLNCircleStyleLayer alloc] initWithIdentifier:@"circle-layer" source:source];
|
||||||
[self.style removeLayer:circleLayer];
|
[self.style removeLayer:circleLayer];
|
||||||
[self.style addLayer:circleLayer];
|
[self.style addLayer:circleLayer];
|
||||||
XCTAssertNotNil([self.style layerWithIdentifier:circleLayer.identifier]);
|
XCTAssertNotNil([self.style layerWithIdentifier:circleLayer.identifier]);
|
||||||
|
|
||||||
MLNBackgroundStyleLayer *backgroundLayer = [[MLNBackgroundStyleLayer alloc] initWithIdentifier:@"background-layer"];
|
MLNBackgroundStyleLayer *backgroundLayer =
|
||||||
|
[[MLNBackgroundStyleLayer alloc] initWithIdentifier:@"background-layer"];
|
||||||
[self.style removeLayer:backgroundLayer];
|
[self.style removeLayer:backgroundLayer];
|
||||||
[self.style addLayer:backgroundLayer];
|
[self.style addLayer:backgroundLayer];
|
||||||
XCTAssertNotNil([self.style layerWithIdentifier:backgroundLayer.identifier]);
|
XCTAssertNotNil([self.style layerWithIdentifier:backgroundLayer.identifier]);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
MLNRasterTileSource *rasterSource = [[MLNRasterTileSource alloc] initWithIdentifier:@"raster-tile-source" tileURLTemplates:@[] options:nil];
|
MLNRasterTileSource *rasterSource =
|
||||||
|
[[MLNRasterTileSource alloc] initWithIdentifier:@"raster-tile-source"
|
||||||
|
tileURLTemplates:@[]
|
||||||
|
options:nil];
|
||||||
[self.style addSource:rasterSource];
|
[self.style addSource:rasterSource];
|
||||||
|
|
||||||
MLNRasterStyleLayer *rasterLayer = [[MLNRasterStyleLayer alloc] initWithIdentifier:@"raster-layer" source:rasterSource];
|
MLNRasterStyleLayer *rasterLayer =
|
||||||
|
[[MLNRasterStyleLayer alloc] initWithIdentifier:@"raster-layer" source:rasterSource];
|
||||||
[self.style removeLayer:rasterLayer];
|
[self.style removeLayer:rasterLayer];
|
||||||
[self.style addLayer:rasterLayer];
|
[self.style addLayer:rasterLayer];
|
||||||
XCTAssertNotNil([self.style layerWithIdentifier:rasterLayer.identifier]);
|
XCTAssertNotNil([self.style layerWithIdentifier:rasterLayer.identifier]);
|
||||||
@@ -280,26 +376,33 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testAddingLayerOfTypeABeforeRemovingLayerOfTypeBWithSameIdentifier {
|
- (void)testAddingLayerOfTypeABeforeRemovingLayerOfTypeBWithSameIdentifier {
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"shape-source-identifier" shape:nil options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"shape-source-identifier"
|
||||||
|
shape:nil
|
||||||
|
options:nil];
|
||||||
[self.style addSource:source];
|
[self.style addSource:source];
|
||||||
|
|
||||||
// Add a fill layer
|
// Add a fill layer
|
||||||
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"some-identifier" source:source];
|
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"some-identifier"
|
||||||
|
source:source];
|
||||||
[self.style addLayer:fillLayer];
|
[self.style addLayer:fillLayer];
|
||||||
|
|
||||||
// Attempt to remove a line layer with the same identifier as the fill layer
|
// Attempt to remove a line layer with the same identifier as the fill layer
|
||||||
MLNLineStyleLayer *lineLayer = [[MLNLineStyleLayer alloc] initWithIdentifier:fillLayer.identifier source:source];
|
MLNLineStyleLayer *lineLayer = [[MLNLineStyleLayer alloc] initWithIdentifier:fillLayer.identifier
|
||||||
|
source:source];
|
||||||
[self.style removeLayer:lineLayer];
|
[self.style removeLayer:lineLayer];
|
||||||
|
|
||||||
XCTAssertTrue([[self.style layerWithIdentifier:fillLayer.identifier] isMemberOfClass:[MLNFillStyleLayer class]]);
|
XCTAssertTrue([[self.style layerWithIdentifier:fillLayer.identifier]
|
||||||
|
isMemberOfClass:[MLNFillStyleLayer class]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)stringWithContentsOfStyleHeader {
|
- (NSString *)stringWithContentsOfStyleHeader {
|
||||||
NSURL *styleHeaderURL = [[[NSBundle mgl_frameworkBundle].bundleURL
|
NSURL *styleHeaderURL = [[[NSBundle mgl_frameworkBundle].bundleURL
|
||||||
URLByAppendingPathComponent:@"Headers" isDirectory:YES]
|
URLByAppendingPathComponent:@"Headers"
|
||||||
URLByAppendingPathComponent:@"MLNStyle.h"];
|
isDirectory:YES] URLByAppendingPathComponent:@"MLNStyle.h"];
|
||||||
NSError *styleHeaderError;
|
NSError *styleHeaderError;
|
||||||
NSString *styleHeader = [NSString stringWithContentsOfURL:styleHeaderURL usedEncoding:nil error:&styleHeaderError];
|
NSString *styleHeader = [NSString stringWithContentsOfURL:styleHeaderURL
|
||||||
|
usedEncoding:nil
|
||||||
|
error:&styleHeaderError];
|
||||||
XCTAssertNil(styleHeaderError, @"Error getting contents of MLNStyle.h.");
|
XCTAssertNil(styleHeaderError, @"Error getting contents of MLNStyle.h.");
|
||||||
return styleHeader;
|
return styleHeader;
|
||||||
}
|
}
|
||||||
@@ -324,24 +427,32 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testLayersOrder {
|
- (void)testLayersOrder {
|
||||||
NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"];
|
NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam"
|
||||||
|
ofType:@"geojson"];
|
||||||
NSURL *url = [NSURL fileURLWithPath:filePath];
|
NSURL *url = [NSURL fileURLWithPath:filePath];
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"sourceID" URL:url options:nil];
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"sourceID"
|
||||||
|
URL:url
|
||||||
|
options:nil];
|
||||||
[self.style addSource:source];
|
[self.style addSource:source];
|
||||||
|
|
||||||
MLNCircleStyleLayer *layer1 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer1" source:source];
|
MLNCircleStyleLayer *layer1 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer1"
|
||||||
|
source:source];
|
||||||
[self.style addLayer:layer1];
|
[self.style addLayer:layer1];
|
||||||
|
|
||||||
MLNCircleStyleLayer *layer3 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer3" source:source];
|
MLNCircleStyleLayer *layer3 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer3"
|
||||||
|
source:source];
|
||||||
[self.style addLayer:layer3];
|
[self.style addLayer:layer3];
|
||||||
|
|
||||||
MLNCircleStyleLayer *layer2 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer2" source:source];
|
MLNCircleStyleLayer *layer2 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer2"
|
||||||
|
source:source];
|
||||||
[self.style insertLayer:layer2 aboveLayer:layer1];
|
[self.style insertLayer:layer2 aboveLayer:layer1];
|
||||||
|
|
||||||
MLNCircleStyleLayer *layer4 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer4" source:source];
|
MLNCircleStyleLayer *layer4 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer4"
|
||||||
|
source:source];
|
||||||
[self.style insertLayer:layer4 aboveLayer:layer3];
|
[self.style insertLayer:layer4 aboveLayer:layer3];
|
||||||
|
|
||||||
MLNCircleStyleLayer *layer0 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer0" source:source];
|
MLNCircleStyleLayer *layer0 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer0"
|
||||||
|
source:source];
|
||||||
[self.style insertLayer:layer0 belowLayer:layer1];
|
[self.style insertLayer:layer0 belowLayer:layer1];
|
||||||
|
|
||||||
NSArray<MLNStyleLayer *> *layers = [self.style layers];
|
NSArray<MLNStyleLayer *> *layers = [self.style layers];
|
||||||
@@ -361,51 +472,58 @@
|
|||||||
|
|
||||||
- (void)testLanguageMatching {
|
- (void)testLanguageMatching {
|
||||||
{
|
{
|
||||||
NSArray *preferences = @[@"en"];
|
NSArray *preferences = @[ @"en" ];
|
||||||
XCTAssertEqualObjects([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
|
XCTAssertEqualObjects(
|
||||||
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *preferences = @[@"en-US"];
|
NSArray *preferences = @[ @"en-US" ];
|
||||||
XCTAssertEqualObjects([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
|
XCTAssertEqualObjects(
|
||||||
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *preferences = @[@"fr"];
|
NSArray *preferences = @[ @"fr" ];
|
||||||
XCTAssertEqualObjects([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"fr");
|
XCTAssertEqualObjects(
|
||||||
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"fr");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *preferences = @[@"zh-Hans"];
|
NSArray *preferences = @[ @"zh-Hans" ];
|
||||||
XCTAssertEqualObjects([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hans");
|
XCTAssertEqualObjects(
|
||||||
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hans");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *preferences = @[@"zh-Hans", @"en"];
|
NSArray *preferences = @[ @"zh-Hans", @"en" ];
|
||||||
XCTAssertEqualObjects([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hans");
|
XCTAssertEqualObjects(
|
||||||
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hans");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *preferences = @[@"zh-Hant"];
|
NSArray *preferences = @[ @"zh-Hant" ];
|
||||||
XCTAssertEqualObjects([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hant");
|
XCTAssertEqualObjects(
|
||||||
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hant");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *preferences = @[@"en", @"fr", @"el"];
|
NSArray *preferences = @[ @"en", @"fr", @"el" ];
|
||||||
XCTAssertEqualObjects([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
|
XCTAssertEqualObjects(
|
||||||
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *preferences = @[@"tlh"];
|
NSArray *preferences = @[ @"tlh" ];
|
||||||
XCTAssertNil([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
|
XCTAssertNil([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *preferences = @[@"tlh", @"en"];
|
NSArray *preferences = @[ @"tlh", @"en" ];
|
||||||
XCTAssertEqualObjects([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
|
XCTAssertEqualObjects(
|
||||||
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
NSArray *preferences = @[@"mul"];
|
NSArray *preferences = @[ @"mul" ];
|
||||||
XCTAssertNil([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
|
XCTAssertNil([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Transition tests
|
// MARK: Transition tests
|
||||||
|
|
||||||
- (void)testTransition
|
- (void)testTransition {
|
||||||
{
|
|
||||||
MLNTransition transitionTest = MLNTransitionMake(5, 4);
|
MLNTransition transitionTest = MLNTransitionMake(5, 4);
|
||||||
|
|
||||||
self.style.transition = transitionTest;
|
self.style.transition = transitionTest;
|
||||||
@@ -414,12 +532,13 @@
|
|||||||
XCTAssert(self.style.transition.duration == transitionTest.duration);
|
XCTAssert(self.style.transition.duration == transitionTest.duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testPerformsPlacementTransitions
|
- (void)testPerformsPlacementTransitions {
|
||||||
{
|
XCTAssertTrue(self.style.performsPlacementTransitions,
|
||||||
XCTAssertTrue(self.style.performsPlacementTransitions, @"The default value for enabling placement transitions should be YES.");
|
@"The default value for enabling placement transitions should be YES.");
|
||||||
|
|
||||||
self.style.performsPlacementTransitions = NO;
|
self.style.performsPlacementTransitions = NO;
|
||||||
XCTAssertFalse(self.style.performsPlacementTransitions, @"Enabling placement transitions should be NO.");
|
XCTAssertFalse(self.style.performsPlacementTransitions,
|
||||||
|
@"Enabling placement transitions should be NO.");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testStyleJSON {
|
- (void)testStyleJSON {
|
||||||
@@ -429,12 +548,14 @@
|
|||||||
|
|
||||||
// Verify the JSON is valid
|
// Verify the JSON is valid
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
id jsonObject = [NSJSONSerialization JSONObjectWithData:[styleJSON dataUsingEncoding:NSUTF8StringEncoding]
|
id jsonObject =
|
||||||
|
[NSJSONSerialization JSONObjectWithData:[styleJSON dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
options:0
|
options:0
|
||||||
error:&error];
|
error:&error];
|
||||||
XCTAssertNil(error, @"Style JSON should be valid JSON");
|
XCTAssertNil(error, @"Style JSON should be valid JSON");
|
||||||
XCTAssertNotNil(jsonObject, @"Style JSON should parse to a valid object");
|
XCTAssertNotNil(jsonObject, @"Style JSON should parse to a valid object");
|
||||||
XCTAssertTrue([jsonObject isKindOfClass:[NSDictionary class]], @"Style JSON should represent a dictionary");
|
XCTAssertTrue([jsonObject isKindOfClass:[NSDictionary class]],
|
||||||
|
@"Style JSON should represent a dictionary");
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#import <XCTest/XCTest.h>
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
#import <Mapbox.h>
|
#import <Mapbox.h>
|
||||||
#import "MLNVectorTileSource.h"
|
|
||||||
#import "MLNTileSource_Private.h"
|
|
||||||
#import "MLNGeometry_Private.h"
|
#import "MLNGeometry_Private.h"
|
||||||
|
#import "MLNTileSource_Private.h"
|
||||||
|
#import "MLNVectorTileSource.h"
|
||||||
|
|
||||||
#include <mbgl/util/tileset.hpp>
|
#include <mbgl/util/tileset.hpp>
|
||||||
|
|
||||||
@@ -14,10 +14,10 @@
|
|||||||
@implementation MLNTileSetTests
|
@implementation MLNTileSetTests
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// a tile set that provides an mbgl tile set
|
// a tile set that provides an mbgl tile set
|
||||||
NSArray *tileURLTemplates = @[@"tile.1", @"tile.2", @"tile.3"];
|
NSArray *tileURLTemplates = @[ @"tile.1", @"tile.2", @"tile.3" ];
|
||||||
|
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
- (void)testTileSetFromTileURLTemplates {
|
- (void)testTileSetFromTileURLTemplates {
|
||||||
mbgl::Tileset tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, nil);
|
mbgl::Tileset tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, nil);
|
||||||
@@ -37,9 +37,9 @@ namespace {
|
|||||||
XCTAssertEqual(tileSet.zoomRange.max, 22);
|
XCTAssertEqual(tileSet.zoomRange.max, 22);
|
||||||
|
|
||||||
// when the tile set has min and/or max zoom level set
|
// when the tile set has min and/or max zoom level set
|
||||||
tileSet = MLNTileSetFromTileURLTemplates(@[@"tile.1"], @{
|
tileSet = MLNTileSetFromTileURLTemplates(@[ @"tile.1" ], @{
|
||||||
MLNTileSourceOptionMinimumZoomLevel: @1,
|
MLNTileSourceOptionMinimumZoomLevel : @1,
|
||||||
MLNTileSourceOptionMaximumZoomLevel: @2,
|
MLNTileSourceOptionMaximumZoomLevel : @2,
|
||||||
});
|
});
|
||||||
|
|
||||||
// the mbgl object reflects the set values for min and max zoom level
|
// the mbgl object reflects the set values for min and max zoom level
|
||||||
@@ -47,9 +47,10 @@ namespace {
|
|||||||
XCTAssertEqual(tileSet.zoomRange.max, 2);
|
XCTAssertEqual(tileSet.zoomRange.max, 2);
|
||||||
|
|
||||||
// when the tile set has a bounds set
|
// when the tile set has a bounds set
|
||||||
MLNCoordinateBounds bounds = MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(12, 34), CLLocationCoordinate2DMake(56, 78));
|
MLNCoordinateBounds bounds = MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(12, 34),
|
||||||
tileSet = MLNTileSetFromTileURLTemplates(@[@"tile.1"], @{
|
CLLocationCoordinate2DMake(56, 78));
|
||||||
MLNTileSourceOptionCoordinateBounds: @(bounds),
|
tileSet = MLNTileSetFromTileURLTemplates(@[ @"tile.1" ], @{
|
||||||
|
MLNTileSourceOptionCoordinateBounds : @(bounds),
|
||||||
});
|
});
|
||||||
|
|
||||||
// the mbgl object reflects the set values for the bounds
|
// the mbgl object reflects the set values for the bounds
|
||||||
@@ -62,14 +63,15 @@ namespace {
|
|||||||
// when the tile set has an attribution
|
// when the tile set has an attribution
|
||||||
NSString *attribution = @"my tileset © ©️🎈";
|
NSString *attribution = @"my tileset © ©️🎈";
|
||||||
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
||||||
MLNTileSourceOptionAttributionHTMLString: attribution,
|
MLNTileSourceOptionAttributionHTMLString : attribution,
|
||||||
});
|
});
|
||||||
|
|
||||||
// the attribution is reflected by the mbgl tileset
|
// the attribution is reflected by the mbgl tileset
|
||||||
XCTAssertEqual(tileSet.attribution, attribution.UTF8String);
|
XCTAssertEqual(tileSet.attribution, attribution.UTF8String);
|
||||||
|
|
||||||
// when the tile set has attribution infos
|
// when the tile set has attribution infos
|
||||||
MLNAttributionInfo *mapboxInfo = [[MLNAttributionInfo alloc] initWithTitle:[[NSAttributedString alloc] initWithString:@"Mapbox"]
|
MLNAttributionInfo *mapboxInfo = [[MLNAttributionInfo alloc]
|
||||||
|
initWithTitle:[[NSAttributedString alloc] initWithString:@"Mapbox"]
|
||||||
URL:[NSURL URLWithString:@"https://www.mapbox.com/"]];
|
URL:[NSURL URLWithString:@"https://www.mapbox.com/"]];
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
UIColor *redColor = [UIColor redColor];
|
UIColor *redColor = [UIColor redColor];
|
||||||
@@ -80,12 +82,14 @@ namespace {
|
|||||||
// <rdar://problem/46115233> <http://www.openradar.me/46115233>
|
// <rdar://problem/46115233> <http://www.openradar.me/46115233>
|
||||||
NSColor *redColor = [NSColor colorWithCalibratedRed:1 green:0 blue:0 alpha:1];
|
NSColor *redColor = [NSColor colorWithCalibratedRed:1 green:0 blue:0 alpha:1];
|
||||||
#endif
|
#endif
|
||||||
NSAttributedString *gl = [[NSAttributedString alloc] initWithString:@"GL" attributes:@{
|
NSAttributedString *gl =
|
||||||
NSBackgroundColorAttributeName: redColor,
|
[[NSAttributedString alloc] initWithString:@"GL"
|
||||||
|
attributes:@{
|
||||||
|
NSBackgroundColorAttributeName : redColor,
|
||||||
}];
|
}];
|
||||||
MLNAttributionInfo *glInfo = [[MLNAttributionInfo alloc] initWithTitle:gl URL:nil];
|
MLNAttributionInfo *glInfo = [[MLNAttributionInfo alloc] initWithTitle:gl URL:nil];
|
||||||
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
||||||
MLNTileSourceOptionAttributionInfos: @[mapboxInfo, glInfo],
|
MLNTileSourceOptionAttributionInfos : @[ mapboxInfo, glInfo ],
|
||||||
});
|
});
|
||||||
|
|
||||||
// the attribution is reflected by the mbgl tileset
|
// the attribution is reflected by the mbgl tileset
|
||||||
@@ -94,24 +98,29 @@ namespace {
|
|||||||
if (@available(iOS 13.0, *)) {
|
if (@available(iOS 13.0, *)) {
|
||||||
// TODO: investigate visual impact
|
// TODO: investigate visual impact
|
||||||
// iOS 13 evidently changes font size from points to pixels
|
// iOS 13 evidently changes font size from points to pixels
|
||||||
html = (@"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; font-size: 12.00px\">"
|
html = (@"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; "
|
||||||
|
@"font-size: 12.00px\">"
|
||||||
@"<a href=\"https://www.mapbox.com/\">Mapbox</a> </font>"
|
@"<a href=\"https://www.mapbox.com/\">Mapbox</a> </font>"
|
||||||
@"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; font-size: 12.00px; background-color: #ff0000\">GL</font>\n");
|
@"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; "
|
||||||
|
@"font-size: 12.00px; background-color: #ff0000\">GL</font>\n");
|
||||||
} else {
|
} else {
|
||||||
html = (@"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; font-size: 12.00pt\">"
|
html = (@"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; "
|
||||||
|
@"font-size: 12.00pt\">"
|
||||||
@"<a href=\"https://www.mapbox.com/\">Mapbox</a> </font>"
|
@"<a href=\"https://www.mapbox.com/\">Mapbox</a> </font>"
|
||||||
@"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; font-size: 12.00pt; background-color: #ff0000\">GL</font>\n");
|
@"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; "
|
||||||
|
@"font-size: 12.00pt; background-color: #ff0000\">GL</font>\n");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
NSString *html = (@"<font face=\"Helvetica\" size=\"3\" style=\"font: 12.0px Helvetica\">"
|
NSString *html = (@"<font face=\"Helvetica\" size=\"3\" style=\"font: 12.0px Helvetica\">"
|
||||||
@"<a href=\"https://www.mapbox.com/\">Mapbox</a> </font>"
|
@"<a href=\"https://www.mapbox.com/\">Mapbox</a> </font>"
|
||||||
@"<font face=\"Helvetica\" size=\"3\" style=\"font: 12.0px Helvetica; background-color: #ff0000\">GL</font>\n");
|
@"<font face=\"Helvetica\" size=\"3\" style=\"font: 12.0px Helvetica; "
|
||||||
|
@"background-color: #ff0000\">GL</font>\n");
|
||||||
#endif
|
#endif
|
||||||
XCTAssertEqualObjects(@(tileSet.attribution.c_str()), html);
|
XCTAssertEqualObjects(@(tileSet.attribution.c_str()), html);
|
||||||
|
|
||||||
// when the tile coordinate system is changed using an NSNumber
|
// when the tile coordinate system is changed using an NSNumber
|
||||||
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
||||||
MLNTileSourceOptionTileCoordinateSystem: @(MLNTileCoordinateSystemTMS),
|
MLNTileSourceOptionTileCoordinateSystem : @(MLNTileCoordinateSystemTMS),
|
||||||
});
|
});
|
||||||
|
|
||||||
// the scheme is reflected by the mbgl tileset
|
// the scheme is reflected by the mbgl tileset
|
||||||
@@ -120,7 +129,8 @@ namespace {
|
|||||||
// when the tile coordinate system is changed using an NSValue
|
// when the tile coordinate system is changed using an NSValue
|
||||||
MLNTileCoordinateSystem tms = MLNTileCoordinateSystemTMS;
|
MLNTileCoordinateSystem tms = MLNTileCoordinateSystemTMS;
|
||||||
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
||||||
MLNTileSourceOptionTileCoordinateSystem: [NSValue value:&tms withObjCType:@encode(MLNTileCoordinateSystem)],
|
MLNTileSourceOptionTileCoordinateSystem : [NSValue value:&tms
|
||||||
|
withObjCType:@encode(MLNTileCoordinateSystem)],
|
||||||
});
|
});
|
||||||
|
|
||||||
// the scheme is reflected by the mbgl tileset
|
// the scheme is reflected by the mbgl tileset
|
||||||
@@ -132,7 +142,7 @@ namespace {
|
|||||||
|
|
||||||
// when the DEM encoding is changed using an NSNumber
|
// when the DEM encoding is changed using an NSNumber
|
||||||
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
||||||
MLNTileSourceOptionDEMEncoding: @(MLNDEMEncodingTerrarium),
|
MLNTileSourceOptionDEMEncoding : @(MLNDEMEncodingTerrarium),
|
||||||
});
|
});
|
||||||
|
|
||||||
// the encoding is reflected by the mbgl tileset
|
// the encoding is reflected by the mbgl tileset
|
||||||
@@ -142,7 +152,8 @@ namespace {
|
|||||||
// when the raster encoding is changed using an NSValue
|
// when the raster encoding is changed using an NSValue
|
||||||
MLNDEMEncoding terrarium = MLNDEMEncodingTerrarium;
|
MLNDEMEncoding terrarium = MLNDEMEncodingTerrarium;
|
||||||
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
||||||
MLNTileSourceOptionDEMEncoding: [NSValue value:&terrarium withObjCType:@encode(MLNDEMEncoding)],
|
MLNTileSourceOptionDEMEncoding : [NSValue value:&terrarium
|
||||||
|
withObjCType:@encode(MLNDEMEncoding)],
|
||||||
});
|
});
|
||||||
|
|
||||||
// the encoding is reflected by the mbgl tileset
|
// the encoding is reflected by the mbgl tileset
|
||||||
@@ -155,7 +166,7 @@ namespace {
|
|||||||
|
|
||||||
// when the raster encoding is changed using an NSNumber
|
// when the raster encoding is changed using an NSNumber
|
||||||
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
||||||
MLNVectorTileSourceOptionEncoding: @(MLNVectorTileSourceEncodingMapbox),
|
MLNVectorTileSourceOptionEncoding : @(MLNVectorTileSourceEncodingMapbox),
|
||||||
});
|
});
|
||||||
|
|
||||||
// the encoding is reflected by the mbgl tileset
|
// the encoding is reflected by the mbgl tileset
|
||||||
@@ -164,7 +175,7 @@ namespace {
|
|||||||
|
|
||||||
// when the raster encoding is changed using an NSNumber
|
// when the raster encoding is changed using an NSNumber
|
||||||
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
||||||
MLNVectorTileSourceOptionEncoding: @(MLNVectorTileSourceEncodingMLT),
|
MLNVectorTileSourceOptionEncoding : @(MLNVectorTileSourceEncodingMLT),
|
||||||
});
|
});
|
||||||
|
|
||||||
// the encoding is reflected by the mbgl tileset
|
// the encoding is reflected by the mbgl tileset
|
||||||
@@ -174,7 +185,8 @@ namespace {
|
|||||||
// when the raster encoding is changed using an NSValue
|
// when the raster encoding is changed using an NSValue
|
||||||
MLNVectorTileSourceEncoding mlt = MLNVectorTileSourceEncodingMLT;
|
MLNVectorTileSourceEncoding mlt = MLNVectorTileSourceEncodingMLT;
|
||||||
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
tileSet = MLNTileSetFromTileURLTemplates(tileURLTemplates, @{
|
||||||
MLNVectorTileSourceOptionEncoding: [NSValue value:&mlt withObjCType:@encode(MLNVectorTileSourceEncoding)],
|
MLNVectorTileSourceOptionEncoding : [NSValue value:&mlt
|
||||||
|
withObjCType:@encode(MLNVectorTileSourceEncoding)],
|
||||||
});
|
});
|
||||||
|
|
||||||
// the encoding is reflected by the mbgl tileset
|
// the encoding is reflected by the mbgl tileset
|
||||||
@@ -183,11 +195,13 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testInvalidTileSet {
|
- (void)testInvalidTileSet {
|
||||||
// a tile set that provides an mbgl tile set and invalid (crossed) minimum and maximum zoom levels throws an exception
|
// a tile set that provides an mbgl tile set and invalid (crossed) minimum and maximum zoom levels
|
||||||
XCTAssertThrowsSpecificNamed(MLNTileSetFromTileURLTemplates(@[@"tile.1"], @{
|
// throws an exception
|
||||||
MLNTileSourceOptionMinimumZoomLevel: @10,
|
XCTAssertThrowsSpecificNamed(MLNTileSetFromTileURLTemplates(@[ @"tile.1" ], @{
|
||||||
MLNTileSourceOptionMaximumZoomLevel: @9,
|
MLNTileSourceOptionMinimumZoomLevel : @10,
|
||||||
}), NSException, NSInvalidArgumentException);
|
MLNTileSourceOptionMaximumZoomLevel : @9,
|
||||||
|
}),
|
||||||
|
NSException, NSInvalidArgumentException);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
// Called early in GLFW initialization so that the local glyph rasterizer
|
// Called early in GLFW initialization so that the local glyph rasterizer
|
||||||
// can find a suitable CJK font via NSUserDefaults.
|
// can find a suitable CJK font via NSUserDefaults.
|
||||||
__attribute__((constructor))
|
__attribute__((constructor)) static void setDefaultIdeographicFont() {
|
||||||
static void setDefaultIdeographicFont() {
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||||
if (![defaults objectForKey:@"MLNIdeographicFontFamilyName"]) {
|
if (![defaults objectForKey:@"MLNIdeographicFontFamilyName"]) {
|
||||||
[defaults setObject:@[@"PingFang TC"] forKey:@"MLNIdeographicFontFamilyName"];
|
[defaults setObject:@[ @"PingFang TC" ] forKey:@"MLNIdeographicFontFamilyName"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ struct GLFWwindow;
|
|||||||
@class NSWindow;
|
@class NSWindow;
|
||||||
|
|
||||||
class GLFWMetalBackend final : public GLFWBackend {
|
class GLFWMetalBackend final : public GLFWBackend {
|
||||||
public:
|
public:
|
||||||
GLFWMetalBackend(GLFWwindow *window_, const bool capFrameRate);
|
GLFWMetalBackend(GLFWwindow *window_, const bool capFrameRate);
|
||||||
~GLFWMetalBackend() = default;
|
~GLFWMetalBackend() = default;
|
||||||
mbgl::gfx::RendererBackend &getRendererBackend() override;
|
mbgl::gfx::RendererBackend &getRendererBackend() override;
|
||||||
void setSize(mbgl::Size) override;
|
void setSize(mbgl::Size) override;
|
||||||
mbgl::Size getSize() const override;
|
mbgl::Size getSize() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NSWindow *window;
|
NSWindow *window;
|
||||||
MetalBackend rendererBackend;
|
MetalBackend rendererBackend;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,20 +7,10 @@
|
|||||||
#include <GLFW/glfw3native.h>
|
#include <GLFW/glfw3native.h>
|
||||||
|
|
||||||
GLFWMetalBackend::GLFWMetalBackend(GLFWwindow* window_, [[maybe_unused]] const bool capFrameRate)
|
GLFWMetalBackend::GLFWMetalBackend(GLFWwindow* window_, [[maybe_unused]] const bool capFrameRate)
|
||||||
:
|
: window(glfwGetCocoaWindow(window_)), rendererBackend(window) {}
|
||||||
window(glfwGetCocoaWindow(window_)),
|
|
||||||
rendererBackend(window)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
mbgl::gfx::RendererBackend& GLFWMetalBackend::getRendererBackend() {
|
mbgl::gfx::RendererBackend& GLFWMetalBackend::getRendererBackend() { return rendererBackend; }
|
||||||
return rendererBackend;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLFWMetalBackend::setSize(mbgl::Size size) {
|
void GLFWMetalBackend::setSize(mbgl::Size size) { rendererBackend.setSize(size); }
|
||||||
rendererBackend.setSize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
mbgl::Size GLFWMetalBackend::getSize() const {
|
mbgl::Size GLFWMetalBackend::getSize() const { return rendererBackend.getSize(); }
|
||||||
return rendererBackend.getSize();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,30 +10,27 @@ namespace mbgl {
|
|||||||
|
|
||||||
using namespace mtl;
|
using namespace mtl;
|
||||||
|
|
||||||
class MetalRenderableResource final: public mtl::RenderableResource {
|
class MetalRenderableResource final : public mtl::RenderableResource {
|
||||||
public:
|
public:
|
||||||
MetalRenderableResource(MetalBackend &backend):
|
MetalRenderableResource(MetalBackend& backend)
|
||||||
rendererBackend(backend),
|
: rendererBackend(backend),
|
||||||
commandQueue(NS::TransferPtr(backend.getDevice()->newCommandQueue())),
|
commandQueue(NS::TransferPtr(backend.getDevice()->newCommandQueue())),
|
||||||
swapchain(NS::TransferPtr(CA::MetalLayer::layer()))
|
swapchain(NS::TransferPtr(CA::MetalLayer::layer())) {
|
||||||
{
|
|
||||||
swapchain->setDevice(backend.getDevice().get());
|
swapchain->setDevice(backend.getDevice().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBackendSize(mbgl::Size size_) {
|
void setBackendSize(mbgl::Size size_) {
|
||||||
size = size_;
|
size = size_;
|
||||||
swapchain->setDrawableSize({static_cast<CGFloat>(size.width), static_cast<CGFloat>(size.height)});
|
swapchain->setDrawableSize(
|
||||||
|
{static_cast<CGFloat>(size.width), static_cast<CGFloat>(size.height)});
|
||||||
buffersInvalid = true;
|
buffersInvalid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
mbgl::Size getSize() const {
|
mbgl::Size getSize() const { return size; }
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bind() override {
|
void bind() override {
|
||||||
surface = NS::TransferPtr(swapchain->nextDrawable());
|
surface = NS::TransferPtr(swapchain->nextDrawable());
|
||||||
auto texSize = mbgl::Size{
|
auto texSize = mbgl::Size{static_cast<uint32_t>(swapchain->drawableSize().width),
|
||||||
static_cast<uint32_t>(swapchain->drawableSize().width),
|
|
||||||
static_cast<uint32_t>(swapchain->drawableSize().height)};
|
static_cast<uint32_t>(swapchain->drawableSize().height)};
|
||||||
|
|
||||||
commandBuffer = NS::TransferPtr(commandQueue->commandBuffer());
|
commandBuffer = NS::TransferPtr(commandQueue->commandBuffer());
|
||||||
@@ -45,30 +42,37 @@ public:
|
|||||||
depthTexture = rendererBackend.getContext().createTexture2D();
|
depthTexture = rendererBackend.getContext().createTexture2D();
|
||||||
depthTexture->setSize(texSize);
|
depthTexture->setSize(texSize);
|
||||||
depthTexture->setFormat(gfx::TexturePixelType::Depth, gfx::TextureChannelDataType::Float);
|
depthTexture->setFormat(gfx::TexturePixelType::Depth, gfx::TextureChannelDataType::Float);
|
||||||
depthTexture->setSamplerConfiguration(
|
depthTexture->setSamplerConfiguration({gfx::TextureFilterType::Linear,
|
||||||
{gfx::TextureFilterType::Linear, gfx::TextureWrapType::Clamp, gfx::TextureWrapType::Clamp});
|
gfx::TextureWrapType::Clamp,
|
||||||
static_cast<mtl::Texture2D*>(depthTexture.get())->setUsage(
|
gfx::TextureWrapType::Clamp});
|
||||||
MTL::TextureUsageShaderRead | MTL::TextureUsageShaderWrite | MTL::TextureUsageRenderTarget);
|
static_cast<mtl::Texture2D*>(depthTexture.get())
|
||||||
|
->setUsage(MTL::TextureUsageShaderRead | MTL::TextureUsageShaderWrite |
|
||||||
|
MTL::TextureUsageRenderTarget);
|
||||||
|
|
||||||
stencilTexture = rendererBackend.getContext().createTexture2D();
|
stencilTexture = rendererBackend.getContext().createTexture2D();
|
||||||
stencilTexture->setSize(texSize);
|
stencilTexture->setSize(texSize);
|
||||||
stencilTexture->setFormat(gfx::TexturePixelType::Stencil, gfx::TextureChannelDataType::UnsignedByte);
|
stencilTexture->setFormat(gfx::TexturePixelType::Stencil,
|
||||||
stencilTexture->setSamplerConfiguration(
|
gfx::TextureChannelDataType::UnsignedByte);
|
||||||
{gfx::TextureFilterType::Linear, gfx::TextureWrapType::Clamp, gfx::TextureWrapType::Clamp});
|
stencilTexture->setSamplerConfiguration({gfx::TextureFilterType::Linear,
|
||||||
|
gfx::TextureWrapType::Clamp,
|
||||||
|
gfx::TextureWrapType::Clamp});
|
||||||
static_cast<mtl::Texture2D*>(stencilTexture.get())
|
static_cast<mtl::Texture2D*>(stencilTexture.get())
|
||||||
->setUsage(MTL::TextureUsageShaderRead | MTL::TextureUsageShaderWrite | MTL::TextureUsageRenderTarget);
|
->setUsage(MTL::TextureUsageShaderRead | MTL::TextureUsageShaderWrite |
|
||||||
|
MTL::TextureUsageRenderTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depthTexture) {
|
if (depthTexture) {
|
||||||
depthTexture->create();
|
depthTexture->create();
|
||||||
if (auto* depthTarget = renderPassDescriptor->depthAttachment()) {
|
if (auto* depthTarget = renderPassDescriptor->depthAttachment()) {
|
||||||
depthTarget->setTexture(static_cast<mtl::Texture2D*>(depthTexture.get())->getMetalTexture());
|
depthTarget->setTexture(
|
||||||
|
static_cast<mtl::Texture2D*>(depthTexture.get())->getMetalTexture());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (stencilTexture) {
|
if (stencilTexture) {
|
||||||
stencilTexture->create();
|
stencilTexture->create();
|
||||||
if (auto* stencilTarget = renderPassDescriptor->stencilAttachment()) {
|
if (auto* stencilTarget = renderPassDescriptor->stencilAttachment()) {
|
||||||
stencilTarget->setTexture(static_cast<mtl::Texture2D*>(stencilTexture.get())->getMetalTexture());
|
stencilTarget->setTexture(
|
||||||
|
static_cast<mtl::Texture2D*>(stencilTexture.get())->getMetalTexture());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,13 +84,9 @@ public:
|
|||||||
renderPassDescriptor.reset();
|
renderPassDescriptor.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
const mtl::RendererBackend& getBackend() const override {
|
const mtl::RendererBackend& getBackend() const override { return rendererBackend; }
|
||||||
return rendererBackend;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mtl::MTLCommandBufferPtr& getCommandBuffer() const override {
|
const mtl::MTLCommandBufferPtr& getCommandBuffer() const override { return commandBuffer; }
|
||||||
return commandBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
mtl::MTLBlitPassDescriptorPtr getUploadPassDescriptor() const override {
|
mtl::MTLBlitPassDescriptorPtr getUploadPassDescriptor() const override {
|
||||||
return NS::TransferPtr(MTL::BlitPassDescriptor::alloc()->init());
|
return NS::TransferPtr(MTL::BlitPassDescriptor::alloc()->init());
|
||||||
@@ -96,12 +96,10 @@ public:
|
|||||||
return renderPassDescriptor;
|
return renderPassDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CAMetalLayerPtr& getSwapchain() const {
|
const CAMetalLayerPtr& getSwapchain() const { return swapchain; }
|
||||||
return swapchain;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MetalBackend &rendererBackend;
|
MetalBackend& rendererBackend;
|
||||||
MTLCommandQueuePtr commandQueue;
|
MTLCommandQueuePtr commandQueue;
|
||||||
MTLCommandBufferPtr commandBuffer;
|
MTLCommandBufferPtr commandBuffer;
|
||||||
MTLRenderPassDescriptorPtr renderPassDescriptor;
|
MTLRenderPassDescriptorPtr renderPassDescriptor;
|
||||||
@@ -115,17 +113,18 @@ private:
|
|||||||
|
|
||||||
} // namespace mbgl
|
} // namespace mbgl
|
||||||
|
|
||||||
MetalBackend::MetalBackend(NSWindow *window):
|
MetalBackend::MetalBackend(NSWindow* window)
|
||||||
mbgl::mtl::RendererBackend(mbgl::gfx::ContextMode::Unique),
|
: mbgl::mtl::RendererBackend(mbgl::gfx::ContextMode::Unique),
|
||||||
mbgl::gfx::Renderable(mbgl::Size{0, 0}, std::make_unique<mbgl::MetalRenderableResource>(*this))
|
mbgl::gfx::Renderable(mbgl::Size{0, 0},
|
||||||
{
|
std::make_unique<mbgl::MetalRenderableResource>(*this)) {
|
||||||
window.contentView.layer = (__bridge CALayer *)getDefaultRenderable().getResource<mbgl::MetalRenderableResource>().getSwapchain().get();
|
window.contentView.layer = (__bridge CALayer*)getDefaultRenderable()
|
||||||
|
.getResource<mbgl::MetalRenderableResource>()
|
||||||
|
.getSwapchain()
|
||||||
|
.get();
|
||||||
window.contentView.wantsLayer = YES;
|
window.contentView.wantsLayer = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
mbgl::gfx::Renderable &MetalBackend::getDefaultRenderable() {
|
mbgl::gfx::Renderable& MetalBackend::getDefaultRenderable() { return *this; }
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MetalBackend::activate() {}
|
void MetalBackend::activate() {}
|
||||||
void MetalBackend::deactivate() {}
|
void MetalBackend::deactivate() {}
|
||||||
@@ -135,4 +134,6 @@ void MetalBackend::setSize(mbgl::Size size_) {
|
|||||||
getResource<mbgl::MetalRenderableResource>().setBackendSize(size_);
|
getResource<mbgl::MetalRenderableResource>().setBackendSize(size_);
|
||||||
}
|
}
|
||||||
|
|
||||||
mbgl::Size MetalBackend::getSize() const { return getResource<mbgl::MetalRenderableResource>().getSize(); }
|
mbgl::Size MetalBackend::getSize() const {
|
||||||
|
return getResource<mbgl::MetalRenderableResource>().getSize();
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
#import "MLNMapViewIntegrationTest.h"
|
|
||||||
#import "MLNTestUtility.h"
|
|
||||||
#import "MLNMapAccessibilityElement.h"
|
|
||||||
#import "MLNTestLocationManager.h"
|
|
||||||
#import "MLNCompactCalloutView.h"
|
#import "MLNCompactCalloutView.h"
|
||||||
|
#import "MLNMapAccessibilityElement.h"
|
||||||
|
#import "MLNMapViewIntegrationTest.h"
|
||||||
|
#import "MLNTestLocationManager.h"
|
||||||
|
#import "MLNTestUtility.h"
|
||||||
|
|
||||||
#import "MLNGeometry_Private.h"
|
#import "MLNGeometry_Private.h"
|
||||||
#import "MLNMapView_Private.h"
|
#import "MLNMapView_Private.h"
|
||||||
|
|
||||||
#include <mbgl/util/geo.hpp>
|
|
||||||
#include <mbgl/map/camera.hpp>
|
#include <mbgl/map/camera.hpp>
|
||||||
#include <mbgl/map/map.hpp>
|
#include <mbgl/map/map.hpp>
|
||||||
|
#include <mbgl/util/geo.hpp>
|
||||||
|
|
||||||
@interface MLNTestCalloutView : MLNCompactCalloutView
|
@interface MLNTestCalloutView : MLNCompactCalloutView
|
||||||
@property (nonatomic) BOOL implementsMarginHints;
|
@property (nonatomic) BOOL implementsMarginHints;
|
||||||
@@ -25,11 +25,11 @@
|
|||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@interface MLNMapView (Tests)
|
@interface MLNMapView (Tests)
|
||||||
- (MLNAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL)persist;
|
- (MLNAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL)persist;
|
||||||
- (id <MLNAnnotation>)annotationWithTag:(MLNAnnotationTag)tag;
|
- (id<MLNAnnotation>)annotationWithTag:(MLNAnnotationTag)tag;
|
||||||
- (MLNMapCamera *)cameraByRotatingToDirection:(CLLocationDirection)degrees aroundAnchorPoint:(CGPoint)anchorPoint;
|
- (MLNMapCamera *)cameraByRotatingToDirection:(CLLocationDirection)degrees
|
||||||
|
aroundAnchorPoint:(CGPoint)anchorPoint;
|
||||||
- (MLNMapCamera *)cameraByZoomingToZoomLevel:(double)zoom aroundAnchorPoint:(CGPoint)anchorPoint;
|
- (MLNMapCamera *)cameraByZoomingToZoomLevel:(double)zoom aroundAnchorPoint:(CGPoint)anchorPoint;
|
||||||
- (MLNMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions;
|
- (MLNMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions;
|
||||||
@property (nonatomic) UIView<MLNCalloutView> *calloutViewForSelectedAnnotation;
|
@property (nonatomic) UIView<MLNCalloutView> *calloutViewForSelectedAnnotation;
|
||||||
@@ -52,10 +52,11 @@ typedef struct PanTestData {
|
|||||||
} PanTestData;
|
} PanTestData;
|
||||||
|
|
||||||
#define PAN_TEST_TERMINATOR {{FLT_MAX, FLT_MAX}, NO, NO, NO, NO, NO}
|
#define PAN_TEST_TERMINATOR {{FLT_MAX, FLT_MAX}, NO, NO, NO, NO, NO}
|
||||||
static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
static const CGPoint kAnnotationRelativeScale = {0.05f, 0.125f};
|
||||||
|
|
||||||
- (void)internalTestOffscreenSelectionTitle:(NSString*)title withTestData:(PanTestData)test animateSelection:(BOOL)animateSelection {
|
|
||||||
|
|
||||||
|
- (void)internalTestOffscreenSelectionTitle:(NSString *)title
|
||||||
|
withTestData:(PanTestData)test
|
||||||
|
animateSelection:(BOOL)animateSelection {
|
||||||
CGPoint relativeCoordinate = test.relativeCoord;
|
CGPoint relativeCoordinate = test.relativeCoord;
|
||||||
BOOL showsCallout = test.showsCallout;
|
BOOL showsCallout = test.showsCallout;
|
||||||
BOOL calloutImplementsMarginHints = test.implementsMargins;
|
BOOL calloutImplementsMarginHints = test.implementsMargins;
|
||||||
@@ -70,19 +71,21 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
|
|
||||||
XCTAssert(self.mapView.annotations.count == 0);
|
XCTAssert(self.mapView.annotations.count == 0);
|
||||||
|
|
||||||
NSString * const MLNTestAnnotationReuseIdentifer = @"MLNTestAnnotationReuseIdentifer";
|
NSString *const MLNTestAnnotationReuseIdentifer = @"MLNTestAnnotationReuseIdentifer";
|
||||||
CGSize size = self.mapView.bounds.size;
|
CGSize size = self.mapView.bounds.size;
|
||||||
CGSize annotationSize = CGSizeMake(floor(size.width*kAnnotationRelativeScale.x), floor(size.height*kAnnotationRelativeScale.y));
|
CGSize annotationSize = CGSizeMake(floor(size.width * kAnnotationRelativeScale.x),
|
||||||
|
floor(size.height * kAnnotationRelativeScale.y));
|
||||||
self.viewForAnnotation = ^MLNAnnotationView*(MLNMapView *view, id<MLNAnnotation> annotation) {
|
|
||||||
|
|
||||||
|
self.viewForAnnotation = ^MLNAnnotationView *(MLNMapView *view, id<MLNAnnotation> annotation) {
|
||||||
if (![annotation isKindOfClass:[MLNPointAnnotation class]]) {
|
if (![annotation isKindOfClass:[MLNPointAnnotation class]]) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No dequeue
|
// No dequeue
|
||||||
MLNAnnotationView *annotationView = [[MLNAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:MLNTestAnnotationReuseIdentifer];
|
MLNAnnotationView *annotationView =
|
||||||
annotationView.bounds = (CGRect){ .origin = CGPointZero, .size = annotationSize };
|
[[MLNAnnotationView alloc] initWithAnnotation:annotation
|
||||||
|
reuseIdentifier:MLNTestAnnotationReuseIdentifer];
|
||||||
|
annotationView.bounds = (CGRect){.origin = CGPointZero, .size = annotationSize};
|
||||||
annotationView.backgroundColor = UIColor.redColor;
|
annotationView.backgroundColor = UIColor.redColor;
|
||||||
annotationView.enabled = YES;
|
annotationView.enabled = YES;
|
||||||
|
|
||||||
@@ -90,20 +93,23 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Coordinate for annotation screen coordinate
|
// Coordinate for annotation screen coordinate
|
||||||
CGPoint annotationPoint = CGPointMake(floor(relativeCoordinate.x * size.width), floor(relativeCoordinate.y * size.height) );
|
CGPoint annotationPoint = CGPointMake(floor(relativeCoordinate.x * size.width),
|
||||||
CLLocationCoordinate2D coordinate = [self.mapView convertPoint:annotationPoint toCoordinateFromView:self.mapView];
|
floor(relativeCoordinate.y * size.height));
|
||||||
|
CLLocationCoordinate2D coordinate = [self.mapView convertPoint:annotationPoint
|
||||||
|
toCoordinateFromView:self.mapView];
|
||||||
|
|
||||||
MLNPointAnnotation *point = [[MLNPointAnnotation alloc] init];
|
MLNPointAnnotation *point = [[MLNPointAnnotation alloc] init];
|
||||||
point.title = title;
|
point.title = title;
|
||||||
point.coordinate = coordinate;
|
point.coordinate = coordinate;
|
||||||
|
|
||||||
self.mapViewAnnotationCanShowCalloutForAnnotation = ^BOOL(MLNMapView *mapView, id<MLNAnnotation> annotation) {
|
self.mapViewAnnotationCanShowCalloutForAnnotation =
|
||||||
|
^BOOL(MLNMapView *mapView, id<MLNAnnotation> annotation) {
|
||||||
return showsCallout;
|
return showsCallout;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.mapViewCalloutViewForAnnotation = ^id<MLNCalloutView>(MLNMapView *mapView, id<MLNAnnotation> annotation) {
|
self.mapViewCalloutViewForAnnotation =
|
||||||
if (!showsCallout)
|
^id<MLNCalloutView>(MLNMapView *mapView, id<MLNAnnotation> annotation) {
|
||||||
return nil;
|
if (!showsCallout) return nil;
|
||||||
|
|
||||||
MLNTestCalloutView *calloutView = [[MLNTestCalloutView alloc] init];
|
MLNTestCalloutView *calloutView = [[MLNTestCalloutView alloc] init];
|
||||||
calloutView.representedObject = annotation;
|
calloutView.representedObject = annotation;
|
||||||
@@ -125,7 +131,10 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
// will have run, and `viewForAnnotation` may return nil
|
// will have run, and `viewForAnnotation` may return nil
|
||||||
|
|
||||||
XCTestExpectation *selectionCompleted = [self expectationWithDescription:@"Selection completed"];
|
XCTestExpectation *selectionCompleted = [self expectationWithDescription:@"Selection completed"];
|
||||||
[self.mapView selectAnnotation:point moveIntoView:moveIntoView animateSelection:animateSelection completionHandler:^{
|
[self.mapView selectAnnotation:point
|
||||||
|
moveIntoView:moveIntoView
|
||||||
|
animateSelection:animateSelection
|
||||||
|
completionHandler:^{
|
||||||
[selectionCompleted fulfill];
|
[selectionCompleted fulfill];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@@ -137,9 +146,9 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
// Something is amiss with dispatch_async & XCTest on simulators in a recent
|
// Something is amiss with dispatch_async & XCTest on simulators in a recent
|
||||||
// iOS SDK release.
|
// iOS SDK release.
|
||||||
// TODO: Add issue
|
// TODO: Add issue
|
||||||
[self waitForExpectations:@[selectionCompleted] timeout:animateSelection ? 0.4: 0.1];
|
[self waitForExpectations:@[ selectionCompleted ] timeout:animateSelection ? 0.4 : 0.1];
|
||||||
#else
|
#else
|
||||||
[self waitForExpectations:@[selectionCompleted] timeout:animateSelection ? 0.4: 0.05];
|
[self waitForExpectations:@[ selectionCompleted ] timeout:animateSelection ? 0.4 : 0.05];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
UIView *annotationViewAfterSelection = [self.mapView viewForAnnotation:point];
|
UIView *annotationViewAfterSelection = [self.mapView viewForAnnotation:point];
|
||||||
@@ -148,17 +157,21 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
|
|
||||||
// If the annotation is still "offscreen" at this point, then the above annotation view
|
// If the annotation is still "offscreen" at this point, then the above annotation view
|
||||||
// may be nil, which is expected.
|
// may be nil, which is expected.
|
||||||
BOOL (^CGRectContainsRectWithAccuracy)(CGRect, CGRect, CGFloat) = ^(CGRect rect1, CGRect rect2, CGFloat accuracy) {
|
BOOL (^CGRectContainsRectWithAccuracy)(CGRect, CGRect, CGFloat) =
|
||||||
|
^(CGRect rect1, CGRect rect2, CGFloat accuracy) {
|
||||||
CGRect expandedRect1 = CGRectInset(rect1, -accuracy, -accuracy);
|
CGRect expandedRect1 = CGRectInset(rect1, -accuracy, -accuracy);
|
||||||
return (BOOL)CGRectContainsRect(expandedRect1, rect2);
|
return (BOOL)CGRectContainsRect(expandedRect1, rect2);
|
||||||
};
|
};
|
||||||
|
|
||||||
CGFloat epsilon = 0.00001;
|
CGFloat epsilon = 0.00001;
|
||||||
if (expectMapToHavePanned) {
|
if (expectMapToHavePanned) {
|
||||||
CLLocationDegrees latitudeDelta = fabs(mapCenterAfterSelection.latitude - mapCenterBeforeSelection.latitude);
|
CLLocationDegrees latitudeDelta =
|
||||||
CLLocationDegrees longitudeDelta = fabs(mapCenterAfterSelection.longitude - mapCenterBeforeSelection.longitude);
|
fabs(mapCenterAfterSelection.latitude - mapCenterBeforeSelection.latitude);
|
||||||
|
CLLocationDegrees longitudeDelta =
|
||||||
|
fabs(mapCenterAfterSelection.longitude - mapCenterBeforeSelection.longitude);
|
||||||
|
|
||||||
XCTAssert( (latitudeDelta > epsilon) || (longitudeDelta > epsilon), @"Deltas: lat=%f, long=%f", latitudeDelta, longitudeDelta); // One of them should have moved
|
XCTAssert((latitudeDelta > epsilon) || (longitudeDelta > epsilon), @"Deltas: lat=%f, long=%f",
|
||||||
|
latitudeDelta, longitudeDelta); // One of them should have moved
|
||||||
|
|
||||||
// If the map panned - the intention is that the annotation is on-screen,
|
// If the map panned - the intention is that the annotation is on-screen,
|
||||||
// and so should have an annotation view and that it is fully on screen
|
// and so should have an annotation view and that it is fully on screen
|
||||||
@@ -166,30 +179,33 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
|
|
||||||
XCTAssertNotNil(annotationViewAfterSelection);
|
XCTAssertNotNil(annotationViewAfterSelection);
|
||||||
|
|
||||||
XCTAssert(CGRectContainsRectWithAccuracy(self.mapView.bounds, annotationFrameAfterSelection, 0.25), @"Mapview:%@ frame:%@", NSStringFromCGRect(self.mapView.bounds), NSStringFromCGRect(annotationFrameAfterSelection));
|
XCTAssert(
|
||||||
|
CGRectContainsRectWithAccuracy(self.mapView.bounds, annotationFrameAfterSelection, 0.25),
|
||||||
|
@"Mapview:%@ frame:%@", NSStringFromCGRect(self.mapView.bounds),
|
||||||
|
NSStringFromCGRect(annotationFrameAfterSelection));
|
||||||
|
|
||||||
// Check the callout
|
// Check the callout
|
||||||
if (showsCallout) {
|
if (showsCallout) {
|
||||||
UIView *calloutView = self.mapView.calloutViewForSelectedAnnotation;
|
UIView *calloutView = self.mapView.calloutViewForSelectedAnnotation;
|
||||||
XCTAssertNotNil(calloutView);
|
XCTAssertNotNil(calloutView);
|
||||||
|
|
||||||
// This can fail if the callout view's width is < the annotations. This is really a warning, so
|
// This can fail if the callout view's width is < the annotations. This is really a warning,
|
||||||
// if you need this NOT to fail the tests, consider replacing with MLNTestWarning
|
// so if you need this NOT to fail the tests, consider replacing with MLNTestWarning
|
||||||
XCTAssert(expectCalloutToBeFullyOnscreen == CGRectContainsRectWithAccuracy(self.mapView.bounds, calloutView.frame, 0.25),
|
XCTAssert(expectCalloutToBeFullyOnscreen ==
|
||||||
|
CGRectContainsRectWithAccuracy(self.mapView.bounds, calloutView.frame, 0.25),
|
||||||
@"Expect contains:%d, Mapview:%@ annotation:%@ callout:%@",
|
@"Expect contains:%d, Mapview:%@ annotation:%@ callout:%@",
|
||||||
expectCalloutToBeFullyOnscreen,
|
expectCalloutToBeFullyOnscreen, NSStringFromCGRect(self.mapView.bounds),
|
||||||
NSStringFromCGRect(self.mapView.bounds),
|
|
||||||
NSStringFromCGRect(annotationFrameAfterSelection),
|
NSStringFromCGRect(annotationFrameAfterSelection),
|
||||||
NSStringFromCGRect(calloutView.frame));
|
NSStringFromCGRect(calloutView.frame));
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// The map shouldn't have moved, so use equality (rather than an error check)
|
// The map shouldn't have moved, so use equality (rather than an error check)
|
||||||
XCTAssertEqual(mapCenterBeforeSelection.latitude, mapCenterAfterSelection.latitude);
|
XCTAssertEqual(mapCenterBeforeSelection.latitude, mapCenterAfterSelection.latitude);
|
||||||
XCTAssertEqual(mapCenterBeforeSelection.longitude, mapCenterAfterSelection.longitude);
|
XCTAssertEqual(mapCenterBeforeSelection.longitude, mapCenterAfterSelection.longitude);
|
||||||
|
|
||||||
// Annotation shouldn't have moved
|
// Annotation shouldn't have moved
|
||||||
CGPoint annotationPoint2 = [self.mapView convertCoordinate:point.coordinate toPointToView:self.mapView];
|
CGPoint annotationPoint2 = [self.mapView convertCoordinate:point.coordinate
|
||||||
|
toPointToView:self.mapView];
|
||||||
CGFloat xDelta = fabs(annotationPoint2.x - annotationPoint.x);
|
CGFloat xDelta = fabs(annotationPoint2.x - annotationPoint.x);
|
||||||
CGFloat yDelta = fabs(annotationPoint2.y - annotationPoint.y);
|
CGFloat yDelta = fabs(annotationPoint2.y - annotationPoint.y);
|
||||||
|
|
||||||
@@ -204,13 +220,12 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
// If kAnnotationScale == 0.25, then the following assert can fail.
|
// If kAnnotationScale == 0.25, then the following assert can fail.
|
||||||
// This is really a warning (see https://github.com/mapbox/mapbox-gl-native/issues/13744 )
|
// This is really a warning (see https://github.com/mapbox/mapbox-gl-native/issues/13744 )
|
||||||
// If you need this NOT to fail the tests, consider replacing with MLNTestWarning
|
// If you need this NOT to fail the tests, consider replacing with MLNTestWarning
|
||||||
XCTAssert((expectCalloutToBeFullyOnscreen == CGRectContainsRectWithAccuracy(self.mapView.bounds, calloutView.frame, 0.25)),
|
XCTAssert((expectCalloutToBeFullyOnscreen ==
|
||||||
@"Mapview:%@ annotation:%@ callout:%@",
|
CGRectContainsRectWithAccuracy(self.mapView.bounds, calloutView.frame, 0.25)),
|
||||||
NSStringFromCGRect(self.mapView.bounds),
|
@"Mapview:%@ annotation:%@ callout:%@", NSStringFromCGRect(self.mapView.bounds),
|
||||||
NSStringFromCGRect(annotationViewAfterSelection.frame),
|
NSStringFromCGRect(annotationViewAfterSelection.frame),
|
||||||
NSStringFromCGRect(calloutView.frame));
|
NSStringFromCGRect(calloutView.frame));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// If there's no annotation view, should we expect a callout?
|
// If there's no annotation view, should we expect a callout?
|
||||||
XCTAssertNil(calloutView);
|
XCTAssertNil(calloutView);
|
||||||
XCTAssertFalse(expectCalloutToBeFullyOnscreen);
|
XCTAssertFalse(expectCalloutToBeFullyOnscreen);
|
||||||
@@ -228,10 +243,9 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
// What follows are tests based on this table.
|
// What follows are tests based on this table.
|
||||||
// This is not a full-set of possible combinations, just the most important/likely
|
// This is not a full-set of possible combinations, just the most important/likely
|
||||||
// ones
|
// ones
|
||||||
- (void)internalRunTests:(PanTestData*)testData
|
- (void)internalRunTests:(PanTestData *)testData {
|
||||||
{
|
|
||||||
// Test both animated and not-animated.
|
// Test both animated and not-animated.
|
||||||
for (int i = 0; i<2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
int row = 0;
|
int row = 0;
|
||||||
PanTestData *test = testData;
|
PanTestData *test = testData;
|
||||||
while (test->relativeCoord.x != FLT_MAX) {
|
while (test->relativeCoord.x != FLT_MAX) {
|
||||||
@@ -253,24 +267,24 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
// WITHOUT a callout
|
// WITHOUT a callout
|
||||||
|
|
||||||
PanTestData tests[] = {
|
PanTestData tests[] = {
|
||||||
// Coord showsCallout impl margins? moveIntoView expectMapToPan calloutOnScreen
|
// Coord showsCallout impl margins? moveIntoView expectMapToPan
|
||||||
|
// calloutOnScreen
|
||||||
// Offscreen
|
// Offscreen
|
||||||
{ {-1.0f, 0.5f}, NO, NO, NO, NO, NO },
|
{{-1.0f, 0.5f}, NO, NO, NO, NO, NO},
|
||||||
{ { 2.0f, 0.5f}, NO, NO, NO, NO, NO },
|
{{2.0f, 0.5f}, NO, NO, NO, NO, NO},
|
||||||
{ { 0.5f,-1.0f}, NO, NO, NO, NO, NO },
|
{{0.5f, -1.0f}, NO, NO, NO, NO, NO},
|
||||||
{ { 0.5f, 2.0f}, NO, NO, NO, NO, NO },
|
{{0.5f, 2.0f}, NO, NO, NO, NO, NO},
|
||||||
|
|
||||||
// Partial
|
// Partial
|
||||||
{ { 0.0f, 0.5f}, NO, NO, NO, NO, NO },
|
{{0.0f, 0.5f}, NO, NO, NO, NO, NO},
|
||||||
{ { 1.0f, 0.5f}, NO, NO, NO, NO, NO },
|
{{1.0f, 0.5f}, NO, NO, NO, NO, NO},
|
||||||
{ { 0.5f, 0.0f}, NO, NO, NO, NO, NO },
|
{{0.5f, 0.0f}, NO, NO, NO, NO, NO},
|
||||||
{ { 0.5f, 1.0f}, NO, NO, NO, NO, NO },
|
{{0.5f, 1.0f}, NO, NO, NO, NO, NO},
|
||||||
|
|
||||||
// Onscreen
|
// Onscreen
|
||||||
{ { 0.5f, 0.5f}, NO, NO, NO, NO, NO },
|
{{0.5f, 0.5f}, NO, NO, NO, NO, NO},
|
||||||
|
|
||||||
PAN_TEST_TERMINATOR
|
PAN_TEST_TERMINATOR};
|
||||||
};
|
|
||||||
|
|
||||||
[self internalRunTests:tests];
|
[self internalRunTests:tests];
|
||||||
}
|
}
|
||||||
@@ -280,14 +294,15 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
// WITH the default callout (implements marginshint)
|
// WITH the default callout (implements marginshint)
|
||||||
|
|
||||||
PanTestData tests[] = {
|
PanTestData tests[] = {
|
||||||
// Coord showsCallout impl margins? moveIntoView expectMapToPan calloutOnScreen
|
// Coord showsCallout impl margins? moveIntoView expectMapToPan
|
||||||
{ {-1.0f, 0.5f}, YES, YES, NO, NO, NO },
|
// calloutOnScreen
|
||||||
{ { 0.0f, 0.5f}, YES, YES, NO, NO, NO },
|
{{-1.0f, 0.5f}, YES, YES, NO, NO, NO},
|
||||||
{ { 0.5f, 1.0f}, YES, YES, NO, NO, YES }, // Because annotation was off the bottom of screen, and callout is above annotation
|
{{0.0f, 0.5f}, YES, YES, NO, NO, NO},
|
||||||
{ { 0.5f, 0.5f}, YES, YES, NO, NO, YES },
|
{{0.5f, 1.0f}, YES, YES, NO, NO, YES}, // Because annotation was off the bottom of screen,
|
||||||
|
// and callout is above annotation
|
||||||
|
{{0.5f, 0.5f}, YES, YES, NO, NO, YES},
|
||||||
|
|
||||||
PAN_TEST_TERMINATOR
|
PAN_TEST_TERMINATOR};
|
||||||
};
|
|
||||||
|
|
||||||
[self internalRunTests:tests];
|
[self internalRunTests:tests];
|
||||||
}
|
}
|
||||||
@@ -298,32 +313,33 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
|
|
||||||
// From https://github.com/mapbox/mapbox-gl-native/pull/13727#issuecomment-454028698
|
// From https://github.com/mapbox/mapbox-gl-native/pull/13727#issuecomment-454028698
|
||||||
//
|
//
|
||||||
// | Annotation position | Has callout? | Callout implements `marginInsets...`? | Map pans when selected with moveIntoView=YES? |
|
// | Annotation position | Has callout? | Callout implements `marginInsets...`? | Map pans when
|
||||||
|
// selected with moveIntoView=YES? |
|
||||||
// |---------------------|--------------|---------------------------------------|-----------------------------------------------|
|
// |---------------------|--------------|---------------------------------------|-----------------------------------------------|
|
||||||
// | Offscreen | No | n/a | Yes (no margins) |
|
// | Offscreen | No | n/a | Yes (no margins)
|
||||||
// | Partially | No | n/a | No |
|
// | | Partially | No | n/a | No | |
|
||||||
// | Onscreen | No | n/a | No |
|
// Onscreen | No | n/a | No |
|
||||||
//
|
//
|
||||||
|
|
||||||
PanTestData tests[] = {
|
PanTestData tests[] = {
|
||||||
// Coord showsCallout impl margins? moveIntoView expectMapToPan calloutOnScreen
|
// Coord showsCallout impl margins? moveIntoView expectMapToPan
|
||||||
|
// calloutOnScreen
|
||||||
// Offscreen
|
// Offscreen
|
||||||
{ {-1.0f, 0.5f}, NO, NO, YES, YES, NO },
|
{{-1.0f, 0.5f}, NO, NO, YES, YES, NO},
|
||||||
{ { 2.0f, 0.5f}, NO, NO, YES, YES, NO },
|
{{2.0f, 0.5f}, NO, NO, YES, YES, NO},
|
||||||
{ { 0.5f,-1.0f}, NO, NO, YES, YES, NO },
|
{{0.5f, -1.0f}, NO, NO, YES, YES, NO},
|
||||||
{ { 0.5f, 2.0f}, NO, NO, YES, YES, NO },
|
{{0.5f, 2.0f}, NO, NO, YES, YES, NO},
|
||||||
|
|
||||||
// Partial
|
// Partial
|
||||||
{ { 0.0f, 0.5f}, NO, NO, YES, NO, NO },
|
{{0.0f, 0.5f}, NO, NO, YES, NO, NO},
|
||||||
{ { 1.0f, 0.5f}, NO, NO, YES, NO, NO },
|
{{1.0f, 0.5f}, NO, NO, YES, NO, NO},
|
||||||
{ { 0.5f, 0.0f}, NO, NO, YES, NO, NO },
|
{{0.5f, 0.0f}, NO, NO, YES, NO, NO},
|
||||||
{ { 0.5f, 1.0f}, NO, NO, YES, NO, NO },
|
{{0.5f, 1.0f}, NO, NO, YES, NO, NO},
|
||||||
|
|
||||||
// Onscreen
|
// Onscreen
|
||||||
{ { 0.5f, 0.5f}, NO, NO, YES, NO, NO },
|
{{0.5f, 0.5f}, NO, NO, YES, NO, NO},
|
||||||
|
|
||||||
PAN_TEST_TERMINATOR
|
PAN_TEST_TERMINATOR};
|
||||||
};
|
|
||||||
|
|
||||||
[self internalRunTests:tests];
|
[self internalRunTests:tests];
|
||||||
}
|
}
|
||||||
@@ -334,43 +350,46 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
|
|
||||||
// From https://github.com/mapbox/mapbox-gl-native/pull/13727#issuecomment-454028698
|
// From https://github.com/mapbox/mapbox-gl-native/pull/13727#issuecomment-454028698
|
||||||
//
|
//
|
||||||
// | Annotation position | Has callout? | Callout implements `marginInsets...`? | Map pans when selected with moveIntoView=YES? |
|
// | Annotation position | Has callout? | Callout implements `marginInsets...`? | Map pans when
|
||||||
|
// selected with moveIntoView=YES? |
|
||||||
// |---------------------|--------------|---------------------------------------|-----------------------------------------------|
|
// |---------------------|--------------|---------------------------------------|-----------------------------------------------|
|
||||||
// | Offscreen | Yes | Yes | Yes to ensure callout is fully visible |
|
// | Offscreen | Yes | Yes | Yes to ensure
|
||||||
// | Partially | Yes | Yes | Yes to ensure callout is fully visible |
|
// callout is fully visible | | Partially | Yes | Yes | Yes to ensure
|
||||||
// | Onscreen | Yes | Yes | Yes, but *only* to ensure callout is fully visible |
|
// callout is fully visible | | Onscreen | Yes | Yes | Yes, but *only*
|
||||||
|
// to ensure callout is fully visible |
|
||||||
//
|
//
|
||||||
|
|
||||||
CGFloat offset = kAnnotationRelativeScale.x * 0.5f;
|
CGFloat offset = kAnnotationRelativeScale.x * 0.5f;
|
||||||
|
|
||||||
PanTestData tests[] = {
|
PanTestData tests[] = {
|
||||||
// Coord showsCallout impl margins? moveIntoView expectMapToPan calloutOnScreen
|
// Coord showsCallout impl margins? moveIntoView expectMapToPan
|
||||||
|
// calloutOnScreen
|
||||||
// Offscreen
|
// Offscreen
|
||||||
{ {-1.0f, 0.5f}, YES, YES, YES, YES, YES },
|
{{-1.0f, 0.5f}, YES, YES, YES, YES, YES},
|
||||||
{ { 2.0f, 0.5f}, YES, YES, YES, YES, YES },
|
{{2.0f, 0.5f}, YES, YES, YES, YES, YES},
|
||||||
{ { 0.5f,-1.0f}, YES, YES, YES, YES, YES },
|
{{0.5f, -1.0f}, YES, YES, YES, YES, YES},
|
||||||
{ { 0.5f, 2.0f}, YES, YES, YES, YES, YES },
|
{{0.5f, 2.0f}, YES, YES, YES, YES, YES},
|
||||||
|
|
||||||
// Partial
|
// Partial
|
||||||
{ { 0.0f, 0.5f}, YES, YES, YES, YES, YES },
|
{{0.0f, 0.5f}, YES, YES, YES, YES, YES},
|
||||||
{ { 1.0f, 0.5f}, YES, YES, YES, YES, YES },
|
{{1.0f, 0.5f}, YES, YES, YES, YES, YES},
|
||||||
{ { 0.5f, 0.0f}, YES, YES, YES, YES, YES },
|
{{0.5f, 0.0f}, YES, YES, YES, YES, YES},
|
||||||
{ { 0.5f, 1.0f}, YES, YES, YES, YES, YES },
|
{{0.5f, 1.0f}, YES, YES, YES, YES, YES},
|
||||||
|
|
||||||
// Onscreen
|
// Onscreen
|
||||||
{ { 0.5f, 0.5f}, YES, YES, YES, NO, YES },
|
{{0.5f, 0.5f}, YES, YES, YES, NO, YES},
|
||||||
|
|
||||||
// Just at the edge of the screen.
|
// Just at the edge of the screen.
|
||||||
// Expects to move, because although onscreen, callout would not be.
|
// Expects to move, because although onscreen, callout would not be.
|
||||||
// However, if the scale is 0.25, then expectToPan should be NO, because
|
// However, if the scale is 0.25, then expectToPan should be NO, because
|
||||||
// of the width of the annotation
|
// of the width of the annotation
|
||||||
|
|
||||||
// Coord showsCallout impl margins? moveIntoView expectMapToPan calloutOnScreen
|
// Coord showsCallout impl margins? moveIntoView expectMapToPan
|
||||||
{ {offset, 0.5f}, YES, YES, YES, YES, YES },
|
// calloutOnScreen
|
||||||
{ {(CGFloat)(1.0 - offset), 0.5f}, YES, YES, YES, YES, YES },
|
{{offset, 0.5f}, YES, YES, YES, YES, YES},
|
||||||
|
{{(CGFloat)(1.0 - offset), 0.5f}, YES, YES, YES, YES, YES},
|
||||||
|
|
||||||
PAN_TEST_TERMINATOR
|
PAN_TEST_TERMINATOR};
|
||||||
};
|
|
||||||
|
|
||||||
[self internalRunTests:tests];
|
[self internalRunTests:tests];
|
||||||
}
|
}
|
||||||
@@ -381,34 +400,37 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
|
|
||||||
// From https://github.com/mapbox/mapbox-gl-native/pull/13727#issuecomment-454028698
|
// From https://github.com/mapbox/mapbox-gl-native/pull/13727#issuecomment-454028698
|
||||||
//
|
//
|
||||||
// | Annotation position | Has callout? | Callout implements `marginInsets...`? | Map pans when selected with moveIntoView=YES? |
|
// | Annotation position | Has callout? | Callout implements `marginInsets...`? | Map pans when
|
||||||
|
// selected with moveIntoView=YES? |
|
||||||
// |---------------------|--------------|---------------------------------------|-----------------------------------------------|
|
// |---------------------|--------------|---------------------------------------|-----------------------------------------------|
|
||||||
// | Offscreen | Yes | No | Yes, but only to show annotation (not callout) with no margins |
|
// | Offscreen | Yes | No | Yes, but only to
|
||||||
// | Partially | Yes | No | No |
|
// show annotation (not callout) with no margins | | Partially | Yes | No | No
|
||||||
// | Onscreen | Yes | No | No |
|
// | | Onscreen | Yes | No | No |
|
||||||
//
|
//
|
||||||
|
|
||||||
PanTestData tests[] = {
|
PanTestData tests[] = {
|
||||||
// Coord showsCallout impl margins? moveIntoView expectMapToPan calloutOnScreen
|
// Coord showsCallout impl margins? moveIntoView expectMapToPan
|
||||||
|
// calloutOnScreen
|
||||||
// Offscreen
|
// Offscreen
|
||||||
{ {-1.0f, 0.5f}, YES, NO, YES, YES, NO },
|
{{-1.0f, 0.5f}, YES, NO, YES, YES, NO},
|
||||||
{ { 2.0f, 0.5f}, YES, NO, YES, YES, NO },
|
{{2.0f, 0.5f}, YES, NO, YES, YES, NO},
|
||||||
{ { 0.5f,-1.0f}, YES, NO, YES, YES, NO },
|
{{0.5f, -1.0f}, YES, NO, YES, YES, NO},
|
||||||
{ { 0.5f, 2.0f}, YES, NO, YES, YES, YES }, // Because annotation was off the bottom of screen, and callout is above annotation
|
{{0.5f, 2.0f}, YES, NO, YES, YES, YES}, // Because annotation was off the bottom of screen,
|
||||||
{ { 2.0f, 2.0f}, YES, NO, YES, YES, NO },
|
// and callout is above annotation
|
||||||
|
{{2.0f, 2.0f}, YES, NO, YES, YES, NO},
|
||||||
|
|
||||||
// Partial
|
// Partial
|
||||||
{ { 0.0f, 0.5f}, YES, NO, YES, NO, NO },
|
{{0.0f, 0.5f}, YES, NO, YES, NO, NO},
|
||||||
{ { 1.0f, 0.5f}, YES, NO, YES, NO, NO },
|
{{1.0f, 0.5f}, YES, NO, YES, NO, NO},
|
||||||
{ { 0.5f, 0.0f}, YES, NO, YES, NO, NO },
|
{{0.5f, 0.0f}, YES, NO, YES, NO, NO},
|
||||||
{ { 0.5f, 1.0f}, YES, NO, YES, NO, YES }, // Because annotation was off the bottom of screen, and callout is above annotation
|
{{0.5f, 1.0f}, YES, NO, YES, NO, YES}, // Because annotation was off the bottom of screen,
|
||||||
{ { 1.0f, 1.0f}, YES, NO, YES, NO, NO },
|
// and callout is above annotation
|
||||||
|
{{1.0f, 1.0f}, YES, NO, YES, NO, NO},
|
||||||
|
|
||||||
// Onscreen
|
// Onscreen
|
||||||
{ { 0.5f, 0.5f}, YES, NO, YES, NO, YES },
|
{{0.5f, 0.5f}, YES, NO, YES, NO, YES},
|
||||||
|
|
||||||
PAN_TEST_TERMINATOR
|
PAN_TEST_TERMINATOR};
|
||||||
};
|
|
||||||
|
|
||||||
[self internalRunTests:tests];
|
[self internalRunTests:tests];
|
||||||
}
|
}
|
||||||
@@ -416,34 +438,35 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
// MARK: - Selection with an offset
|
// MARK: - Selection with an offset
|
||||||
|
|
||||||
- (void)testSelectingAnnotationWithCenterOffset {
|
- (void)testSelectingAnnotationWithCenterOffset {
|
||||||
|
for (CGFloat dx = -100.0; dx <= 100.0; dx += 100.0) {
|
||||||
for (CGFloat dx = -100.0; dx <= 100.0; dx += 100.0 ) {
|
for (CGFloat dy = -100.0; dy <= 100.0; dy += 100.0) {
|
||||||
for (CGFloat dy = -100.0; dy <= 100.0; dy += 100.0 ) {
|
|
||||||
CGVector offset = CGVectorMake(dx, dy);
|
CGVector offset = CGVectorMake(dx, dy);
|
||||||
UIEdgeInsets edgeInsets = UIEdgeInsetsMake(fmax(-dy, 0.0), fmax(-dy, 0.0), fmax(dy, 0.0), fmax(dx, 0.0));
|
UIEdgeInsets edgeInsets =
|
||||||
|
UIEdgeInsetsMake(fmax(-dy, 0.0), fmax(-dy, 0.0), fmax(dy, 0.0), fmax(dx, 0.0));
|
||||||
[self internalTestSelectingAnnotationWithCenterOffsetWithOffset:offset edgeInsets:edgeInsets];
|
[self internalTestSelectingAnnotationWithCenterOffsetWithOffset:offset edgeInsets:edgeInsets];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)internalTestSelectingAnnotationWithCenterOffsetWithOffset:(CGVector)offset edgeInsets:(UIEdgeInsets)edgeInsets {
|
- (void)internalTestSelectingAnnotationWithCenterOffsetWithOffset:(CGVector)offset
|
||||||
|
edgeInsets:(UIEdgeInsets)edgeInsets {
|
||||||
NSString * const MLNTestAnnotationReuseIdentifer = @"MLNTestAnnotationReuseIdentifer";
|
NSString *const MLNTestAnnotationReuseIdentifer = @"MLNTestAnnotationReuseIdentifer";
|
||||||
|
|
||||||
self.mapView.contentInset = edgeInsets;
|
self.mapView.contentInset = edgeInsets;
|
||||||
CGSize size = self.mapView.bounds.size;
|
CGSize size = self.mapView.bounds.size;
|
||||||
|
|
||||||
CGSize annotationSize = CGSizeMake(40.0, 40.0);
|
CGSize annotationSize = CGSizeMake(40.0, 40.0);
|
||||||
|
|
||||||
self.viewForAnnotation = ^MLNAnnotationView*(MLNMapView *view, id<MLNAnnotation> annotation) {
|
self.viewForAnnotation = ^MLNAnnotationView *(MLNMapView *view, id<MLNAnnotation> annotation) {
|
||||||
|
|
||||||
if (![annotation isKindOfClass:[MLNPointAnnotation class]]) {
|
if (![annotation isKindOfClass:[MLNPointAnnotation class]]) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No dequeue
|
// No dequeue
|
||||||
MLNAnnotationView *annotationView = [[MLNAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:MLNTestAnnotationReuseIdentifer];
|
MLNAnnotationView *annotationView =
|
||||||
annotationView.bounds = (CGRect){ .origin = CGPointZero, .size = annotationSize };
|
[[MLNAnnotationView alloc] initWithAnnotation:annotation
|
||||||
|
reuseIdentifier:MLNTestAnnotationReuseIdentifer];
|
||||||
|
annotationView.bounds = (CGRect){.origin = CGPointZero, .size = annotationSize};
|
||||||
annotationView.backgroundColor = UIColor.redColor;
|
annotationView.backgroundColor = UIColor.redColor;
|
||||||
annotationView.enabled = YES;
|
annotationView.enabled = YES;
|
||||||
annotationView.centerOffset = offset;
|
annotationView.centerOffset = offset;
|
||||||
@@ -469,9 +492,12 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
[self waitForCollisionDetectionToRun];
|
[self waitForCollisionDetectionToRun];
|
||||||
|
|
||||||
// Check that the annotation is in the center of the view
|
// Check that the annotation is in the center of the view
|
||||||
CGPoint annotationPoint = [self.mapView convertCoordinate:point.coordinate toPointToView:self.mapView];
|
CGPoint annotationPoint = [self.mapView convertCoordinate:point.coordinate
|
||||||
XCTAssertEqualWithAccuracy(annotationPoint.x, (size.width - edgeInsets.right + edgeInsets.left)/2.0, 0.25);
|
toPointToView:self.mapView];
|
||||||
XCTAssertEqualWithAccuracy(annotationPoint.y, (size.height - edgeInsets.bottom + edgeInsets.top)/2.0, 0.25);
|
XCTAssertEqualWithAccuracy(annotationPoint.x,
|
||||||
|
(size.width - edgeInsets.right + edgeInsets.left) / 2.0, 0.25);
|
||||||
|
XCTAssertEqualWithAccuracy(annotationPoint.y,
|
||||||
|
(size.height - edgeInsets.bottom + edgeInsets.top) / 2.0, 0.25);
|
||||||
|
|
||||||
// Now test taps around the annotation
|
// Now test taps around the annotation
|
||||||
CGPoint tapPoint = CGPointMake(annotationPoint.x + offset.dx, annotationPoint.y + offset.dy);
|
CGPoint tapPoint = CGPointMake(annotationPoint.x + offset.dx, annotationPoint.y + offset.dy);
|
||||||
@@ -479,13 +505,11 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
MLNAnnotationTag tagAtPoint = [self.mapView annotationTagAtPoint:tapPoint persistingResults:YES];
|
MLNAnnotationTag tagAtPoint = [self.mapView annotationTagAtPoint:tapPoint persistingResults:YES];
|
||||||
XCTAssert(tagAtPoint != UINT32_MAX, @"Should have tapped on annotation");
|
XCTAssert(tagAtPoint != UINT32_MAX, @"Should have tapped on annotation");
|
||||||
|
|
||||||
CGPoint testPoints[] = {
|
CGPoint testPoints[] = {{tapPoint.x - annotationSize.width, tapPoint.y},
|
||||||
{ tapPoint.x - annotationSize.width, tapPoint.y },
|
{tapPoint.x + annotationSize.width, tapPoint.y},
|
||||||
{ tapPoint.x + annotationSize.width, tapPoint.y },
|
{tapPoint.x, tapPoint.y - annotationSize.height},
|
||||||
{ tapPoint.x, tapPoint.y - annotationSize.height },
|
{tapPoint.x, tapPoint.y + annotationSize.height},
|
||||||
{ tapPoint.x, tapPoint.y + annotationSize.height },
|
CGPointZero};
|
||||||
CGPointZero
|
|
||||||
};
|
|
||||||
|
|
||||||
CGPoint *testPoint = testPoints;
|
CGPoint *testPoint = testPoints;
|
||||||
|
|
||||||
@@ -510,8 +534,9 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
|
|
||||||
CGPoint offset = CGPointMake(20, 20);
|
CGPoint offset = CGPointMake(20, 20);
|
||||||
|
|
||||||
self.mapViewUserLocationAnchorPoint = ^CGPoint (MLNMapView *mapView) {
|
self.mapViewUserLocationAnchorPoint = ^CGPoint(MLNMapView *mapView) {
|
||||||
return offset;;
|
return offset;
|
||||||
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
[self.mapView setUserTrackingMode:MLNUserTrackingModeFollow animated:NO completionHandler:nil];
|
[self.mapView setUserTrackingMode:MLNUserTrackingModeFollow animated:NO completionHandler:nil];
|
||||||
@@ -524,11 +549,7 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
// MARK: - Rotating/zooming
|
// MARK: - Rotating/zooming
|
||||||
|
|
||||||
- (void)testSelectingAnnotationWhenMapIsRotated {
|
- (void)testSelectingAnnotationWhenMapIsRotated {
|
||||||
|
CLLocationCoordinate2D coordinates[] = {{40.0, 40.0}, {NAN, NAN}};
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
|
||||||
{ 40.0, 40.0 },
|
|
||||||
{ NAN, NAN }
|
|
||||||
};
|
|
||||||
|
|
||||||
NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
|
NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
|
||||||
MLNPointAnnotation *annotation = annotations.firstObject;
|
MLNPointAnnotation *annotation = annotations.firstObject;
|
||||||
@@ -537,17 +558,19 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
CLLocationDirection lastAngle = 0.0;
|
CLLocationDirection lastAngle = 0.0;
|
||||||
|
|
||||||
srand48(0);
|
srand48(0);
|
||||||
for (NSInteger iter = 0; iter < 10; iter++ ) {
|
for (NSInteger iter = 0; iter < 10; iter++) {
|
||||||
|
CLLocationDirection angle = (CLLocationDirection)((drand48() * 1080.0) - 540.0);
|
||||||
|
|
||||||
CLLocationDirection angle = (CLLocationDirection)((drand48()*1080.0) - 540.0);
|
CGPoint anchor = CGPointMake(drand48() * CGRectGetWidth(self.mapView.bounds),
|
||||||
|
drand48() * CGRectGetHeight(self.mapView.bounds));
|
||||||
|
|
||||||
CGPoint anchor = CGPointMake(drand48()*CGRectGetWidth(self.mapView.bounds), drand48()*CGRectGetHeight(self.mapView.bounds));
|
NSString *activityTitle =
|
||||||
|
[NSString stringWithFormat:@"Rotate to: %0.1f from: %0.1f", angle, lastAngle];
|
||||||
NSString *activityTitle = [NSString stringWithFormat:@"Rotate to: %0.1f from: %0.1f", angle, lastAngle];
|
|
||||||
[XCTContext runActivityNamed:activityTitle
|
[XCTContext runActivityNamed:activityTitle
|
||||||
block:^(id<XCTActivity> _Nonnull activity) {
|
block:^(id<XCTActivity> _Nonnull activity) {
|
||||||
|
MLNMapCamera *toCamera =
|
||||||
MLNMapCamera *toCamera = [self.mapView cameraByRotatingToDirection:angle aroundAnchorPoint:anchor];
|
[self.mapView cameraByRotatingToDirection:angle
|
||||||
|
aroundAnchorPoint:anchor];
|
||||||
[self internalTestSelecting:annotation withCamera:toCamera];
|
[self internalTestSelecting:annotation withCamera:toCamera];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@@ -556,66 +579,60 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testSelectingAnnotationWhenMapIsScaled {
|
- (void)testSelectingAnnotationWhenMapIsScaled {
|
||||||
|
CLLocationCoordinate2D coordinates[] = {{0.005, 0.005}, {NAN, NAN}};
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
|
||||||
{ 0.005, 0.005 },
|
|
||||||
{ NAN, NAN }
|
|
||||||
};
|
|
||||||
|
|
||||||
NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
|
NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
|
||||||
MLNPointAnnotation *annotation = annotations.firstObject;
|
MLNPointAnnotation *annotation = annotations.firstObject;
|
||||||
|
|
||||||
CGPoint anchor = CGPointMake(CGRectGetMidX(self.mapView.bounds), CGRectGetMidY(self.mapView.bounds));
|
CGPoint anchor =
|
||||||
|
CGPointMake(CGRectGetMidX(self.mapView.bounds), CGRectGetMidY(self.mapView.bounds));
|
||||||
|
|
||||||
srand48(0);
|
srand48(0);
|
||||||
for (NSInteger iter = 0; iter < 10; iter++ ) {
|
for (NSInteger iter = 0; iter < 10; iter++) {
|
||||||
|
double zoom = (double)(drand48() * 14.0);
|
||||||
double zoom = (double)(drand48()*14.0);
|
|
||||||
|
|
||||||
NSString *activityTitle = [NSString stringWithFormat:@"Zoom to %0.1f", zoom];
|
NSString *activityTitle = [NSString stringWithFormat:@"Zoom to %0.1f", zoom];
|
||||||
[XCTContext runActivityNamed:activityTitle
|
[XCTContext runActivityNamed:activityTitle
|
||||||
block:^(id<XCTActivity> _Nonnull activity) {
|
block:^(id<XCTActivity> _Nonnull activity) {
|
||||||
MLNMapCamera *toCamera = [self.mapView cameraByZoomingToZoomLevel:zoom aroundAnchorPoint:anchor];
|
MLNMapCamera *toCamera =
|
||||||
|
[self.mapView cameraByZoomingToZoomLevel:zoom
|
||||||
|
aroundAnchorPoint:anchor];
|
||||||
[self internalTestSelecting:annotation withCamera:toCamera];
|
[self internalTestSelecting:annotation withCamera:toCamera];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testSelectingAnnotationWhenMapIsScaledAndRotated {
|
- (void)testSelectingAnnotationWhenMapIsScaledAndRotated {
|
||||||
|
CLLocationCoordinate2D coordinates[] = {{0.005, 0.005}, {NAN, NAN}};
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
|
||||||
{ 0.005, 0.005 },
|
|
||||||
{ NAN, NAN }
|
|
||||||
};
|
|
||||||
|
|
||||||
NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
|
NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
|
||||||
MLNPointAnnotation *annotation = annotations.firstObject;
|
MLNPointAnnotation *annotation = annotations.firstObject;
|
||||||
|
|
||||||
srand48(0);
|
srand48(0);
|
||||||
for (NSInteger iter = 0; iter < 10; iter++ ) {
|
for (NSInteger iter = 0; iter < 10; iter++) {
|
||||||
|
double zoom = (double)(7.0 + drand48() * 7.0);
|
||||||
|
CLLocationDirection angle = (CLLocationDirection)((drand48() * 1080.0) - 540.0);
|
||||||
|
|
||||||
double zoom = (double)(7.0 + drand48()*7.0);
|
CGPoint anchor = CGPointMake(drand48() * CGRectGetWidth(self.mapView.bounds),
|
||||||
CLLocationDirection angle = (CLLocationDirection)((drand48()*1080.0) - 540.0);
|
drand48() * CGRectGetHeight(self.mapView.bounds));
|
||||||
|
|
||||||
CGPoint anchor = CGPointMake(drand48()*CGRectGetWidth(self.mapView.bounds), drand48()*CGRectGetHeight(self.mapView.bounds));
|
|
||||||
|
|
||||||
NSString *activityTitle = [NSString stringWithFormat:@"Zoom to %0.1f", zoom];
|
NSString *activityTitle = [NSString stringWithFormat:@"Zoom to %0.1f", zoom];
|
||||||
[XCTContext runActivityNamed:activityTitle
|
[XCTContext runActivityNamed:activityTitle
|
||||||
block:^(id<XCTActivity> _Nonnull activity)
|
block:^(id<XCTActivity> _Nonnull activity) {
|
||||||
{
|
|
||||||
mbgl::CameraOptions currentCameraOptions;
|
mbgl::CameraOptions currentCameraOptions;
|
||||||
|
|
||||||
currentCameraOptions.bearing = angle;
|
currentCameraOptions.bearing = angle;
|
||||||
currentCameraOptions.anchor = mbgl::ScreenCoordinate { anchor.x, anchor.y };
|
currentCameraOptions.anchor =
|
||||||
|
mbgl::ScreenCoordinate{anchor.x, anchor.y};
|
||||||
currentCameraOptions.zoom = zoom;
|
currentCameraOptions.zoom = zoom;
|
||||||
MLNMapCamera *toCamera = [self.mapView cameraForCameraOptions:currentCameraOptions];
|
MLNMapCamera *toCamera =
|
||||||
|
[self.mapView cameraForCameraOptions:currentCameraOptions];
|
||||||
|
|
||||||
[self internalTestSelecting:annotation withCamera:toCamera];
|
[self internalTestSelecting:annotation withCamera:toCamera];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)testShowingAnnotationsThenSelectingAnimated {
|
- (void)testShowingAnnotationsThenSelectingAnimated {
|
||||||
[self internalTestShowingAnnotationsThenSelectingAnimated:YES];
|
[self internalTestShowingAnnotationsThenSelectingAnimated:YES];
|
||||||
}
|
}
|
||||||
@@ -627,31 +644,34 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
- (void)internalTestShowingAnnotationsThenSelectingAnimated:(BOOL)animated {
|
- (void)internalTestShowingAnnotationsThenSelectingAnimated:(BOOL)animated {
|
||||||
srand48(0);
|
srand48(0);
|
||||||
|
|
||||||
CGFloat maxXPadding = std::max(CGRectGetWidth(self.mapView.bounds)/5.0, 100.0);
|
CGFloat maxXPadding = std::max(CGRectGetWidth(self.mapView.bounds) / 5.0, 100.0);
|
||||||
CGFloat maxYPadding = std::max(CGRectGetHeight(self.mapView.bounds)/5.0, 100.0);
|
CGFloat maxYPadding = std::max(CGRectGetHeight(self.mapView.bounds) / 5.0, 100.0);
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
UIEdgeInsets edgePadding;
|
UIEdgeInsets edgePadding;
|
||||||
edgePadding.top = floor(drand48()*maxYPadding);
|
edgePadding.top = floor(drand48() * maxYPadding);
|
||||||
edgePadding.bottom = floor(drand48()*maxYPadding);
|
edgePadding.bottom = floor(drand48() * maxYPadding);
|
||||||
edgePadding.left = floor(drand48()*maxXPadding);
|
edgePadding.left = floor(drand48() * maxXPadding);
|
||||||
edgePadding.right = floor(drand48()*maxXPadding);
|
edgePadding.right = floor(drand48() * maxXPadding);
|
||||||
|
|
||||||
UIEdgeInsets contentInsets;
|
UIEdgeInsets contentInsets;
|
||||||
contentInsets.top = floor(drand48()*maxYPadding);
|
contentInsets.top = floor(drand48() * maxYPadding);
|
||||||
contentInsets.bottom = floor(drand48()*maxYPadding);
|
contentInsets.bottom = floor(drand48() * maxYPadding);
|
||||||
contentInsets.left = floor(drand48()*maxXPadding);
|
contentInsets.left = floor(drand48() * maxXPadding);
|
||||||
contentInsets.right = floor(drand48()*maxXPadding);
|
contentInsets.right = floor(drand48() * maxXPadding);
|
||||||
|
|
||||||
[self internalTestShowingAnnotationsThenSelectingAnimated:animated edgePadding:edgePadding contentInsets:contentInsets];
|
[self internalTestShowingAnnotationsThenSelectingAnimated:animated
|
||||||
|
edgePadding:edgePadding
|
||||||
|
contentInsets:contentInsets];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)internalTestShowingAnnotationsThenSelectingAnimated:(BOOL)animated edgePadding:(UIEdgeInsets)edgeInsets contentInsets:(UIEdgeInsets)contentInsets {
|
- (void)internalTestShowingAnnotationsThenSelectingAnimated:(BOOL)animated
|
||||||
|
edgePadding:(UIEdgeInsets)edgeInsets
|
||||||
|
contentInsets:(UIEdgeInsets)contentInsets {
|
||||||
CLLocationCoordinate2D coordinates[21];
|
CLLocationCoordinate2D coordinates[21];
|
||||||
|
|
||||||
for (int i = 0; i < (int)(sizeof(coordinates)/sizeof(coordinates[0])); i++)
|
for (int i = 0; i < (int)(sizeof(coordinates) / sizeof(coordinates[0])); i++) {
|
||||||
{
|
|
||||||
coordinates[i].latitude = drand48();
|
coordinates[i].latitude = drand48();
|
||||||
coordinates[i].longitude = drand48();
|
coordinates[i].longitude = drand48();
|
||||||
}
|
}
|
||||||
@@ -669,7 +689,7 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
[showCompleted fulfill];
|
[showCompleted fulfill];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self waitForExpectations:@[showCompleted] timeout:3.5];
|
[self waitForExpectations:@[ showCompleted ] timeout:3.5];
|
||||||
|
|
||||||
// These tests will fail if this isn't here. But this isn't quite what we're
|
// These tests will fail if this isn't here. But this isn't quite what we're
|
||||||
// seeing in https://github.com/mapbox/mapbox-gl-native/issues/15106
|
// seeing in https://github.com/mapbox/mapbox-gl-native/issues/15106
|
||||||
@@ -684,27 +704,28 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
[self waitForCollisionDetectionToRun];
|
[self waitForCollisionDetectionToRun];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray*)internalAddAnnotationsAtCoordinates:(CLLocationCoordinate2D*)coordinates
|
- (NSArray *)internalAddAnnotationsAtCoordinates:(CLLocationCoordinate2D *)coordinates {
|
||||||
{
|
|
||||||
__block NSMutableArray *annotations = [NSMutableArray array];
|
__block NSMutableArray *annotations = [NSMutableArray array];
|
||||||
|
|
||||||
[XCTContext runActivityNamed:@"Map setup"
|
[XCTContext runActivityNamed:@"Map setup"
|
||||||
block:^(id<XCTActivity> _Nonnull activity)
|
block:^(id<XCTActivity> _Nonnull activity) {
|
||||||
{
|
NSString *const MLNTestAnnotationReuseIdentifer =
|
||||||
|
@"MLNTestAnnotationReuseIdentifer";
|
||||||
NSString * const MLNTestAnnotationReuseIdentifer = @"MLNTestAnnotationReuseIdentifer";
|
|
||||||
|
|
||||||
CGSize annotationSize = CGSizeMake(40.0, 40.0);
|
CGSize annotationSize = CGSizeMake(40.0, 40.0);
|
||||||
|
|
||||||
self.viewForAnnotation = ^MLNAnnotationView*(MLNMapView *view, id<MLNAnnotation> annotation2) {
|
self.viewForAnnotation = ^MLNAnnotationView *(
|
||||||
|
MLNMapView *view, id<MLNAnnotation> annotation2) {
|
||||||
if (![annotation2 isKindOfClass:[MLNPointAnnotation class]]) {
|
if (![annotation2 isKindOfClass:[MLNPointAnnotation class]]) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No dequeue
|
// No dequeue
|
||||||
MLNAnnotationView *annotationView = [[MLNAnnotationView alloc] initWithAnnotation:annotation2 reuseIdentifier:MLNTestAnnotationReuseIdentifer];
|
MLNAnnotationView *annotationView = [[MLNAnnotationView alloc]
|
||||||
annotationView.bounds = (CGRect){ .origin = CGPointZero, .size = annotationSize };
|
initWithAnnotation:annotation2
|
||||||
|
reuseIdentifier:MLNTestAnnotationReuseIdentifer];
|
||||||
|
annotationView.bounds =
|
||||||
|
(CGRect){.origin = CGPointZero, .size = annotationSize};
|
||||||
annotationView.backgroundColor = UIColor.redColor;
|
annotationView.backgroundColor = UIColor.redColor;
|
||||||
annotationView.enabled = YES;
|
annotationView.enabled = YES;
|
||||||
|
|
||||||
@@ -722,7 +743,6 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
}
|
}
|
||||||
|
|
||||||
[self.mapView addAnnotations:annotations];
|
[self.mapView addAnnotations:annotations];
|
||||||
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
NSArray *copiedAnnotations = [annotations copy];
|
NSArray *copiedAnnotations = [annotations copy];
|
||||||
@@ -731,15 +751,17 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
return copiedAnnotations;
|
return copiedAnnotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)internalTestSelecting:(MLNPointAnnotation*)point withCamera:(MLNMapCamera*)camera {
|
- (void)internalTestSelecting:(MLNPointAnnotation *)point withCamera:(MLNMapCamera *)camera {
|
||||||
|
|
||||||
// Rotate
|
// Rotate
|
||||||
XCTestExpectation *rotationCompleted = [self expectationWithDescription:@"rotationCompleted"];
|
XCTestExpectation *rotationCompleted = [self expectationWithDescription:@"rotationCompleted"];
|
||||||
[self.mapView setCamera:camera withDuration:0.1 animationTimingFunction:nil completionHandler:^{
|
[self.mapView setCamera:camera
|
||||||
|
withDuration:0.1
|
||||||
|
animationTimingFunction:nil
|
||||||
|
completionHandler:^{
|
||||||
[rotationCompleted fulfill];
|
[rotationCompleted fulfill];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self waitForExpectations:@[rotationCompleted] timeout:1.5];
|
[self waitForExpectations:@[ rotationCompleted ] timeout:1.5];
|
||||||
|
|
||||||
// Collision detection may not have completed, if not we may not get our annotation.
|
// Collision detection may not have completed, if not we may not get our annotation.
|
||||||
[self waitForCollisionDetectionToRun];
|
[self waitForCollisionDetectionToRun];
|
||||||
@@ -748,38 +770,43 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
[self internalSelectDeselectAnnotation:point];
|
[self internalSelectDeselectAnnotation:point];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)internalSelectDeselectAnnotation:(MLNPointAnnotation*)point {
|
- (void)internalSelectDeselectAnnotation:(MLNPointAnnotation *)point {
|
||||||
[XCTContext runActivityNamed:[NSString stringWithFormat:@"Select annotation: %@", point]
|
[XCTContext
|
||||||
block:^(id<XCTActivity> _Nonnull activity)
|
runActivityNamed:[NSString stringWithFormat:@"Select annotation: %@", point]
|
||||||
{
|
block:^(id<XCTActivity> _Nonnull activity) {
|
||||||
CGPoint annotationPoint = [self.mapView convertCoordinate:point.coordinate toPointToView:self.mapView];
|
CGPoint annotationPoint = [self.mapView convertCoordinate:point.coordinate
|
||||||
|
toPointToView:self.mapView];
|
||||||
|
|
||||||
MLNAnnotationTag tagAtPoint = [self.mapView annotationTagAtPoint:annotationPoint persistingResults:YES];
|
MLNAnnotationTag tagAtPoint = [self.mapView annotationTagAtPoint:annotationPoint
|
||||||
if (tagAtPoint != UINT32_MAX)
|
persistingResults:YES];
|
||||||
{
|
if (tagAtPoint != UINT32_MAX) {
|
||||||
id <MLNAnnotation> annotation = [self.mapView annotationWithTag:tagAtPoint];
|
id<MLNAnnotation> annotation = [self.mapView annotationWithTag:tagAtPoint];
|
||||||
XCTAssertNotNil(annotation);
|
XCTAssertNotNil(annotation);
|
||||||
|
|
||||||
// Select
|
// Select
|
||||||
XCTestExpectation *selectionCompleted = [self expectationWithDescription:@"Selection completed"];
|
XCTestExpectation *selectionCompleted =
|
||||||
[self.mapView selectAnnotation:annotation moveIntoView:NO animateSelection:NO completionHandler:^{
|
[self expectationWithDescription:@"Selection completed"];
|
||||||
|
[self.mapView selectAnnotation:annotation
|
||||||
|
moveIntoView:NO
|
||||||
|
animateSelection:NO
|
||||||
|
completionHandler:^{
|
||||||
[selectionCompleted fulfill];
|
[selectionCompleted fulfill];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self waitForExpectations:@[selectionCompleted] timeout:0.05];
|
[self waitForExpectations:@[ selectionCompleted ] timeout:0.05];
|
||||||
|
|
||||||
XCTAssert(self.mapView.selectedAnnotations.count == 1, @"There should only be 1 selected annotation");
|
XCTAssert(self.mapView.selectedAnnotations.count == 1,
|
||||||
XCTAssertEqualObjects(self.mapView.selectedAnnotations.firstObject, annotation, @"The annotation should be selected");
|
@"There should only be 1 selected annotation");
|
||||||
|
XCTAssertEqualObjects(self.mapView.selectedAnnotations.firstObject, annotation,
|
||||||
|
@"The annotation should be selected");
|
||||||
|
|
||||||
// Deselect
|
// Deselect
|
||||||
[self.mapView deselectAnnotation:annotation animated:NO];
|
[self.mapView deselectAnnotation:annotation animated:NO];
|
||||||
}
|
} else {
|
||||||
else
|
XCTFail(@"Should be an annotation at this point: %@",
|
||||||
{
|
NSStringFromCGPoint(annotationPoint));
|
||||||
XCTFail(@"Should be an annotation at this point: %@", NSStringFromCGPoint(annotationPoint));
|
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Utilities
|
// MARK: - Utilities
|
||||||
@@ -802,7 +829,7 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
[timerExpired fulfill];
|
[timerExpired fulfill];
|
||||||
});
|
});
|
||||||
|
|
||||||
[self waitForExpectations:@[timerExpired] timeout:seconds + 1.0];
|
[self waitForExpectations:@[ timerExpired ] timeout:seconds + 1.0];
|
||||||
[timer invalidate];
|
[timer invalidate];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,11 +841,12 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
|
|||||||
XCTestExpectation *timerExpired = [self expectationWithDescription:@"Timer expires"];
|
XCTestExpectation *timerExpired = [self expectationWithDescription:@"Timer expires"];
|
||||||
|
|
||||||
// Wait 1/2 second
|
// Wait 1/2 second
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC >> 1)), dispatch_get_main_queue(), ^{
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC >> 1)),
|
||||||
|
dispatch_get_main_queue(), ^{
|
||||||
[timerExpired fulfill];
|
[timerExpired fulfill];
|
||||||
});
|
});
|
||||||
|
|
||||||
[self waitForExpectations:@[timerExpired, self.renderFinishedExpectation] timeout:5];
|
[self waitForExpectations:@[ timerExpired, self.renderFinishedExpectation ] timeout:5];
|
||||||
|
|
||||||
self.renderFinishedExpectation = nil;
|
self.renderFinishedExpectation = nil;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
#import "../../../darwin/src/MLNGeometry_Private.h"
|
||||||
#import "MLNMapViewIntegrationTest.h"
|
#import "MLNMapViewIntegrationTest.h"
|
||||||
#import "MLNTestUtility.h"
|
#import "MLNTestUtility.h"
|
||||||
#import "../../../darwin/src/MLNGeometry_Private.h"
|
|
||||||
|
|
||||||
#include <mbgl/map/camera.hpp>
|
#include <mbgl/map/camera.hpp>
|
||||||
|
|
||||||
@@ -10,12 +10,12 @@
|
|||||||
@implementation MLNCameraTransitionFinishTests
|
@implementation MLNCameraTransitionFinishTests
|
||||||
|
|
||||||
- (void)testEaseToCompletionHandler {
|
- (void)testEaseToCompletionHandler {
|
||||||
|
|
||||||
MLNCoordinateBounds bounds = MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
|
MLNCoordinateBounds bounds = MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
|
||||||
CLLocationCoordinate2DMake(1.0, 1.0));
|
CLLocationCoordinate2DMake(1.0, 1.0));
|
||||||
MLNMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
|
MLNMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
|
||||||
|
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Completion block should be called"];
|
XCTestExpectation *expectation =
|
||||||
|
[self expectationWithDescription:@"Completion block should be called"];
|
||||||
|
|
||||||
[self.mapView setCamera:camera
|
[self.mapView setCamera:camera
|
||||||
withDuration:0.0
|
withDuration:0.0
|
||||||
@@ -24,16 +24,16 @@
|
|||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self waitForExpectations:@[expectation] timeout:0.5];
|
[self waitForExpectations:@[ expectation ] timeout:0.5];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testEaseToCompletionHandlerAnimated {
|
- (void)testEaseToCompletionHandlerAnimated {
|
||||||
|
|
||||||
MLNCoordinateBounds bounds = MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
|
MLNCoordinateBounds bounds = MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
|
||||||
CLLocationCoordinate2DMake(1.0, 1.0));
|
CLLocationCoordinate2DMake(1.0, 1.0));
|
||||||
MLNMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
|
MLNMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
|
||||||
|
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Completion block should be called"];
|
XCTestExpectation *expectation =
|
||||||
|
[self expectationWithDescription:@"Completion block should be called"];
|
||||||
|
|
||||||
[self.mapView setCamera:camera
|
[self.mapView setCamera:camera
|
||||||
withDuration:0.3
|
withDuration:0.3
|
||||||
@@ -42,16 +42,16 @@
|
|||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self waitForExpectations:@[expectation] timeout:0.5];
|
[self waitForExpectations:@[ expectation ] timeout:0.5];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testFlyToCompletionHandler {
|
- (void)testFlyToCompletionHandler {
|
||||||
|
|
||||||
MLNCoordinateBounds bounds = MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
|
MLNCoordinateBounds bounds = MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
|
||||||
CLLocationCoordinate2DMake(1.0, 1.0));
|
CLLocationCoordinate2DMake(1.0, 1.0));
|
||||||
MLNMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
|
MLNMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
|
||||||
|
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Completion block should be called"];
|
XCTestExpectation *expectation =
|
||||||
|
[self expectationWithDescription:@"Completion block should be called"];
|
||||||
|
|
||||||
[self.mapView flyToCamera:camera
|
[self.mapView flyToCamera:camera
|
||||||
withDuration:0.0
|
withDuration:0.0
|
||||||
@@ -59,16 +59,16 @@
|
|||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self waitForExpectations:@[expectation] timeout:0.5];
|
[self waitForExpectations:@[ expectation ] timeout:0.5];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testFlyToCompletionHandlerAnimated {
|
- (void)testFlyToCompletionHandlerAnimated {
|
||||||
|
|
||||||
MLNCoordinateBounds bounds = MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
|
MLNCoordinateBounds bounds = MLNCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
|
||||||
CLLocationCoordinate2DMake(1.0, 1.0));
|
CLLocationCoordinate2DMake(1.0, 1.0));
|
||||||
MLNMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
|
MLNMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
|
||||||
|
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Completion block should be called"];
|
XCTestExpectation *expectation =
|
||||||
|
[self expectationWithDescription:@"Completion block should be called"];
|
||||||
|
|
||||||
[self.mapView flyToCamera:camera
|
[self.mapView flyToCamera:camera
|
||||||
withDuration:0.3
|
withDuration:0.3
|
||||||
@@ -76,22 +76,25 @@
|
|||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self waitForExpectations:@[expectation] timeout:0.5];
|
[self waitForExpectations:@[ expectation ] timeout:0.5];
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
// MARK: - camera transitions with NaN values
|
// MARK: - camera transitions with NaN values
|
||||||
|
|
||||||
@interface MLNMapView (MLNCameraTransitionFinishNaNTests)
|
@interface MLNMapView (MLNCameraTransitionFinishNaNTests)
|
||||||
- (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MLNMapCamera *)camera edgePadding:(UIEdgeInsets)insets;
|
- (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MLNMapCamera *)camera
|
||||||
|
edgePadding:(UIEdgeInsets)insets;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MLNCameraTransitionNaNZoomMapView: MLNMapView
|
@interface MLNCameraTransitionNaNZoomMapView : MLNMapView
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MLNCameraTransitionNaNZoomMapView
|
@implementation MLNCameraTransitionNaNZoomMapView
|
||||||
- (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MLNMapCamera *)camera edgePadding:(UIEdgeInsets)insets {
|
- (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MLNMapCamera *)camera
|
||||||
mbgl::CameraOptions options = [super cameraOptionsObjectForAnimatingToCamera:camera edgePadding:insets];
|
edgePadding:(UIEdgeInsets)insets {
|
||||||
|
mbgl::CameraOptions options = [super cameraOptionsObjectForAnimatingToCamera:camera
|
||||||
|
edgePadding:insets];
|
||||||
options.zoom = NAN;
|
options.zoom = NAN;
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
#import "../../../darwin/src/MLNGeometry_Private.h"
|
||||||
#import "MLNMapViewIntegrationTest.h"
|
#import "MLNMapViewIntegrationTest.h"
|
||||||
#import "MLNTestUtility.h"
|
#import "MLNTestUtility.h"
|
||||||
#import "../../../darwin/src/MLNGeometry_Private.h"
|
|
||||||
|
|
||||||
@interface MLNCameraTransitionTests : MLNMapViewIntegrationTest
|
@interface MLNCameraTransitionTests : MLNMapViewIntegrationTest
|
||||||
@end
|
@end
|
||||||
@@ -8,7 +8,6 @@
|
|||||||
@implementation MLNCameraTransitionTests
|
@implementation MLNCameraTransitionTests
|
||||||
|
|
||||||
- (void)testSetAndResetNorthWithDispatchAsyncInDelegateMethod {
|
- (void)testSetAndResetNorthWithDispatchAsyncInDelegateMethod {
|
||||||
|
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"];
|
XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"];
|
||||||
expectation.expectedFulfillmentCount = 2;
|
expectation.expectedFulfillmentCount = 2;
|
||||||
expectation.assertForOverFulfill = YES;
|
expectation.assertForOverFulfill = YES;
|
||||||
@@ -16,7 +15,6 @@
|
|||||||
__weak MLNCameraTransitionTests *weakself = self;
|
__weak MLNCameraTransitionTests *weakself = self;
|
||||||
|
|
||||||
self.regionDidChange = ^(MLNMapView *mapView, MLNCameraChangeReason reason, BOOL animated) {
|
self.regionDidChange = ^(MLNMapView *mapView, MLNCameraChangeReason reason, BOOL animated) {
|
||||||
|
|
||||||
MLNCameraTransitionTests *strongSelf = weakself;
|
MLNCameraTransitionTests *strongSelf = weakself;
|
||||||
|
|
||||||
if (!strongSelf) return;
|
if (!strongSelf) return;
|
||||||
@@ -34,12 +32,10 @@
|
|||||||
[self.mapView setDirection:90 animated:YES];
|
[self.mapView setDirection:90 animated:YES];
|
||||||
|
|
||||||
// loop, render, and wait
|
// loop, render, and wait
|
||||||
[self waitForExpectations:@[expectation] timeout:10];
|
[self waitForExpectations:@[ expectation ] timeout:10];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)testSetAndResetNorthInDelegateMethod {
|
- (void)testSetAndResetNorthInDelegateMethod {
|
||||||
|
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"];
|
XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"];
|
||||||
expectation.expectedFulfillmentCount = 2;
|
expectation.expectedFulfillmentCount = 2;
|
||||||
expectation.assertForOverFulfill = YES;
|
expectation.assertForOverFulfill = YES;
|
||||||
@@ -47,7 +43,6 @@
|
|||||||
__weak MLNCameraTransitionTests *weakself = self;
|
__weak MLNCameraTransitionTests *weakself = self;
|
||||||
|
|
||||||
self.regionDidChange = ^(MLNMapView *mapView, MLNCameraChangeReason reason, BOOL animated) {
|
self.regionDidChange = ^(MLNMapView *mapView, MLNCameraChangeReason reason, BOOL animated) {
|
||||||
|
|
||||||
MLNCameraTransitionTests *strongSelf = weakself;
|
MLNCameraTransitionTests *strongSelf = weakself;
|
||||||
|
|
||||||
if (!strongSelf) return;
|
if (!strongSelf) return;
|
||||||
@@ -62,11 +57,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
[self.mapView setDirection:90 animated:YES];
|
[self.mapView setDirection:90 animated:YES];
|
||||||
[self waitForExpectations:@[expectation] timeout:10];
|
[self waitForExpectations:@[ expectation ] timeout:10];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testInterruptingAndResetNorthOnlyOnceInIsChanging {
|
- (void)testInterruptingAndResetNorthOnlyOnceInIsChanging {
|
||||||
|
|
||||||
// Reset to non-zero, prior to testing
|
// Reset to non-zero, prior to testing
|
||||||
[self.mapView setDirection:45 animated:NO];
|
[self.mapView setDirection:45 animated:NO];
|
||||||
|
|
||||||
@@ -99,20 +93,21 @@
|
|||||||
if (finishedReset) {
|
if (finishedReset) {
|
||||||
MLNTestAssert(strongSelf, !(reason & MLNCameraChangeReasonTransitionCancelled));
|
MLNTestAssert(strongSelf, !(reason & MLNCameraChangeReasonTransitionCancelled));
|
||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
MLNTestAssert(strongSelf, reason & MLNCameraChangeReasonTransitionCancelled);
|
MLNTestAssert(strongSelf, reason & MLNCameraChangeReasonTransitionCancelled);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
[self.mapView setDirection:90 animated:YES];
|
[self.mapView setDirection:90 animated:YES];
|
||||||
[self waitForExpectations:@[expectation] timeout:10];
|
[self waitForExpectations:@[ expectation ] timeout:10];
|
||||||
|
|
||||||
XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001, @"Camera should have reset to north. %0.3f", self.mapView.direction);
|
XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001,
|
||||||
|
@"Camera should have reset to north. %0.3f", self.mapView.direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testSetCenterCancelsTransitions {
|
- (void)testSetCenterCancelsTransitions {
|
||||||
XCTestExpectation *cameraIsInDCExpectation = [self expectationWithDescription:@"camera reset to DC"];
|
XCTestExpectation *cameraIsInDCExpectation =
|
||||||
|
[self expectationWithDescription:@"camera reset to DC"];
|
||||||
|
|
||||||
CLLocationCoordinate2D dc = CLLocationCoordinate2DMake(38.894368, -77.036487);
|
CLLocationCoordinate2D dc = CLLocationCoordinate2DMake(38.894368, -77.036487);
|
||||||
CLLocationCoordinate2D dc_west = CLLocationCoordinate2DMake(38.894368, -77.076487);
|
CLLocationCoordinate2D dc_west = CLLocationCoordinate2DMake(38.894368, -77.076487);
|
||||||
@@ -124,30 +119,24 @@
|
|||||||
|
|
||||||
__weak MLNCameraTransitionTests *weakself = self;
|
__weak MLNCameraTransitionTests *weakself = self;
|
||||||
|
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.15 * NSEC_PER_SEC),
|
dispatch_after(
|
||||||
dispatch_get_main_queue(),
|
dispatch_time(DISPATCH_TIME_NOW, 0.15 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
||||||
^{
|
|
||||||
MLNCameraTransitionTests *strongSelf = weakself;
|
MLNCameraTransitionTests *strongSelf = weakself;
|
||||||
|
|
||||||
[strongSelf.mapView setCenterCoordinate:dc zoomLevel:zoomLevel animated:NO];
|
[strongSelf.mapView setCenterCoordinate:dc zoomLevel:zoomLevel animated:NO];
|
||||||
MLNTestAssertEqualWithAccuracy(strongSelf,
|
MLNTestAssertEqualWithAccuracy(strongSelf, dc.latitude,
|
||||||
dc.latitude,
|
strongSelf.mapView.centerCoordinate.latitude, 0.0005,
|
||||||
strongSelf.mapView.centerCoordinate.latitude,
|
|
||||||
0.0005,
|
|
||||||
@"setting center coordinate should cancel transitions");
|
@"setting center coordinate should cancel transitions");
|
||||||
MLNTestAssertEqualWithAccuracy(strongSelf,
|
MLNTestAssertEqualWithAccuracy(strongSelf, dc.longitude,
|
||||||
dc.longitude,
|
strongSelf.mapView.centerCoordinate.longitude, 0.0005,
|
||||||
strongSelf.mapView.centerCoordinate.longitude,
|
|
||||||
0.0005,
|
|
||||||
@"setting center coordinate should cancel transitions");
|
@"setting center coordinate should cancel transitions");
|
||||||
[cameraIsInDCExpectation fulfill];
|
[cameraIsInDCExpectation fulfill];
|
||||||
});
|
});
|
||||||
|
|
||||||
[self waitForExpectations:@[cameraIsInDCExpectation] timeout:10.0];
|
[self waitForExpectations:@[ cameraIsInDCExpectation ] timeout:10.0];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testSetCenterCoordinateInDelegateMethod {
|
- (void)testSetCenterCoordinateInDelegateMethod {
|
||||||
|
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"];
|
XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"];
|
||||||
expectation.expectedFulfillmentCount = 2;
|
expectation.expectedFulfillmentCount = 2;
|
||||||
expectation.assertForOverFulfill = YES;
|
expectation.assertForOverFulfill = YES;
|
||||||
@@ -159,7 +148,6 @@
|
|||||||
CLLocationCoordinate2D target2 = CLLocationCoordinate2DMake(-40.0, -40.0);
|
CLLocationCoordinate2D target2 = CLLocationCoordinate2DMake(-40.0, -40.0);
|
||||||
|
|
||||||
self.regionDidChange = ^(MLNMapView *mapView, MLNCameraChangeReason reason, BOOL animated) {
|
self.regionDidChange = ^(MLNMapView *mapView, MLNCameraChangeReason reason, BOOL animated) {
|
||||||
|
|
||||||
MLNCameraTransitionTests *strongSelf = weakself;
|
MLNCameraTransitionTests *strongSelf = weakself;
|
||||||
|
|
||||||
if (!strongSelf) return;
|
if (!strongSelf) return;
|
||||||
@@ -168,21 +156,14 @@
|
|||||||
|
|
||||||
CLLocationCoordinate2D center = mapView.centerCoordinate;
|
CLLocationCoordinate2D center = mapView.centerCoordinate;
|
||||||
|
|
||||||
switch(delegateCallCount) {
|
switch (delegateCallCount) {
|
||||||
case 0:
|
case 0: {
|
||||||
{
|
|
||||||
// Our center coordinate should match our target (assuming we're not
|
// Our center coordinate should match our target (assuming we're not
|
||||||
// constrained by zoom level)
|
// constrained by zoom level)
|
||||||
MLNTestAssertEqualWithAccuracy(strongSelf,
|
MLNTestAssertEqualWithAccuracy(strongSelf, target.longitude, center.longitude, 0.0005,
|
||||||
target.longitude,
|
|
||||||
center.longitude,
|
|
||||||
0.0005,
|
|
||||||
@"center coordinate longitude should be at target");
|
@"center coordinate longitude should be at target");
|
||||||
|
|
||||||
MLNTestAssertEqualWithAccuracy(strongSelf,
|
MLNTestAssertEqualWithAccuracy(strongSelf, target.latitude, center.latitude, 0.0005,
|
||||||
target.latitude,
|
|
||||||
center.latitude,
|
|
||||||
0.0005,
|
|
||||||
@"center coordinate latitude should be at target");
|
@"center coordinate latitude should be at target");
|
||||||
|
|
||||||
// Now set another coordinate.
|
// Now set another coordinate.
|
||||||
@@ -191,23 +172,15 @@
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 1:
|
case 1: {
|
||||||
{
|
|
||||||
// Our center coordinate should match our target (assuming we're not
|
// Our center coordinate should match our target (assuming we're not
|
||||||
// constrained by zoom level)
|
// constrained by zoom level)
|
||||||
MLNTestAssertEqualWithAccuracy(strongSelf,
|
MLNTestAssertEqualWithAccuracy(strongSelf, target2.longitude, center.longitude, 0.0005,
|
||||||
target2.longitude,
|
|
||||||
center.longitude,
|
|
||||||
0.0005,
|
|
||||||
@"center coordinate longitude should be at target2");
|
@"center coordinate longitude should be at target2");
|
||||||
|
|
||||||
MLNTestAssertEqualWithAccuracy(strongSelf,
|
MLNTestAssertEqualWithAccuracy(strongSelf, target2.latitude, center.latitude, 0.0005,
|
||||||
target2.latitude,
|
|
||||||
center.latitude,
|
|
||||||
0.0005,
|
|
||||||
@"center coordinate latitude should be at target2");
|
@"center coordinate latitude should be at target2");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -222,11 +195,10 @@
|
|||||||
|
|
||||||
// Should take MLNAnimationDuration seconds (0.3)
|
// Should take MLNAnimationDuration seconds (0.3)
|
||||||
[self.mapView setCenterCoordinate:target zoomLevel:15.0 animated:YES];
|
[self.mapView setCenterCoordinate:target zoomLevel:15.0 animated:YES];
|
||||||
[self waitForExpectations:@[expectation] timeout:10];
|
[self waitForExpectations:@[ expectation ] timeout:10];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testFlyToCameraInDelegateMethod {
|
- (void)testFlyToCameraInDelegateMethod {
|
||||||
|
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"];
|
XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"];
|
||||||
|
|
||||||
__weak MLNCameraTransitionTests *weakself = self;
|
__weak MLNCameraTransitionTests *weakself = self;
|
||||||
@@ -244,10 +216,10 @@
|
|||||||
__block NSTimeInterval stop2 = 0.0;
|
__block NSTimeInterval stop2 = 0.0;
|
||||||
|
|
||||||
double zoomLevel = 5.0;
|
double zoomLevel = 5.0;
|
||||||
double altitude = MLNAltitudeForZoomLevel(zoomLevel, 0.0, target.latitude, self.mapView.frame.size);
|
double altitude =
|
||||||
|
MLNAltitudeForZoomLevel(zoomLevel, 0.0, target.latitude, self.mapView.frame.size);
|
||||||
|
|
||||||
self.regionDidChange = ^(MLNMapView *mapView, MLNCameraChangeReason reason, BOOL animated) {
|
self.regionDidChange = ^(MLNMapView *mapView, MLNCameraChangeReason reason, BOOL animated) {
|
||||||
|
|
||||||
MLNCameraTransitionTests *strongSelf = weakself;
|
MLNCameraTransitionTests *strongSelf = weakself;
|
||||||
|
|
||||||
if (!strongSelf) return;
|
if (!strongSelf) return;
|
||||||
@@ -256,23 +228,16 @@
|
|||||||
|
|
||||||
CLLocationCoordinate2D center = mapView.centerCoordinate;
|
CLLocationCoordinate2D center = mapView.centerCoordinate;
|
||||||
|
|
||||||
switch(delegateCallCount) {
|
switch (delegateCallCount) {
|
||||||
case 0:
|
case 0: {
|
||||||
{
|
|
||||||
stop1 = CACurrentMediaTime();
|
stop1 = CACurrentMediaTime();
|
||||||
|
|
||||||
// Our center coordinate should match our target (assuming we're not
|
// Our center coordinate should match our target (assuming we're not
|
||||||
// constrained by zoom level)
|
// constrained by zoom level)
|
||||||
MLNTestAssertEqualWithAccuracy(strongSelf,
|
MLNTestAssertEqualWithAccuracy(strongSelf, target.longitude, center.longitude, 0.0005,
|
||||||
target.longitude,
|
|
||||||
center.longitude,
|
|
||||||
0.0005,
|
|
||||||
@"center coordinate longitude should be at target");
|
@"center coordinate longitude should be at target");
|
||||||
|
|
||||||
MLNTestAssertEqualWithAccuracy(strongSelf,
|
MLNTestAssertEqualWithAccuracy(strongSelf, target.latitude, center.latitude, 0.0005,
|
||||||
target.latitude,
|
|
||||||
center.latitude,
|
|
||||||
0.0005,
|
|
||||||
@"center coordinate latitude should be at target");
|
@"center coordinate latitude should be at target");
|
||||||
|
|
||||||
// Now set another coordinate.
|
// Now set another coordinate.
|
||||||
@@ -282,28 +247,23 @@
|
|||||||
heading:0.0];
|
heading:0.0];
|
||||||
|
|
||||||
// flyToCamera can take a while...
|
// flyToCamera can take a while...
|
||||||
[mapView flyToCamera:camera completionHandler:^{
|
[mapView flyToCamera:camera
|
||||||
MLNTestAssert(strongSelf, !runloop, @"Completion block should be called after delegate method");
|
completionHandler:^{
|
||||||
|
MLNTestAssert(strongSelf, !runloop,
|
||||||
|
@"Completion block should be called after delegate method");
|
||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
stop2 = CACurrentMediaTime();
|
stop2 = CACurrentMediaTime();
|
||||||
}];
|
}];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 1:
|
case 1: {
|
||||||
{
|
|
||||||
// Our center coordinate should match our target (assuming we're not
|
// Our center coordinate should match our target (assuming we're not
|
||||||
// constrained by zoom level)
|
// constrained by zoom level)
|
||||||
MLNTestAssertEqualWithAccuracy(strongSelf,
|
MLNTestAssertEqualWithAccuracy(strongSelf, target2.longitude, center.longitude, 0.0005,
|
||||||
target2.longitude,
|
|
||||||
center.longitude,
|
|
||||||
0.0005,
|
|
||||||
@"center coordinate longitude should be at target2");
|
@"center coordinate longitude should be at target2");
|
||||||
|
|
||||||
MLNTestAssertEqualWithAccuracy(strongSelf,
|
MLNTestAssertEqualWithAccuracy(strongSelf, target2.latitude, center.latitude, 0.0005,
|
||||||
target2.latitude,
|
|
||||||
center.latitude,
|
|
||||||
0.0005,
|
|
||||||
@"center coordinate latitude should be at target2");
|
@"center coordinate latitude should be at target2");
|
||||||
|
|
||||||
runloop = NO;
|
runloop = NO;
|
||||||
@@ -323,17 +283,18 @@
|
|||||||
// Should take MLNAnimationDuration
|
// Should take MLNAnimationDuration
|
||||||
[self.mapView setCenterCoordinate:target zoomLevel:zoomLevel animated:YES];
|
[self.mapView setCenterCoordinate:target zoomLevel:zoomLevel animated:YES];
|
||||||
|
|
||||||
[self waitForExpectations:@[expectation] timeout:10];
|
[self waitForExpectations:@[ expectation ] timeout:10];
|
||||||
|
|
||||||
NSLog(@"setCenterCoordinate: %0.4fs", stop1 - stop0);
|
NSLog(@"setCenterCoordinate: %0.4fs", stop1 - stop0);
|
||||||
NSLog(@"flyToCamera: %0.4fs", stop2 - stop1);
|
NSLog(@"flyToCamera: %0.4fs", stop2 - stop1);
|
||||||
|
|
||||||
XCTAssert(delegateCallCount == 2, @"Expecting 2 regionDidChange callbacks, got %ld", (long)delegateCallCount); // Once for the setDirection and once for the reset north
|
XCTAssert(delegateCallCount == 2, @"Expecting 2 regionDidChange callbacks, got %ld",
|
||||||
|
(long)delegateCallCount); // Once for the setDirection and once for the reset north
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Pending tests
|
// MARK: - Pending tests
|
||||||
|
|
||||||
- (void)testContinuallyResettingNorthInIsChangingPENDING{
|
- (void)testContinuallyResettingNorthInIsChangingPENDING {
|
||||||
// See https://github.com/mapbox/mapbox-gl-native/pull/11614
|
// See https://github.com/mapbox/mapbox-gl-native/pull/11614
|
||||||
// This test currently fails, unsurprisingly, since we're continually
|
// This test currently fails, unsurprisingly, since we're continually
|
||||||
// setting the camera to the same parameters during its update.
|
// setting the camera to the same parameters during its update.
|
||||||
@@ -360,9 +321,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
[self.mapView setDirection:90 animated:YES];
|
[self.mapView setDirection:90 animated:YES];
|
||||||
[self waitForExpectations:@[expectation] timeout:10];
|
[self waitForExpectations:@[ expectation ] timeout:10];
|
||||||
|
|
||||||
XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001, @"Camera should have reset to north. %0.3f", self.mapView.direction);
|
XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001,
|
||||||
|
@"Camera should have reset to north. %0.3f", self.mapView.direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testContinuallySettingCoordinateInIsChangingPENDING {
|
- (void)testContinuallySettingCoordinateInIsChangingPENDING {
|
||||||
@@ -386,9 +348,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(40.0, 40.0) animated:YES];
|
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(40.0, 40.0) animated:YES];
|
||||||
[self waitForExpectations:@[expectation] timeout:10];
|
[self waitForExpectations:@[ expectation ] timeout:10];
|
||||||
|
|
||||||
XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001, @"Camera should have reset to north. %0.3f", self.mapView.direction);
|
XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001,
|
||||||
|
@"Camera should have reset to north. %0.3f", self.mapView.direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
#import "MLNMapView+Impl.h"
|
||||||
#import "MLNMapViewIntegrationTest.h"
|
#import "MLNMapViewIntegrationTest.h"
|
||||||
#import "MLNMapView_Private.h"
|
#import "MLNMapView_Private.h"
|
||||||
#import "MLNMapView+Impl.h"
|
|
||||||
|
|
||||||
#include <mbgl/gfx/renderable.hpp>
|
#include <mbgl/gfx/renderable.hpp>
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
- (void)testAddingRemovingOpenGLLayerWithoutRendering {
|
- (void)testAddingRemovingOpenGLLayerWithoutRendering {
|
||||||
XCTAssertNotNil(self.style);
|
XCTAssertNotNil(self.style);
|
||||||
|
|
||||||
void(^addRemoveGLLayer)(void) = ^{
|
void (^addRemoveGLLayer)(void) = ^{
|
||||||
__weak id weakLayer = nil;
|
__weak id weakLayer = nil;
|
||||||
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
@@ -83,8 +83,7 @@
|
|||||||
- (void)testAddingRemovingOpenGLLayer {
|
- (void)testAddingRemovingOpenGLLayer {
|
||||||
XCTAssertNotNil(self.style);
|
XCTAssertNotNil(self.style);
|
||||||
|
|
||||||
void(^addRemoveGLLayer)(void) = ^{
|
void (^addRemoveGLLayer)(void) = ^{
|
||||||
|
|
||||||
__weak id retrievedLayer = nil;
|
__weak id retrievedLayer = nil;
|
||||||
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
@@ -163,12 +162,15 @@
|
|||||||
|
|
||||||
MLNStyleLayer *layer2 = [self.mapView.style layerWithIdentifier:@"gl-layer"];
|
MLNStyleLayer *layer2 = [self.mapView.style layerWithIdentifier:@"gl-layer"];
|
||||||
|
|
||||||
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
|
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner"
|
||||||
self.styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
|
withExtension:@"json"];
|
||||||
|
self.styleLoadingExpectation =
|
||||||
|
[self expectationWithDescription:@"Map view should finish loading style."];
|
||||||
[self.mapView setStyleURL:styleURL];
|
[self.mapView setStyleURL:styleURL];
|
||||||
[self waitForExpectations:@[self.styleLoadingExpectation] timeout:10];
|
[self waitForExpectations:@[ self.styleLoadingExpectation ] timeout:10];
|
||||||
|
|
||||||
// At this point the C++ CustomLayer will have been destroyed, and the rawLayer pointer has been NULLed
|
// At this point the C++ CustomLayer will have been destroyed, and the rawLayer pointer has been
|
||||||
|
// NULLed
|
||||||
XCTAssert(weakLayer == layer2);
|
XCTAssert(weakLayer == layer2);
|
||||||
XCTAssertNotNil(weakLayer);
|
XCTAssertNotNil(weakLayer);
|
||||||
|
|
||||||
@@ -177,19 +179,20 @@
|
|||||||
XCTAssertNil(layer3);
|
XCTAssertNil(layer3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)testOpenGLLayerDoesNotLeakWhenMapViewDeallocs {
|
- (void)testOpenGLLayerDoesNotLeakWhenMapViewDeallocs {
|
||||||
__weak id weakLayer;
|
__weak id weakLayer;
|
||||||
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
|
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner"
|
||||||
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
|
withExtension:@"json"];
|
||||||
MLNMapView *mapView2 = [[MLNMapView alloc] initWithFrame:UIScreen.mainScreen.bounds styleURL:styleURL];
|
MLNMapView *mapView2 = [[MLNMapView alloc] initWithFrame:UIScreen.mainScreen.bounds
|
||||||
|
styleURL:styleURL];
|
||||||
mapView2.delegate = self;
|
mapView2.delegate = self;
|
||||||
|
|
||||||
XCTAssertNil(mapView2.style);
|
XCTAssertNil(mapView2.style);
|
||||||
|
|
||||||
self.styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
|
self.styleLoadingExpectation =
|
||||||
|
[self expectationWithDescription:@"Map view should finish loading style."];
|
||||||
[self waitForExpectationsWithTimeout:10 handler:nil];
|
[self waitForExpectationsWithTimeout:10 handler:nil];
|
||||||
|
|
||||||
MLNCustomStyleLayer *layer = [[MLNCustomStyleLayer alloc] initWithIdentifier:@"gl-layer"];
|
MLNCustomStyleLayer *layer = [[MLNCustomStyleLayer alloc] initWithIdentifier:@"gl-layer"];
|
||||||
@@ -203,47 +206,51 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMLNMapViewImplHasCorrectSize {
|
- (void)testMLNMapViewImplHasCorrectSize {
|
||||||
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
|
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner"
|
||||||
self.styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
|
withExtension:@"json"];
|
||||||
|
self.styleLoadingExpectation =
|
||||||
|
[self expectationWithDescription:@"Map view should finish loading style."];
|
||||||
[self.mapView setStyleURL:styleURL];
|
[self.mapView setStyleURL:styleURL];
|
||||||
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(9.6315313, 52.4133574) animated:NO];
|
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(9.6315313, 52.4133574) animated:NO];
|
||||||
[self waitForExpectations:@[self.styleLoadingExpectation] timeout:1];
|
[self waitForExpectations:@[ self.styleLoadingExpectation ] timeout:1];
|
||||||
|
|
||||||
MLNMapViewImpl *mapViewImpl = [self.mapView viewImpl];
|
MLNMapViewImpl *mapViewImpl = [self.mapView viewImpl];
|
||||||
CGFloat scaleFactor = [UIScreen mainScreen].scale;
|
CGFloat scaleFactor = [UIScreen mainScreen].scale;
|
||||||
mbgl::Size renderableSize = mapViewImpl->getRendererBackend().getDefaultRenderable().getSize();
|
mbgl::Size renderableSize = mapViewImpl->getRendererBackend().getDefaultRenderable().getSize();
|
||||||
mbgl::Size viewSize = {
|
mbgl::Size viewSize = {static_cast<uint32_t>(self.mapView.bounds.size.width * scaleFactor),
|
||||||
static_cast<uint32_t>(self.mapView.bounds.size.width * scaleFactor),
|
static_cast<uint32_t>(self.mapView.bounds.size.height * scaleFactor)};
|
||||||
static_cast<uint32_t>(self.mapView.bounds.size.height * scaleFactor)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Test that mapView and default renderable have the same size.
|
// Test that mapView and default renderable have the same size.
|
||||||
XCTAssertTrue(renderableSize == viewSize);
|
XCTAssertTrue(renderableSize == viewSize);
|
||||||
|
|
||||||
CLLocationCoordinate2D coordinates[] = {
|
CLLocationCoordinate2D coordinates[] = {CLLocationCoordinate2DMake(9.6315313, 52.4133574),
|
||||||
CLLocationCoordinate2DMake(9.6315313, 52.4133574),
|
|
||||||
CLLocationCoordinate2DMake(24.9410248, 60.1733244)};
|
CLLocationCoordinate2DMake(24.9410248, 60.1733244)};
|
||||||
|
|
||||||
MLNPointCollectionFeature *points = [MLNPointCollectionFeature pointCollectionWithCoordinates:coordinates count:sizeof(coordinates)/sizeof(coordinates[0])];
|
MLNPointCollectionFeature *points = [MLNPointCollectionFeature
|
||||||
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"heatmap" shape:points options:nil];
|
pointCollectionWithCoordinates:coordinates
|
||||||
|
count:sizeof(coordinates) / sizeof(coordinates[0])];
|
||||||
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"heatmap"
|
||||||
|
shape:points
|
||||||
|
options:nil];
|
||||||
[self.style addSource:source];
|
[self.style addSource:source];
|
||||||
|
|
||||||
MLNHeatmapStyleLayer *heatmapLayer = [[MLNHeatmapStyleLayer alloc] initWithIdentifier:@"lineLayer" source:source];
|
MLNHeatmapStyleLayer *heatmapLayer = [[MLNHeatmapStyleLayer alloc] initWithIdentifier:@"lineLayer"
|
||||||
|
source:source];
|
||||||
[self.style addLayer:heatmapLayer];
|
[self.style addLayer:heatmapLayer];
|
||||||
|
|
||||||
// Test that heatmap layer can create a texture and be successfully rendered.
|
// Test that heatmap layer can create a texture and be successfully rendered.
|
||||||
[self waitForMapViewToBeRendered];
|
[self waitForMapViewToBeRendered];
|
||||||
|
|
||||||
// Resize frame of the view.
|
// Resize frame of the view.
|
||||||
[self.mapView setFrame: CGRect{self.mapView.bounds.origin, {256, 256}}];
|
[self.mapView setFrame:CGRect{self.mapView.bounds.origin, {256, 256}}];
|
||||||
|
|
||||||
// Force sync re-layout.
|
// Force sync re-layout.
|
||||||
[self.mapView layoutIfNeeded];
|
[self.mapView layoutIfNeeded];
|
||||||
|
|
||||||
// Test that mapView and default renderable have the same size after re-layout.
|
// Test that mapView and default renderable have the same size after re-layout.
|
||||||
renderableSize = mapViewImpl->getRendererBackend().getDefaultRenderable().getSize();
|
renderableSize = mapViewImpl->getRendererBackend().getDefaultRenderable().getSize();
|
||||||
viewSize = { static_cast<uint32_t>(self.mapView.bounds.size.width * scaleFactor),
|
viewSize = {static_cast<uint32_t>(self.mapView.bounds.size.width * scaleFactor),
|
||||||
static_cast<uint32_t>(self.mapView.bounds.size.height * scaleFactor) };
|
static_cast<uint32_t>(self.mapView.bounds.size.height * scaleFactor)};
|
||||||
XCTAssertTrue(renderableSize == viewSize);
|
XCTAssertTrue(renderableSize == viewSize);
|
||||||
[self waitForMapViewToBeRendered];
|
[self waitForMapViewToBeRendered];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,27 @@
|
|||||||
#import <XCTest/XCTest.h>
|
#import <XCTest/XCTest.h>
|
||||||
@import Mapbox;
|
@import Mapbox;
|
||||||
|
#import "MLNFoundation_Private.h"
|
||||||
#import "MLNMapViewIntegrationTest.h"
|
#import "MLNMapViewIntegrationTest.h"
|
||||||
#import "MLNNetworkConfiguration_Private.h"
|
#import "MLNNetworkConfiguration_Private.h"
|
||||||
#import "MLNOfflineStorage_Private.h"
|
#import "MLNOfflineStorage_Private.h"
|
||||||
#import "MLNFoundation_Private.h"
|
|
||||||
|
|
||||||
@interface MLNNetworkConfiguration (Testing)
|
@interface MLNNetworkConfiguration (Testing)
|
||||||
+ (void)testing_clearNativeNetworkManagerDelegate;
|
+ (void)testing_clearNativeNetworkManagerDelegate;
|
||||||
+ (id)testing_nativeNetworkManagerDelegate;
|
+ (id)testing_nativeNetworkManagerDelegate;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MLNNetworkConfigurationTestDelegate: NSObject <MLNNetworkConfigurationDelegate>
|
@interface MLNNetworkConfigurationTestDelegate : NSObject <MLNNetworkConfigurationDelegate>
|
||||||
@property (nonatomic) NSURLSession *(^handler)();
|
@property (nonatomic) NSURLSession * (^handler)();
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MLNNetworkConfigurationSessionDelegate: NSObject <NSURLSessionDelegate>
|
@interface MLNNetworkConfigurationSessionDelegate : NSObject <NSURLSessionDelegate>
|
||||||
@property (nonatomic) dispatch_block_t authHandler;
|
@property (nonatomic) dispatch_block_t authHandler;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MLNNetworkConfigurationSessionDataDelegate: NSObject <NSURLSessionDataDelegate>
|
@interface MLNNetworkConfigurationSessionDataDelegate : NSObject <NSURLSessionDataDelegate>
|
||||||
@property (nonatomic) void (^dataHandler)(NSURLSessionDataTask *, NSData *);
|
@property (nonatomic) void (^dataHandler)(NSURLSessionDataTask *, NSData *);
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@interface MLNNetworkConfigurationIntegrationTests : MLNIntegrationTestCase
|
@interface MLNNetworkConfigurationIntegrationTests : MLNIntegrationTestCase
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -42,15 +41,15 @@
|
|||||||
[MLNNetworkConfiguration testing_clearNativeNetworkManagerDelegate];
|
[MLNNetworkConfiguration testing_clearNativeNetworkManagerDelegate];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)test0_NativeNetworkManagerDelegateIsSet
|
- (void)test0_NativeNetworkManagerDelegateIsSet {
|
||||||
{
|
|
||||||
ASSERT_NATIVE_DELEGATE_IS_NIL();
|
ASSERT_NATIVE_DELEGATE_IS_NIL();
|
||||||
MLNNetworkConfiguration *config = [[MLNNetworkConfiguration alloc] init];
|
MLNNetworkConfiguration *config = [[MLNNetworkConfiguration alloc] init];
|
||||||
[config resetNativeNetworkManagerDelegate];
|
[config resetNativeNetworkManagerDelegate];
|
||||||
|
|
||||||
id delegate = [MLNNetworkConfiguration testing_nativeNetworkManagerDelegate];
|
id delegate = [MLNNetworkConfiguration testing_nativeNetworkManagerDelegate];
|
||||||
|
|
||||||
id<MLNNativeNetworkDelegate> manager = MLN_OBJC_DYNAMIC_CAST_AS_PROTOCOL(delegate, MLNNativeNetworkDelegate);
|
id<MLNNativeNetworkDelegate> manager =
|
||||||
|
MLN_OBJC_DYNAMIC_CAST_AS_PROTOCOL(delegate, MLNNativeNetworkDelegate);
|
||||||
XCTAssertNotNil(manager);
|
XCTAssertNotNil(manager);
|
||||||
|
|
||||||
// Expected properties
|
// Expected properties
|
||||||
@@ -61,8 +60,7 @@
|
|||||||
XCTAssert(delegate != delegate2);
|
XCTAssert(delegate != delegate2);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)test1_NativeNetworkManagerDelegateIsSetBySharedManager
|
- (void)test1_NativeNetworkManagerDelegateIsSetBySharedManager {
|
||||||
{
|
|
||||||
ASSERT_NATIVE_DELEGATE_IS_NIL();
|
ASSERT_NATIVE_DELEGATE_IS_NIL();
|
||||||
|
|
||||||
// Just calling the shared manager is also sufficient (even though, it's a
|
// Just calling the shared manager is also sufficient (even though, it's a
|
||||||
@@ -72,15 +70,15 @@
|
|||||||
ASSERT_NATIVE_DELEGATE_IS_NOT_NIL();
|
ASSERT_NATIVE_DELEGATE_IS_NOT_NIL();
|
||||||
|
|
||||||
id delegate = [MLNNetworkConfiguration testing_nativeNetworkManagerDelegate];
|
id delegate = [MLNNetworkConfiguration testing_nativeNetworkManagerDelegate];
|
||||||
id<MLNNativeNetworkDelegate> manager = MLN_OBJC_DYNAMIC_CAST_AS_PROTOCOL(delegate, MLNNativeNetworkDelegate);
|
id<MLNNativeNetworkDelegate> manager =
|
||||||
|
MLN_OBJC_DYNAMIC_CAST_AS_PROTOCOL(delegate, MLNNativeNetworkDelegate);
|
||||||
XCTAssertNotNil(manager);
|
XCTAssertNotNil(manager);
|
||||||
|
|
||||||
// Expected properties
|
// Expected properties
|
||||||
XCTAssertNotNil([manager sessionConfiguration]);
|
XCTAssertNotNil([manager sessionConfiguration]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)test2_NativeNetworkManagerDelegateIsSet
|
- (void)test2_NativeNetworkManagerDelegateIsSet {
|
||||||
{
|
|
||||||
ASSERT_NATIVE_DELEGATE_IS_NIL();
|
ASSERT_NATIVE_DELEGATE_IS_NIL();
|
||||||
[MLNNetworkConfiguration sharedManager];
|
[MLNNetworkConfiguration sharedManager];
|
||||||
id delegate = [MLNNetworkConfiguration testing_nativeNetworkManagerDelegate];
|
id delegate = [MLNNetworkConfiguration testing_nativeNetworkManagerDelegate];
|
||||||
@@ -90,8 +88,7 @@
|
|||||||
XCTAssert(delegate == delegate2);
|
XCTAssert(delegate == delegate2);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)test3_NativeNetworkManagerDelegateIsSetBySharedOfflineStorage
|
- (void)test3_NativeNetworkManagerDelegateIsSetBySharedOfflineStorage {
|
||||||
{
|
|
||||||
ASSERT_NATIVE_DELEGATE_IS_NIL();
|
ASSERT_NATIVE_DELEGATE_IS_NIL();
|
||||||
|
|
||||||
// Similar to `[MLNNetworkConfiguration sharedManager]`,
|
// Similar to `[MLNNetworkConfiguration sharedManager]`,
|
||||||
@@ -100,38 +97,36 @@
|
|||||||
ASSERT_NATIVE_DELEGATE_IS_NOT_NIL();
|
ASSERT_NATIVE_DELEGATE_IS_NOT_NIL();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)test4_NativeNetworkManagerDelegateIsSetBySharedOfflineStorageASecondTime
|
- (void)test4_NativeNetworkManagerDelegateIsSetBySharedOfflineStorageASecondTime {
|
||||||
{
|
|
||||||
// Testing a second time...
|
// Testing a second time...
|
||||||
ASSERT_NATIVE_DELEGATE_IS_NIL();
|
ASSERT_NATIVE_DELEGATE_IS_NIL();
|
||||||
[MLNOfflineStorage sharedOfflineStorage];
|
[MLNOfflineStorage sharedOfflineStorage];
|
||||||
ASSERT_NATIVE_DELEGATE_IS_NOT_NIL();
|
ASSERT_NATIVE_DELEGATE_IS_NOT_NIL();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)test5_NativeNetworkManagerDelegateIsSetByMapViewInit
|
- (void)test5_NativeNetworkManagerDelegateIsSetByMapViewInit {
|
||||||
{
|
|
||||||
ASSERT_NATIVE_DELEGATE_IS_NIL();
|
ASSERT_NATIVE_DELEGATE_IS_NIL();
|
||||||
(void)[[MLNMapView alloc] init];
|
(void)[[MLNMapView alloc] init];
|
||||||
ASSERT_NATIVE_DELEGATE_IS_NOT_NIL();
|
ASSERT_NATIVE_DELEGATE_IS_NOT_NIL();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testNetworkConfigurationDelegateIsNil
|
- (void)testNetworkConfigurationDelegateIsNil {
|
||||||
{
|
|
||||||
MLNNetworkConfiguration *manager = [MLNNetworkConfiguration sharedManager];
|
MLNNetworkConfiguration *manager = [MLNNetworkConfiguration sharedManager];
|
||||||
XCTAssertNil(manager.delegate);
|
XCTAssertNil(manager.delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)internalTestNetworkConfigurationWithSession:(NSURLSession*)session shouldDownload:(BOOL)shouldDownload {
|
- (void)internalTestNetworkConfigurationWithSession:(NSURLSession *)session
|
||||||
|
shouldDownload:(BOOL)shouldDownload {
|
||||||
__block BOOL didCallSessionDelegate = NO;
|
__block BOOL didCallSessionDelegate = NO;
|
||||||
__block BOOL isMainThread = YES;
|
__block BOOL isMainThread = YES;
|
||||||
|
|
||||||
// Setup delegate object that provides a NSURLSession
|
// Setup delegate object that provides a NSURLSession
|
||||||
MLNNetworkConfiguration *manager = [MLNNetworkConfiguration sharedManager];
|
MLNNetworkConfiguration *manager = [MLNNetworkConfiguration sharedManager];
|
||||||
MLNNetworkConfigurationTestDelegate *delegate = [[MLNNetworkConfigurationTestDelegate alloc] init];
|
MLNNetworkConfigurationTestDelegate *delegate =
|
||||||
|
[[MLNNetworkConfigurationTestDelegate alloc] init];
|
||||||
delegate.handler = ^{
|
delegate.handler = ^{
|
||||||
NSURLSession *internalSession;
|
NSURLSession *internalSession;
|
||||||
@synchronized (self) {
|
@synchronized(self) {
|
||||||
didCallSessionDelegate = YES;
|
didCallSessionDelegate = YES;
|
||||||
isMainThread = [NSThread isMainThread];
|
isMainThread = [NSThread isMainThread];
|
||||||
internalSession = session;
|
internalSession = session;
|
||||||
@@ -146,8 +141,10 @@
|
|||||||
//
|
//
|
||||||
// Want to ensure we download from the network; nuclear option
|
// Want to ensure we download from the network; nuclear option
|
||||||
{
|
{
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Expect database to be reset without an error."];
|
XCTestExpectation *expectation =
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] resetDatabaseWithCompletionHandler:^(NSError * _Nullable error) {
|
[self expectationWithDescription:@"Expect database to be reset without an error."];
|
||||||
|
[[MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
resetDatabaseWithCompletionHandler:^(NSError *_Nullable error) {
|
||||||
XCTAssertNil(error);
|
XCTAssertNil(error);
|
||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
}];
|
}];
|
||||||
@@ -156,28 +153,30 @@
|
|||||||
|
|
||||||
// Boston
|
// Boston
|
||||||
MLNCoordinateBounds bounds = {
|
MLNCoordinateBounds bounds = {
|
||||||
{ .latitude = 42.360, .longitude = -71.056 },
|
{.latitude = 42.360, .longitude = -71.056},
|
||||||
{ .latitude = 42.358, .longitude = -71.053 },
|
{.latitude = 42.358, .longitude = -71.053},
|
||||||
};
|
};
|
||||||
NSURL *styleURL = [[MLNStyle predefinedStyle:@"Bright"] url];
|
NSURL *styleURL = [[MLNStyle predefinedStyle:@"Bright"] url];
|
||||||
MLNTilePyramidOfflineRegion *region = [[MLNTilePyramidOfflineRegion alloc] initWithStyleURL:styleURL
|
MLNTilePyramidOfflineRegion *region =
|
||||||
|
[[MLNTilePyramidOfflineRegion alloc] initWithStyleURL:styleURL
|
||||||
bounds:bounds
|
bounds:bounds
|
||||||
fromZoomLevel:20
|
fromZoomLevel:20
|
||||||
toZoomLevel:20];
|
toZoomLevel:20];
|
||||||
|
|
||||||
NSData *context = [NSKeyedArchiver archivedDataWithRootObject:@{
|
NSData *context = [NSKeyedArchiver archivedDataWithRootObject:@{@"Name" : @"Faneuil Hall"}];
|
||||||
@"Name": @"Faneuil Hall"
|
|
||||||
}];
|
|
||||||
|
|
||||||
__block MLNOfflinePack *pack = nil;
|
__block MLNOfflinePack *pack = nil;
|
||||||
|
|
||||||
// Add pack
|
// Add pack
|
||||||
{
|
{
|
||||||
XCTestExpectation *additionCompletionHandlerExpectation = [self expectationWithDescription:@"add pack completion handler"];
|
XCTestExpectation *additionCompletionHandlerExpectation =
|
||||||
|
[self expectationWithDescription:@"add pack completion handler"];
|
||||||
|
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] addPackForRegion:region
|
[[MLNOfflineStorage sharedOfflineStorage]
|
||||||
|
addPackForRegion:region
|
||||||
withContext:context
|
withContext:context
|
||||||
completionHandler:^(MLNOfflinePack * _Nullable completionHandlerPack, NSError * _Nullable error) {
|
completionHandler:^(MLNOfflinePack *_Nullable completionHandlerPack,
|
||||||
|
NSError *_Nullable error) {
|
||||||
pack = completionHandlerPack;
|
pack = completionHandlerPack;
|
||||||
[additionCompletionHandlerExpectation fulfill];
|
[additionCompletionHandlerExpectation fulfill];
|
||||||
}];
|
}];
|
||||||
@@ -188,14 +187,17 @@
|
|||||||
|
|
||||||
// Download
|
// Download
|
||||||
{
|
{
|
||||||
XCTestExpectation *expectation = [self expectationForNotification:MLNOfflinePackProgressChangedNotification object:pack handler:^BOOL(NSNotification * _Nonnull notification) {
|
XCTestExpectation *expectation =
|
||||||
|
[self expectationForNotification:MLNOfflinePackProgressChangedNotification
|
||||||
|
object:pack
|
||||||
|
handler:^BOOL(NSNotification *_Nonnull notification) {
|
||||||
return pack.state == MLNOfflinePackStateComplete;
|
return pack.state == MLNOfflinePackStateComplete;
|
||||||
}];
|
}];
|
||||||
|
|
||||||
expectation.inverted = !shouldDownload;
|
expectation.inverted = !shouldDownload;
|
||||||
[pack resume];
|
[pack resume];
|
||||||
|
|
||||||
[self waitForExpectations:@[expectation] timeout:15];
|
[self waitForExpectations:@[ expectation ] timeout:15];
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTAssert(didCallSessionDelegate);
|
XCTAssert(didCallSessionDelegate);
|
||||||
@@ -203,8 +205,10 @@
|
|||||||
|
|
||||||
// Remove pack, so we don't affect other tests
|
// Remove pack, so we don't affect other tests
|
||||||
{
|
{
|
||||||
XCTestExpectation *expectation = [self expectationWithDescription:@"remove pack completion handler"];
|
XCTestExpectation *expectation =
|
||||||
[[MLNOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:^(NSError * _Nullable error) {
|
[self expectationWithDescription:@"remove pack completion handler"];
|
||||||
|
[[MLNOfflineStorage sharedOfflineStorage] removePack:pack
|
||||||
|
withCompletionHandler:^(NSError *_Nullable error) {
|
||||||
[expectation fulfill];
|
[expectation fulfill];
|
||||||
}];
|
}];
|
||||||
[self waitForExpectationsWithTimeout:5 handler:nil];
|
[self waitForExpectationsWithTimeout:5 handler:nil];
|
||||||
@@ -219,7 +223,8 @@
|
|||||||
- (void)testNetworkConfigurationWithBackgroundSessionConfiguration {
|
- (void)testNetworkConfigurationWithBackgroundSessionConfiguration {
|
||||||
// Background session configurations are NOT supported, we expect this test
|
// Background session configurations are NOT supported, we expect this test
|
||||||
// trigger an exception
|
// trigger an exception
|
||||||
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:NSStringFromSelector(_cmd)];
|
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration
|
||||||
|
backgroundSessionConfigurationWithIdentifier:NSStringFromSelector(_cmd)];
|
||||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
|
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
|
||||||
|
|
||||||
XCTAssert([session isKindOfClass:NSClassFromString(@"__NSURLBackgroundSession")]);
|
XCTAssert([session isKindOfClass:NSClassFromString(@"__NSURLBackgroundSession")]);
|
||||||
@@ -227,17 +232,20 @@
|
|||||||
// We cannot do this yet, as it requires intecepting the exception in gl-native
|
// We cannot do this yet, as it requires intecepting the exception in gl-native
|
||||||
// It makes more sense to support background configs (requiring delegation
|
// It makes more sense to support background configs (requiring delegation
|
||||||
// rather than blocks in gl-native)
|
// rather than blocks in gl-native)
|
||||||
// [self internalTestNetworkConfigurationWithSession:session], NSException, NSInvalidArgumentException);
|
// [self internalTestNetworkConfigurationWithSession:session], NSException,
|
||||||
|
// NSInvalidArgumentException);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testNetworkConfigurationWithDefaultSessionConfigurationLOCKED {
|
- (void)testNetworkConfigurationWithDefaultSessionConfigurationLOCKED {
|
||||||
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
NSURLSessionConfiguration *sessionConfig =
|
||||||
|
[NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
|
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
|
||||||
[self internalTestNetworkConfigurationWithSession:session shouldDownload:YES];
|
[self internalTestNetworkConfigurationWithSession:session shouldDownload:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testNetworkConfigurationWithEmphemeralSessionConfigurationLOCKED {
|
- (void)testNetworkConfigurationWithEmphemeralSessionConfigurationLOCKED {
|
||||||
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
NSURLSessionConfiguration *sessionConfig =
|
||||||
|
[NSURLSessionConfiguration ephemeralSessionConfiguration];
|
||||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
|
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
|
||||||
[self internalTestNetworkConfigurationWithSession:session shouldDownload:YES];
|
[self internalTestNetworkConfigurationWithSession:session shouldDownload:YES];
|
||||||
}
|
}
|
||||||
@@ -246,10 +254,12 @@
|
|||||||
__block BOOL didCallAuthChallenge = NO;
|
__block BOOL didCallAuthChallenge = NO;
|
||||||
__block BOOL isMainThread = YES;
|
__block BOOL isMainThread = YES;
|
||||||
|
|
||||||
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
NSURLSessionConfiguration *sessionConfig =
|
||||||
MLNNetworkConfigurationSessionDelegate *delegate = [[MLNNetworkConfigurationSessionDelegate alloc] init];
|
[NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
|
MLNNetworkConfigurationSessionDelegate *delegate =
|
||||||
|
[[MLNNetworkConfigurationSessionDelegate alloc] init];
|
||||||
delegate.authHandler = ^{
|
delegate.authHandler = ^{
|
||||||
@synchronized (self) {
|
@synchronized(self) {
|
||||||
didCallAuthChallenge = YES;
|
didCallAuthChallenge = YES;
|
||||||
isMainThread = [NSThread isMainThread];
|
isMainThread = [NSThread isMainThread];
|
||||||
}
|
}
|
||||||
@@ -269,10 +279,12 @@
|
|||||||
- (void)testFailureForNetworkConfigurationWithSessionWithDataDelegateLOCKED {
|
- (void)testFailureForNetworkConfigurationWithSessionWithDataDelegateLOCKED {
|
||||||
__block BOOL didCallReceiveData = NO;
|
__block BOOL didCallReceiveData = NO;
|
||||||
|
|
||||||
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
NSURLSessionConfiguration *sessionConfig =
|
||||||
MLNNetworkConfigurationSessionDataDelegate *delegate = [[MLNNetworkConfigurationSessionDataDelegate alloc] init];
|
[NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
|
MLNNetworkConfigurationSessionDataDelegate *delegate =
|
||||||
|
[[MLNNetworkConfigurationSessionDataDelegate alloc] init];
|
||||||
delegate.dataHandler = ^(NSURLSessionDataTask *task, NSData *data) {
|
delegate.dataHandler = ^(NSURLSessionDataTask *task, NSData *data) {
|
||||||
@synchronized (self) {
|
@synchronized(self) {
|
||||||
didCallReceiveData = YES;
|
didCallReceiveData = YES;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -299,8 +311,9 @@
|
|||||||
|
|
||||||
- (void)testNetworkConfigurationWithSessionConfigurationWithCustomHeadersLOCKED {
|
- (void)testNetworkConfigurationWithSessionConfigurationWithCustomHeadersLOCKED {
|
||||||
// Custom session configuration, based on ``MLNNetworkConfiguration/defaultSessionConfiguration``
|
// Custom session configuration, based on ``MLNNetworkConfiguration/defaultSessionConfiguration``
|
||||||
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
NSURLSessionConfiguration *sessionConfig =
|
||||||
sessionConfig.HTTPAdditionalHeaders = @{ @"testing" : @YES };
|
[NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
|
sessionConfig.HTTPAdditionalHeaders = @{@"testing" : @YES};
|
||||||
sessionConfig.HTTPMaximumConnectionsPerHost = 1;
|
sessionConfig.HTTPMaximumConnectionsPerHost = 1;
|
||||||
sessionConfig.timeoutIntervalForResource = 30;
|
sessionConfig.timeoutIntervalForResource = 30;
|
||||||
sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
|
sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
|
||||||
@@ -328,7 +341,10 @@
|
|||||||
// MARK: - NSURLSession delegate
|
// MARK: - NSURLSession delegate
|
||||||
|
|
||||||
@implementation MLNNetworkConfigurationSessionDelegate
|
@implementation MLNNetworkConfigurationSessionDelegate
|
||||||
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
|
- (void)URLSession:(NSURLSession *)session
|
||||||
|
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||||
|
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
|
||||||
|
NSURLCredential *_Nullable credential))completionHandler {
|
||||||
if (self.authHandler) {
|
if (self.authHandler) {
|
||||||
self.authHandler();
|
self.authHandler();
|
||||||
}
|
}
|
||||||
@@ -341,7 +357,9 @@
|
|||||||
// MARK: - NSURLSession data delegate
|
// MARK: - NSURLSession data delegate
|
||||||
|
|
||||||
@implementation MLNNetworkConfigurationSessionDataDelegate
|
@implementation MLNNetworkConfigurationSessionDataDelegate
|
||||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
|
- (void)URLSession:(NSURLSession *)session
|
||||||
|
dataTask:(NSURLSessionDataTask *)dataTask
|
||||||
|
didReceiveData:(NSData *)data {
|
||||||
if (self.dataHandler) {
|
if (self.dataHandler) {
|
||||||
self.dataHandler(dataTask, data);
|
self.dataHandler(dataTask, data);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
- (BOOL)application:(UIApplication*)application
|
- (BOOL)application:(UIApplication*)application
|
||||||
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
||||||
|
|
||||||
#ifndef MLN_LOGGING_DISABLED
|
#ifndef MLN_LOGGING_DISABLED
|
||||||
[MLNLoggingConfiguration sharedConfiguration].loggingLevel = MLNLoggingLevelFault;
|
[MLNLoggingConfiguration sharedConfiguration].loggingLevel = MLNLoggingLevelFault;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -19,16 +19,15 @@
|
|||||||
frameRenderingTime:(double)frameRenderingTime;
|
frameRenderingTime:(double)frameRenderingTime;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
class BenchMapObserver : public mbgl::MapObserver {
|
class BenchMapObserver : public mbgl::MapObserver {
|
||||||
public:
|
public:
|
||||||
BenchMapObserver() = delete;
|
BenchMapObserver() = delete;
|
||||||
BenchMapObserver(id<BenchMapDelegate> mapDelegate_) : mapDelegate(mapDelegate_) {}
|
BenchMapObserver(id<BenchMapDelegate> mapDelegate_) : mapDelegate(mapDelegate_) {}
|
||||||
virtual ~BenchMapObserver() = default;
|
virtual ~BenchMapObserver() = default;
|
||||||
|
|
||||||
void onDidFinishRenderingFrame(const RenderFrameStatus& status) override final {
|
void onDidFinishRenderingFrame(const RenderFrameStatus &status) override final {
|
||||||
//NSLog(@"Frame encoding time: %4.1f ms", status.renderingStats.encodingTime * 1e3);
|
// NSLog(@"Frame encoding time: %4.1f ms", status.renderingStats.encodingTime * 1e3);
|
||||||
//NSLog(@"Frame rendering time: %4.1f ms", status.renderingStats.renderingTime * 1e3);
|
// NSLog(@"Frame rendering time: %4.1f ms", status.renderingStats.renderingTime * 1e3);
|
||||||
|
|
||||||
bool fullyRendered = status.mode == mbgl::MapObserver::RenderMode::Full;
|
bool fullyRendered = status.mode == mbgl::MapObserver::RenderMode::Full;
|
||||||
[mapDelegate mapDidFinishRenderingFrameFullyRendered:fullyRendered
|
[mapDelegate mapDidFinishRenderingFrameFullyRendered:fullyRendered
|
||||||
@@ -54,58 +53,58 @@ protected:
|
|||||||
|
|
||||||
// MARK: - Setup
|
// MARK: - Setup
|
||||||
|
|
||||||
+ (void)initialize
|
+ (void)initialize {
|
||||||
{
|
if (self == [MBXBenchViewController class]) {
|
||||||
if (self == [MBXBenchViewController class])
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
|
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
|
||||||
@"MBXUserTrackingMode": @(MLNUserTrackingModeNone),
|
@"MBXUserTrackingMode" : @(MLNUserTrackingModeNone),
|
||||||
@"MBXShowsUserLocation": @NO,
|
@"MBXShowsUserLocation" : @NO,
|
||||||
@"MBXDebug": @NO,
|
@"MBXDebug" : @NO,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewDidLoad
|
- (void)viewDidLoad {
|
||||||
{
|
|
||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewDidAppear:(BOOL)animated
|
- (void)viewDidAppear:(BOOL)animated {
|
||||||
{
|
|
||||||
[super viewDidAppear:animated];
|
[super viewDidAppear:animated];
|
||||||
|
|
||||||
#ifdef LOG_TO_DOCUMENTS_DIR
|
#ifdef LOG_TO_DOCUMENTS_DIR
|
||||||
[self setAndRedirectLogFileToDocuments];
|
[self setAndRedirectLogFileToDocuments];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Use a local style and local assets if they’ve been downloaded.
|
// Use a local style and local assets if they’ve been downloaded.
|
||||||
NSURL *tile = [[NSBundle mainBundle] URLForResource:@"11" withExtension:@"pbf" subdirectory:@"tiles/tiles/v3/5/7"];
|
NSURL *tile = [[NSBundle mainBundle] URLForResource:@"11"
|
||||||
NSURL *tileSourceURL = [[NSBundle mainBundle] URLForResource:@"openmaptiles" withExtension:@"json" subdirectory:@"tiles"];
|
withExtension:@"pbf"
|
||||||
|
subdirectory:@"tiles/tiles/v3/5/7"];
|
||||||
|
NSURL *tileSourceURL = [[NSBundle mainBundle] URLForResource:@"openmaptiles"
|
||||||
|
withExtension:@"json"
|
||||||
|
subdirectory:@"tiles"];
|
||||||
const std::vector<std::string> styles = {
|
const std::vector<std::string> styles = {
|
||||||
"maptiler://maps/streets",
|
"maptiler://maps/streets",
|
||||||
};
|
};
|
||||||
constexpr auto styleIndex = 0;
|
constexpr auto styleIndex = 0;
|
||||||
NSURL *url = [NSURL URLWithString:tile ? @"asset://styles/streets.json" : [NSString stringWithCString:styles[styleIndex].c_str() encoding:NSUTF8StringEncoding]];
|
NSURL *url = [NSURL URLWithString:tile ? @"asset://styles/streets.json"
|
||||||
|
: [NSString stringWithCString:styles[styleIndex].c_str()
|
||||||
|
encoding:NSUTF8StringEncoding]];
|
||||||
NSLog(@"Using style URL: \"%@\"", [url absoluteString]);
|
NSLog(@"Using style URL: \"%@\"", [url absoluteString]);
|
||||||
|
|
||||||
self.imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
|
self.imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
|
||||||
self.imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
self.imageView.autoresizingMask =
|
||||||
|
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||||
[self.view addSubview:self.imageView];
|
[self.view addSubview:self.imageView];
|
||||||
|
|
||||||
mbgl::Size viewSize = { static_cast<uint32_t>(self.view.bounds.size.width),
|
mbgl::Size viewSize = {static_cast<uint32_t>(self.view.bounds.size.width),
|
||||||
static_cast<uint32_t>(self.view.bounds.size.height) };
|
static_cast<uint32_t>(self.view.bounds.size.height)};
|
||||||
auto pixelRatio = [[UIScreen mainScreen] scale];
|
auto pixelRatio = [[UIScreen mainScreen] scale];
|
||||||
|
|
||||||
observer = std::make_unique<BenchMapObserver>(self);
|
observer = std::make_unique<BenchMapObserver>(self);
|
||||||
frontend = std::make_unique<mbgl::HeadlessFrontend>(
|
frontend = std::make_unique<mbgl::HeadlessFrontend>(
|
||||||
viewSize,
|
viewSize, pixelRatio, mbgl::gfx::HeadlessBackend::SwapBehaviour::Flush,
|
||||||
pixelRatio,
|
|
||||||
mbgl::gfx::HeadlessBackend::SwapBehaviour::Flush,
|
|
||||||
mbgl::gfx::ContextMode::Unique,
|
mbgl::gfx::ContextMode::Unique,
|
||||||
/* localFontFamily */ std::nullopt,
|
/* localFontFamily */ std::nullopt,
|
||||||
/* invalidateOnUpdate */ false
|
/* invalidateOnUpdate */ false);
|
||||||
);
|
|
||||||
|
|
||||||
mbgl::MapOptions mapOptions;
|
mbgl::MapOptions mapOptions;
|
||||||
mapOptions.withMapMode(mbgl::MapMode::Continuous)
|
mapOptions.withMapMode(mbgl::MapMode::Continuous)
|
||||||
@@ -115,7 +114,8 @@ protected:
|
|||||||
.withViewportMode(mbgl::ViewportMode::Default)
|
.withViewportMode(mbgl::ViewportMode::Default)
|
||||||
.withCrossSourceCollisions(true);
|
.withCrossSourceCollisions(true);
|
||||||
|
|
||||||
mbgl::TileServerOptions* tileServerOptions = [[MLNSettings sharedSettings] tileServerOptionsInternal];
|
mbgl::TileServerOptions *tileServerOptions =
|
||||||
|
[[MLNSettings sharedSettings] tileServerOptionsInternal];
|
||||||
mbgl::ResourceOptions resourceOptions;
|
mbgl::ResourceOptions resourceOptions;
|
||||||
resourceOptions.withCachePath(MLNOfflineStorage.sharedOfflineStorage.databasePath.UTF8String)
|
resourceOptions.withCachePath(MLNOfflineStorage.sharedOfflineStorage.databasePath.UTF8String)
|
||||||
.withAssetPath([NSBundle mainBundle].resourceURL.path.UTF8String)
|
.withAssetPath([NSBundle mainBundle].resourceURL.path.UTF8String)
|
||||||
@@ -127,7 +127,8 @@ protected:
|
|||||||
resourceOptions.withApiKey([apiKey UTF8String]);
|
resourceOptions.withApiKey([apiKey UTF8String]);
|
||||||
}
|
}
|
||||||
|
|
||||||
map = std::make_unique<mbgl::Map>(*frontend, *observer, mapOptions, resourceOptions, clientOptions);
|
map =
|
||||||
|
std::make_unique<mbgl::Map>(*frontend, *observer, mapOptions, resourceOptions, clientOptions);
|
||||||
map->setSize(viewSize);
|
map->setSize(viewSize);
|
||||||
map->setDebug(mbgl::MapDebugOptions::NoDebug);
|
map->setDebug(mbgl::MapDebugOptions::NoDebug);
|
||||||
map->getStyle().loadURL([url.absoluteString UTF8String]);
|
map->getStyle().loadURL([url.absoluteString UTF8String]);
|
||||||
@@ -136,20 +137,21 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For setting the filename
|
// For setting the filename
|
||||||
NSDate* const currentDate = [NSDate date];
|
NSDate *const currentDate = [NSDate date];
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\description
|
\description
|
||||||
Write the Benchmark log to the Documents Directory. For the device or simulator, fetch the Documents Directory, and make a friendly name for the benchmarking output file. In Build Settings > Other C Flags add the compiler flag. `-DLOG_TO_DOCUMENTS_DIR`
|
Write the Benchmark log to the Documents Directory. For the device or simulator, fetch the
|
||||||
|
Documents Directory, and make a friendly name for the benchmarking output file. In Build Settings
|
||||||
|
> Other C Flags add the compiler flag. `-DLOG_TO_DOCUMENTS_DIR`
|
||||||
*/
|
*/
|
||||||
- (void)setAndRedirectLogFileToDocuments
|
- (void)setAndRedirectLogFileToDocuments {
|
||||||
{
|
NSString *const name = UIDevice.currentDevice.name;
|
||||||
NSString* const name = UIDevice.currentDevice.name;
|
#if TARGET_IPHONE_SIMULATOR
|
||||||
#if TARGET_IPHONE_SIMULATOR
|
NSString *const DeviceMode = @"Simulator";
|
||||||
NSString* const DeviceMode = @"Simulator";
|
#else
|
||||||
#else
|
NSString *const DeviceMode = @"Device";
|
||||||
NSString* const DeviceMode = @"Device";
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
// Set log file name date
|
// Set log file name date
|
||||||
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
||||||
@@ -157,20 +159,21 @@ NSDate* const currentDate = [NSDate date];
|
|||||||
NSString *dateString = [formatter stringFromDate:currentDate];
|
NSString *dateString = [formatter stringFromDate:currentDate];
|
||||||
|
|
||||||
// Set the log file name
|
// Set the log file name
|
||||||
NSString* filename = [NSString stringWithFormat: @"MapLibre-bench-%@-%@-%@.log", name, DeviceMode, dateString];
|
NSString *filename =
|
||||||
|
[NSString stringWithFormat:@"MapLibre-bench-%@-%@-%@.log", name, DeviceMode, dateString];
|
||||||
|
|
||||||
// Set log file path
|
// Set log file path
|
||||||
NSArray *allPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
NSArray *allPaths =
|
||||||
|
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||||
NSString *documentsDirectory = [allPaths objectAtIndex:0];
|
NSString *documentsDirectory = [allPaths objectAtIndex:0];
|
||||||
NSString *pathForLog = [documentsDirectory stringByAppendingPathComponent: filename];
|
NSString *pathForLog = [documentsDirectory stringByAppendingPathComponent:filename];
|
||||||
NSLog(@"Writing MapLibre Bench log. To open in Console, use the CLI command");
|
NSLog(@"Writing MapLibre Bench log. To open in Console, use the CLI command");
|
||||||
NSLog(@" open \"%@\"", pathForLog);
|
NSLog(@" open \"%@\"", pathForLog);
|
||||||
|
|
||||||
[self redirectLogToDocuments :pathForLog];
|
[self redirectLogToDocuments:pathForLog];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)redirectLogToDocuments: (NSString*) fileName
|
- (void)redirectLogToDocuments:(NSString *)fileName {
|
||||||
{
|
|
||||||
// write to file in append "a+" mode
|
// write to file in append "a+" mode
|
||||||
freopen([fileName cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
|
freopen([fileName cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
|
||||||
}
|
}
|
||||||
@@ -181,16 +184,15 @@ int frames = 0;
|
|||||||
double totalFrameEncodingTime = 0;
|
double totalFrameEncodingTime = 0;
|
||||||
double totalFrameRenderingTime = 0;
|
double totalFrameRenderingTime = 0;
|
||||||
std::chrono::steady_clock::time_point started;
|
std::chrono::steady_clock::time_point started;
|
||||||
std::vector<std::pair<std::string, std::pair<double, double>> > result;
|
std::vector<std::pair<std::string, std::pair<double, double>>> result;
|
||||||
|
|
||||||
static const int benchmarkDuration = 5; // seconds
|
static const int benchmarkDuration = 5; // seconds
|
||||||
|
|
||||||
namespace mbgl {
|
namespace mbgl {
|
||||||
extern std::size_t uploadCount, uploadBuildCount, uploadVertextAttrsDirty, uploadInvalidSegments;
|
extern std::size_t uploadCount, uploadBuildCount, uploadVertextAttrsDirty, uploadInvalidSegments;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)renderFrame
|
- (void)renderFrame {
|
||||||
{
|
|
||||||
mbgl::gfx::BackendScope guard{*(frontend->getBackend())};
|
mbgl::gfx::BackendScope guard{*(frontend->getBackend())};
|
||||||
|
|
||||||
frontend->renderFrame();
|
frontend->renderFrame();
|
||||||
@@ -198,15 +200,9 @@ namespace mbgl {
|
|||||||
auto image = frontend->readStillImage();
|
auto image = frontend->readStillImage();
|
||||||
|
|
||||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||||
CGContextRef bitmapContext = CGBitmapContextCreate(
|
CGContextRef bitmapContext =
|
||||||
image.data.get(),
|
CGBitmapContextCreate(image.data.get(), image.size.width, image.size.height, 8,
|
||||||
image.size.width,
|
4 * image.size.width, colorSpace, kCGImageAlphaPremultipliedLast);
|
||||||
image.size.height,
|
|
||||||
8,
|
|
||||||
4 * image.size.width,
|
|
||||||
colorSpace,
|
|
||||||
kCGImageAlphaPremultipliedLast
|
|
||||||
);
|
|
||||||
CFRelease(colorSpace);
|
CFRelease(colorSpace);
|
||||||
CGImageRef cgImage = CGBitmapContextCreateImage(bitmapContext);
|
CGImageRef cgImage = CGBitmapContextCreateImage(bitmapContext);
|
||||||
CGContextRelease(bitmapContext);
|
CGContextRelease(bitmapContext);
|
||||||
@@ -215,17 +211,17 @@ namespace mbgl {
|
|||||||
CGImageRelease(cgImage);
|
CGImageRelease(cgImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)startBenchmarkIteration
|
- (void)startBenchmarkIteration {
|
||||||
{
|
|
||||||
if (mbgl::bench::locations.size() > idx) {
|
if (mbgl::bench::locations.size() > idx) {
|
||||||
const auto& location = mbgl::bench::locations[idx];
|
const auto &location = mbgl::bench::locations[idx];
|
||||||
|
|
||||||
mbgl::CameraOptions cameraOptions;
|
mbgl::CameraOptions cameraOptions;
|
||||||
cameraOptions.center = mbgl::LatLng(location.latitude, location.longitude);
|
cameraOptions.center = mbgl::LatLng(location.latitude, location.longitude);
|
||||||
cameraOptions.zoom = location.zoom;
|
cameraOptions.zoom = location.zoom;
|
||||||
cameraOptions.bearing = location.bearing;
|
cameraOptions.bearing = location.bearing;
|
||||||
mbgl::AnimationOptions animationOptions;
|
mbgl::AnimationOptions animationOptions;
|
||||||
animationOptions.duration.emplace(std::chrono::duration_cast<mbgl::Duration>(std::chrono::duration<NSTimeInterval>(benchmarkDuration)));
|
animationOptions.duration.emplace(std::chrono::duration_cast<mbgl::Duration>(
|
||||||
|
std::chrono::duration<NSTimeInterval>(benchmarkDuration)));
|
||||||
map->easeTo(cameraOptions, animationOptions);
|
map->easeTo(cameraOptions, animationOptions);
|
||||||
|
|
||||||
state = State::WaitingForAssets;
|
state = State::WaitingForAssets;
|
||||||
@@ -241,14 +237,15 @@ namespace mbgl {
|
|||||||
|
|
||||||
NSLog(@"Result:");
|
NSLog(@"Result:");
|
||||||
size_t colWidth = 0;
|
size_t colWidth = 0;
|
||||||
for (const auto& row : result) {
|
for (const auto &row : result) {
|
||||||
colWidth = std::max(row.first.size(), colWidth);
|
colWidth = std::max(row.first.size(), colWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
double totalFrameEncodingTime = 0;
|
double totalFrameEncodingTime = 0;
|
||||||
double totalFrameRenderingTime = 0;
|
double totalFrameRenderingTime = 0;
|
||||||
for (const auto& row : result) {
|
for (const auto &row : result) {
|
||||||
NSLog(@"| %-*s | %4.2f ms | %4.2f ms |", int(colWidth), row.first.c_str(), 1e3 * row.second.first, 1e3 * row.second.second);
|
NSLog(@"| %-*s | %4.2f ms | %4.2f ms |", int(colWidth), row.first.c_str(),
|
||||||
|
1e3 * row.second.first, 1e3 * row.second.second);
|
||||||
totalFrameEncodingTime += row.second.first;
|
totalFrameEncodingTime += row.second.first;
|
||||||
totalFrameRenderingTime += row.second.second;
|
totalFrameRenderingTime += row.second.second;
|
||||||
}
|
}
|
||||||
@@ -271,15 +268,14 @@ namespace mbgl {
|
|||||||
|
|
||||||
// this does not shut the application down correctly,
|
// this does not shut the application down correctly,
|
||||||
// and results in an assertion failure in thread-local code
|
// and results in an assertion failure in thread-local code
|
||||||
//exit(0);
|
// exit(0);
|
||||||
|
|
||||||
// Use the UIApplication lifecycle instead.
|
// Use the UIApplication lifecycle instead.
|
||||||
// Terminating an app programmatically is strongly discouraged by Apple.
|
// Terminating an app programmatically is strongly discouraged by Apple.
|
||||||
// Combined with the plist setting "Application does not run in background" suspend allows
|
// Combined with the plist setting "Application does not run in background" suspend allows
|
||||||
// the XCUITest to wake up, so it doesn't need to guess how long we'll take and wait.
|
// the XCUITest to wake up, so it doesn't need to guess how long we'll take and wait.
|
||||||
UIApplication *app = [UIApplication sharedApplication];
|
UIApplication *app = [UIApplication sharedApplication];
|
||||||
if ([app respondsToSelector:@selector(suspend)])
|
if ([app respondsToSelector:@selector(suspend)]) {
|
||||||
{
|
|
||||||
[app performSelector:@selector(suspend)];
|
[app performSelector:@selector(suspend)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,24 +283,25 @@ namespace mbgl {
|
|||||||
|
|
||||||
- (void)mapDidFinishRenderingFrameFullyRendered:(__unused BOOL)fullyRendered
|
- (void)mapDidFinishRenderingFrameFullyRendered:(__unused BOOL)fullyRendered
|
||||||
frameEncodingTime:(double)frameEncodingTime
|
frameEncodingTime:(double)frameEncodingTime
|
||||||
frameRenderingTime:(double)frameRenderingTime
|
frameRenderingTime:(double)frameRenderingTime {
|
||||||
{
|
if (state == State::Benchmarking) {
|
||||||
if (state == State::Benchmarking)
|
|
||||||
{
|
|
||||||
frames++;
|
frames++;
|
||||||
totalFrameEncodingTime += frameEncodingTime;
|
totalFrameEncodingTime += frameEncodingTime;
|
||||||
totalFrameRenderingTime += frameRenderingTime;
|
totalFrameRenderingTime += frameRenderingTime;
|
||||||
|
|
||||||
const auto duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - started).count();
|
const auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
if (duration >= benchmarkDuration * 1e6)
|
std::chrono::steady_clock::now() - started)
|
||||||
{
|
.count();
|
||||||
|
if (duration >= benchmarkDuration * 1e6) {
|
||||||
state = State::None;
|
state = State::None;
|
||||||
|
|
||||||
// Report FPS
|
// Report FPS
|
||||||
const auto frameEncodingTime = static_cast<double>(totalFrameEncodingTime) / frames;
|
const auto frameEncodingTime = static_cast<double>(totalFrameEncodingTime) / frames;
|
||||||
const auto frameRenderingTime = static_cast<double>(totalFrameRenderingTime) / frames;
|
const auto frameRenderingTime = static_cast<double>(totalFrameRenderingTime) / frames;
|
||||||
result.emplace_back(mbgl::bench::locations[idx].name, std::make_pair(frameEncodingTime, frameRenderingTime));
|
result.emplace_back(mbgl::bench::locations[idx].name,
|
||||||
NSLog(@"- Frame encoding time: %.1f ms, Frame rendering time: %.1f ms (%d frames)", frameEncodingTime * 1e3, frameRenderingTime * 1e3, frames);
|
std::make_pair(frameEncodingTime, frameRenderingTime));
|
||||||
|
NSLog(@"- Frame encoding time: %.1f ms, Frame rendering time: %.1f ms (%d frames)",
|
||||||
|
frameEncodingTime * 1e3, frameRenderingTime * 1e3, frames);
|
||||||
|
|
||||||
// Start benchmarking the next location.
|
// Start benchmarking the next location.
|
||||||
idx++;
|
idx++;
|
||||||
@@ -315,11 +312,8 @@ namespace mbgl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
} else if (state == State::WaitingForAssets) {
|
||||||
else if (state == State::WaitingForAssets)
|
if (map->isFullyLoaded()) {
|
||||||
{
|
|
||||||
if (map->isFullyLoaded())
|
|
||||||
{
|
|
||||||
// Start the benchmarking timer.
|
// Start the benchmarking timer.
|
||||||
frames = 0;
|
frames = 0;
|
||||||
totalFrameEncodingTime = 0;
|
totalFrameEncodingTime = 0;
|
||||||
@@ -335,8 +329,7 @@ namespace mbgl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)supportedInterfaceOrientations
|
- (NSUInteger)supportedInterfaceOrientations {
|
||||||
{
|
|
||||||
return UIInterfaceOrientationMaskLandscape;
|
return UIInterfaceOrientationMaskLandscape;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
#import "MBXBenchAppDelegate.h"
|
#import "MBXBenchAppDelegate.h"
|
||||||
|
|
||||||
int main(int argc, char * argv[])
|
int main(int argc, char* argv[]) {
|
||||||
{
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([MBXBenchAppDelegate class]));
|
return UIApplicationMain(argc, argv, nil, NSStringFromClass([MBXBenchAppDelegate class]));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#import "MLNAnnotationView.h"
|
#import "MLNAnnotationView.h"
|
||||||
#import "MLNAnnotationView_Private.h"
|
|
||||||
#import "MLNMapView_Private.h"
|
|
||||||
#import "MLNCalloutView.h"
|
|
||||||
#import "MLNAnnotation.h"
|
#import "MLNAnnotation.h"
|
||||||
#import "MLNPointAnnotation.h"
|
#import "MLNAnnotationView_Private.h"
|
||||||
|
#import "MLNCalloutView.h"
|
||||||
#import "MLNLoggingConfiguration_Private.h"
|
#import "MLNLoggingConfiguration_Private.h"
|
||||||
|
#import "MLNMapView_Private.h"
|
||||||
|
#import "MLNPointAnnotation.h"
|
||||||
|
|
||||||
#import "NSBundle+MLNAdditions.h"
|
#import "NSBundle+MLNAdditions.h"
|
||||||
#import "NSValue+MLNAdditions.h"
|
#import "NSValue+MLNAdditions.h"
|
||||||
@@ -39,7 +39,8 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithAnnotation:(nullable id<MLNAnnotation>)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier {
|
- (instancetype)initWithAnnotation:(nullable id<MLNAnnotation>)annotation
|
||||||
|
reuseIdentifier:(nullable NSString *)reuseIdentifier {
|
||||||
MLNLogDebug(@"Initializing with annotation: %@ reuseIdentifier: %@", annotation, reuseIdentifier);
|
MLNLogDebug(@"Initializing with annotation: %@ reuseIdentifier: %@", annotation, reuseIdentifier);
|
||||||
self = [super initWithFrame:CGRectZero];
|
self = [super initWithFrame:CGRectZero];
|
||||||
if (self) {
|
if (self) {
|
||||||
@@ -48,7 +49,8 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)commonInitWithAnnotation:(nullable id<MLNAnnotation>)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier {
|
- (void)commonInitWithAnnotation:(nullable id<MLNAnnotation>)annotation
|
||||||
|
reuseIdentifier:(nullable NSString *)reuseIdentifier {
|
||||||
_lastAppliedScaleTransform = CATransform3DIdentity;
|
_lastAppliedScaleTransform = CATransform3DIdentity;
|
||||||
_lastAppliedRotationTransform = CATransform3DIdentity;
|
_lastAppliedRotationTransform = CATransform3DIdentity;
|
||||||
_annotation = annotation;
|
_annotation = annotation;
|
||||||
@@ -83,42 +85,37 @@
|
|||||||
[coder encodeBool:_draggable forKey:@"draggable"];
|
[coder encodeBool:_draggable forKey:@"draggable"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)prepareForReuse
|
- (void)prepareForReuse {
|
||||||
{
|
|
||||||
// Intentionally left blank. The default implementation of this method does nothing.
|
// Intentionally left blank. The default implementation of this method does nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setCenterOffset:(CGVector)centerOffset
|
- (void)setCenterOffset:(CGVector)centerOffset {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Setting centerOffset: %@", NSStringFromCGVector(centerOffset));
|
MLNLogDebug(@"Setting centerOffset: %@", NSStringFromCGVector(centerOffset));
|
||||||
_centerOffset = centerOffset;
|
_centerOffset = centerOffset;
|
||||||
self.center = self.center;
|
self.center = self.center;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSelected:(BOOL)selected
|
- (void)setSelected:(BOOL)selected {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Setting selected: %@", MLNStringFromBOOL(selected));
|
MLNLogDebug(@"Setting selected: %@", MLNStringFromBOOL(selected));
|
||||||
[self setSelected:selected animated:NO];
|
[self setSelected:selected animated:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
|
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
|
||||||
{
|
MLNLogDebug(@"Setting selected: %@ animated: %@", MLNStringFromBOOL(selected),
|
||||||
MLNLogDebug(@"Setting selected: %@ animated: %@", MLNStringFromBOOL(selected), MLNStringFromBOOL(animated));
|
MLNStringFromBOOL(animated));
|
||||||
[self willChangeValueForKey:@"selected"];
|
[self willChangeValueForKey:@"selected"];
|
||||||
_selected = selected;
|
_selected = selected;
|
||||||
[self didChangeValueForKey:@"selected"];
|
[self didChangeValueForKey:@"selected"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGPoint)center
|
- (CGPoint)center {
|
||||||
{
|
|
||||||
CGPoint center = super.center;
|
CGPoint center = super.center;
|
||||||
center.x -= _centerOffset.dx;
|
center.x -= _centerOffset.dx;
|
||||||
center.y -= _centerOffset.dy;
|
center.y -= _centerOffset.dy;
|
||||||
return center;
|
return center;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setCenter:(CGPoint)center
|
- (void)setCenter:(CGPoint)center {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Setting center: %@", NSStringFromCGPoint(center));
|
MLNLogDebug(@"Setting center: %@", NSStringFromCGPoint(center));
|
||||||
center.x += _centerOffset.dx;
|
center.x += _centerOffset.dx;
|
||||||
center.y += _centerOffset.dy;
|
center.y += _centerOffset.dy;
|
||||||
@@ -128,27 +125,26 @@
|
|||||||
[self updateRotateTransform];
|
[self updateRotateTransform];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setScalesWithViewingDistance:(BOOL)scalesWithViewingDistance
|
- (void)setScalesWithViewingDistance:(BOOL)scalesWithViewingDistance {
|
||||||
{
|
MLNLogDebug(@"Setting scaleWithViewingDistance: %@",
|
||||||
MLNLogDebug(@"Setting scaleWithViewingDistance: %@", MLNStringFromBOOL(scalesWithViewingDistance));
|
MLNStringFromBOOL(scalesWithViewingDistance));
|
||||||
if (_scalesWithViewingDistance != scalesWithViewingDistance)
|
if (_scalesWithViewingDistance != scalesWithViewingDistance) {
|
||||||
{
|
|
||||||
_scalesWithViewingDistance = scalesWithViewingDistance;
|
_scalesWithViewingDistance = scalesWithViewingDistance;
|
||||||
[self updateScaleTransformForViewingDistance];
|
[self updateScaleTransformForViewingDistance];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateScaleTransformForViewingDistance
|
- (void)updateScaleTransformForViewingDistance {
|
||||||
{
|
if (self.scalesWithViewingDistance == NO || self.dragState == MLNAnnotationViewDragStateDragging)
|
||||||
if (self.scalesWithViewingDistance == NO || self.dragState == MLNAnnotationViewDragStateDragging) return;
|
return;
|
||||||
|
|
||||||
CGFloat superviewHeight = CGRectGetHeight(self.superview.frame);
|
CGFloat superviewHeight = CGRectGetHeight(self.superview.frame);
|
||||||
if (superviewHeight > 0.0) {
|
if (superviewHeight > 0.0) {
|
||||||
// Find the maximum amount of scale reduction to apply as the view's center moves from the top
|
// Find the maximum amount of scale reduction to apply as the view's center moves from the top
|
||||||
// of the superview to the bottom. For example, if this view's center has moved 25% of the way
|
// of the superview to the bottom. For example, if this view's center has moved 25% of the way
|
||||||
// from the top of the superview towards the bottom then the maximum scale reduction is 1 - .25
|
// from the top of the superview towards the bottom then the maximum scale reduction is 1 - .25
|
||||||
// or 75%. The range goes from a maximum of 100% to 0% as the view moves from the top to the bottom
|
// or 75%. The range goes from a maximum of 100% to 0% as the view moves from the top to the
|
||||||
// along the y axis of its superview.
|
// bottom along the y axis of its superview.
|
||||||
CGFloat maxScaleReduction = 1.0 - self.center.y / superviewHeight;
|
CGFloat maxScaleReduction = 1.0 - self.center.y / superviewHeight;
|
||||||
|
|
||||||
// Since it is possible for the map view to report a pitch less than 0 due to the nature of
|
// Since it is possible for the map view to report a pitch less than 0 due to the nature of
|
||||||
@@ -161,7 +157,8 @@
|
|||||||
|
|
||||||
// The pitch intensity represents how much the map view is actually pitched compared to
|
// The pitch intensity represents how much the map view is actually pitched compared to
|
||||||
// what is possible. The value will range from 0% (not pitched at all) to 100% (pitched as much
|
// what is possible. The value will range from 0% (not pitched at all) to 100% (pitched as much
|
||||||
// as the map view will allow). The map view's maximum pitch is defined in `mbgl::util::PITCH_MAX`.
|
// as the map view will allow). The map view's maximum pitch is defined in
|
||||||
|
// `mbgl::util::PITCH_MAX`.
|
||||||
CGFloat pitchIntensity = pitch / MLNDegreesFromRadians(mbgl::util::PITCH_MAX);
|
CGFloat pitchIntensity = pitch / MLNDegreesFromRadians(mbgl::util::PITCH_MAX);
|
||||||
|
|
||||||
// The pitch adjusted scale is the inverse proportion of the maximum possible scale reduction
|
// The pitch adjusted scale is the inverse proportion of the maximum possible scale reduction
|
||||||
@@ -174,25 +171,24 @@
|
|||||||
// we can account for it so that we don't get cumulative scaling every time we move.
|
// we can account for it so that we don't get cumulative scaling every time we move.
|
||||||
// We also avoid clobbering any existing transform passed in by the client or this SDK.
|
// We also avoid clobbering any existing transform passed in by the client or this SDK.
|
||||||
CATransform3D undoOfLastScaleTransform = CATransform3DInvert(_lastAppliedScaleTransform);
|
CATransform3D undoOfLastScaleTransform = CATransform3DInvert(_lastAppliedScaleTransform);
|
||||||
CATransform3D newScaleTransform = CATransform3DMakeScale(pitchAdjustedScale, pitchAdjustedScale, 1);
|
CATransform3D newScaleTransform =
|
||||||
CATransform3D effectiveTransform = CATransform3DConcat(undoOfLastScaleTransform, newScaleTransform);
|
CATransform3DMakeScale(pitchAdjustedScale, pitchAdjustedScale, 1);
|
||||||
|
CATransform3D effectiveTransform =
|
||||||
|
CATransform3DConcat(undoOfLastScaleTransform, newScaleTransform);
|
||||||
self.layer.transform = CATransform3DConcat(self.layer.transform, effectiveTransform);
|
self.layer.transform = CATransform3DConcat(self.layer.transform, effectiveTransform);
|
||||||
_lastAppliedScaleTransform = newScaleTransform;
|
_lastAppliedScaleTransform = newScaleTransform;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setRotatesToMatchCamera:(BOOL)rotatesToMatchCamera
|
- (void)setRotatesToMatchCamera:(BOOL)rotatesToMatchCamera {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Setting rotatesToMatchCamera: %@", MLNStringFromBOOL(rotatesToMatchCamera));
|
MLNLogDebug(@"Setting rotatesToMatchCamera: %@", MLNStringFromBOOL(rotatesToMatchCamera));
|
||||||
if (_rotatesToMatchCamera != rotatesToMatchCamera)
|
if (_rotatesToMatchCamera != rotatesToMatchCamera) {
|
||||||
{
|
|
||||||
_rotatesToMatchCamera = rotatesToMatchCamera;
|
_rotatesToMatchCamera = rotatesToMatchCamera;
|
||||||
[self updateRotateTransform];
|
[self updateRotateTransform];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateRotateTransform
|
- (void)updateRotateTransform {
|
||||||
{
|
|
||||||
if (self.rotatesToMatchCamera == NO) return;
|
if (self.rotatesToMatchCamera == NO) return;
|
||||||
|
|
||||||
CGFloat direction = -MLNRadiansFromDegrees(self.mapView.direction);
|
CGFloat direction = -MLNRadiansFromDegrees(self.mapView.direction);
|
||||||
@@ -206,57 +202,52 @@
|
|||||||
// We also avoid clobbering any existing transform passed in by the client or this SDK.
|
// We also avoid clobbering any existing transform passed in by the client or this SDK.
|
||||||
CATransform3D undoOfLastRotationTransform = CATransform3DInvert(_lastAppliedRotationTransform);
|
CATransform3D undoOfLastRotationTransform = CATransform3DInvert(_lastAppliedRotationTransform);
|
||||||
CATransform3D newRotationTransform = CATransform3DMakeRotation(direction, 0, 0, 1);
|
CATransform3D newRotationTransform = CATransform3DMakeRotation(direction, 0, 0, 1);
|
||||||
CATransform3D effectiveTransform = CATransform3DConcat(undoOfLastRotationTransform, newRotationTransform);
|
CATransform3D effectiveTransform =
|
||||||
|
CATransform3DConcat(undoOfLastRotationTransform, newRotationTransform);
|
||||||
self.layer.transform = CATransform3DConcat(self.layer.transform, effectiveTransform);
|
self.layer.transform = CATransform3DConcat(self.layer.transform, effectiveTransform);
|
||||||
_lastAppliedRotationTransform = newRotationTransform;
|
_lastAppliedRotationTransform = newRotationTransform;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Draggable
|
// MARK: - Draggable
|
||||||
|
|
||||||
- (void)setDraggable:(BOOL)draggable
|
- (void)setDraggable:(BOOL)draggable {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Setting draggable: %@", MLNStringFromBOOL(draggable));
|
MLNLogDebug(@"Setting draggable: %@", MLNStringFromBOOL(draggable));
|
||||||
[self willChangeValueForKey:@"draggable"];
|
[self willChangeValueForKey:@"draggable"];
|
||||||
_draggable = draggable;
|
_draggable = draggable;
|
||||||
[self didChangeValueForKey:@"draggable"];
|
[self didChangeValueForKey:@"draggable"];
|
||||||
|
|
||||||
if (draggable)
|
if (draggable) {
|
||||||
{
|
|
||||||
[self enableDrag];
|
[self enableDrag];
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
[self disableDrag];
|
[self disableDrag];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)enableDrag
|
- (void)enableDrag {
|
||||||
{
|
if (!_longPressRecognizer) {
|
||||||
if (!_longPressRecognizer)
|
UILongPressGestureRecognizer *recognizer =
|
||||||
{
|
[[UILongPressGestureRecognizer alloc] initWithTarget:self
|
||||||
UILongPressGestureRecognizer *recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
action:@selector(handleLongPress:)];
|
||||||
recognizer.delegate = self;
|
recognizer.delegate = self;
|
||||||
[self addGestureRecognizer:recognizer];
|
[self addGestureRecognizer:recognizer];
|
||||||
_longPressRecognizer = recognizer;
|
_longPressRecognizer = recognizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_panGestureRecognizer)
|
if (!_panGestureRecognizer) {
|
||||||
{
|
UIPanGestureRecognizer *recognizer =
|
||||||
UIPanGestureRecognizer *recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
|
[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
|
||||||
recognizer.delegate = self;
|
recognizer.delegate = self;
|
||||||
[self addGestureRecognizer:recognizer];
|
[self addGestureRecognizer:recognizer];
|
||||||
_panGestureRecognizer = recognizer;
|
_panGestureRecognizer = recognizer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)disableDrag
|
- (void)disableDrag {
|
||||||
{
|
|
||||||
[self removeGestureRecognizer:_longPressRecognizer];
|
[self removeGestureRecognizer:_longPressRecognizer];
|
||||||
[self removeGestureRecognizer:_panGestureRecognizer];
|
[self removeGestureRecognizer:_panGestureRecognizer];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)handleLongPress:(UILongPressGestureRecognizer *)sender
|
- (void)handleLongPress:(UILongPressGestureRecognizer *)sender {
|
||||||
{
|
|
||||||
switch (sender.state) {
|
switch (sender.state) {
|
||||||
case UIGestureRecognizerStateBegan:
|
case UIGestureRecognizerStateBegan:
|
||||||
self.dragState = MLNAnnotationViewDragStateStarting;
|
self.dragState = MLNAnnotationViewDragStateStarting;
|
||||||
@@ -278,8 +269,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)handlePan:(UIPanGestureRecognizer *)sender
|
- (void)handlePan:(UIPanGestureRecognizer *)sender {
|
||||||
{
|
|
||||||
self.center = [sender locationInView:sender.view.superview];
|
self.center = [sender locationInView:sender.view.superview];
|
||||||
|
|
||||||
if (sender.state == UIGestureRecognizerStateEnded) {
|
if (sender.state == UIGestureRecognizerStateEnded) {
|
||||||
@@ -287,62 +277,58 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setDragState:(MLNAnnotationViewDragState)dragState
|
- (void)setDragState:(MLNAnnotationViewDragState)dragState {
|
||||||
{
|
|
||||||
MLNLogDebug(@"Setting dragState: %lu", (unsigned long)dragState);
|
MLNLogDebug(@"Setting dragState: %lu", (unsigned long)dragState);
|
||||||
[self setDragState:dragState animated:YES];
|
[self setDragState:dragState animated:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setDragState:(MLNAnnotationViewDragState)dragState animated:(BOOL)animated
|
- (void)setDragState:(MLNAnnotationViewDragState)dragState animated:(BOOL)animated {
|
||||||
{
|
MLNLogDebug(@"Setting dragState: %lu animated: %@", (unsigned long)dragState,
|
||||||
MLNLogDebug(@"Setting dragState: %lu animated: %@", (unsigned long)dragState, MLNStringFromBOOL(animated));
|
MLNStringFromBOOL(animated));
|
||||||
[self willChangeValueForKey:@"dragState"];
|
[self willChangeValueForKey:@"dragState"];
|
||||||
_dragState = dragState;
|
_dragState = dragState;
|
||||||
[self didChangeValueForKey:@"dragState"];
|
[self didChangeValueForKey:@"dragState"];
|
||||||
|
|
||||||
if (dragState == MLNAnnotationViewDragStateStarting)
|
if (dragState == MLNAnnotationViewDragStateStarting) {
|
||||||
{
|
|
||||||
[self.mapView.calloutViewForSelectedAnnotation dismissCalloutAnimated:animated];
|
[self.mapView.calloutViewForSelectedAnnotation dismissCalloutAnimated:animated];
|
||||||
[self.superview bringSubviewToFront:self];
|
[self.superview bringSubviewToFront:self];
|
||||||
}
|
} else if (dragState == MLNAnnotationViewDragStateCanceling) {
|
||||||
else if (dragState == MLNAnnotationViewDragStateCanceling)
|
|
||||||
{
|
|
||||||
if (!self.annotation) {
|
if (!self.annotation) {
|
||||||
[NSException raise:NSInvalidArgumentException
|
[NSException raise:NSInvalidArgumentException
|
||||||
format:@"Annotation property should not be nil."];
|
format:@"Annotation property should not be nil."];
|
||||||
}
|
}
|
||||||
self.panGestureRecognizer.enabled = NO;
|
self.panGestureRecognizer.enabled = NO;
|
||||||
self.longPressRecognizer.enabled = NO;
|
self.longPressRecognizer.enabled = NO;
|
||||||
self.center = [self.mapView convertCoordinate:self.annotation.coordinate toPointToView:self.mapView];
|
self.center = [self.mapView convertCoordinate:self.annotation.coordinate
|
||||||
|
toPointToView:self.mapView];
|
||||||
self.panGestureRecognizer.enabled = YES;
|
self.panGestureRecognizer.enabled = YES;
|
||||||
self.longPressRecognizer.enabled = YES;
|
self.longPressRecognizer.enabled = YES;
|
||||||
self.dragState = MLNAnnotationViewDragStateNone;
|
self.dragState = MLNAnnotationViewDragStateNone;
|
||||||
}
|
} else if (dragState == MLNAnnotationViewDragStateEnding) {
|
||||||
else if (dragState == MLNAnnotationViewDragStateEnding)
|
if ([self.annotation respondsToSelector:@selector(setCoordinate:)]) {
|
||||||
{
|
CLLocationCoordinate2D coordinate = [self.mapView convertPoint:self.center
|
||||||
if ([self.annotation respondsToSelector:@selector(setCoordinate:)])
|
toCoordinateFromView:self.mapView];
|
||||||
{
|
[(NSObject *)self.annotation setValue:[NSValue valueWithMLNCoordinate:coordinate]
|
||||||
CLLocationCoordinate2D coordinate = [self.mapView convertPoint:self.center toCoordinateFromView:self.mapView];
|
forKey:@"coordinate"];
|
||||||
[(NSObject *)self.annotation setValue:[NSValue valueWithMLNCoordinate:coordinate] forKey:@"coordinate"];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
|
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
|
||||||
{
|
|
||||||
BOOL isDragging = self.dragState == MLNAnnotationViewDragStateDragging;
|
BOOL isDragging = self.dragState == MLNAnnotationViewDragStateDragging;
|
||||||
|
|
||||||
if (gestureRecognizer == _panGestureRecognizer && !(isDragging))
|
if (gestureRecognizer == _panGestureRecognizer && !(isDragging)) {
|
||||||
{
|
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
|
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
|
||||||
{
|
shouldRecognizeSimultaneouslyWithGestureRecognizer:
|
||||||
return otherGestureRecognizer == _longPressRecognizer || otherGestureRecognizer == _panGestureRecognizer;
|
(UIGestureRecognizer *)otherGestureRecognizer {
|
||||||
|
return otherGestureRecognizer == _longPressRecognizer ||
|
||||||
|
otherGestureRecognizer == _panGestureRecognizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: UIAccessibility methods
|
// MARK: UIAccessibility methods
|
||||||
@@ -356,20 +342,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)accessibilityLabel {
|
- (NSString *)accessibilityLabel {
|
||||||
return [self.annotation respondsToSelector:@selector(title)] ? self.annotation.title : super.accessibilityLabel;
|
return [self.annotation respondsToSelector:@selector(title)] ? self.annotation.title
|
||||||
|
: super.accessibilityLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)accessibilityValue {
|
- (NSString *)accessibilityValue {
|
||||||
return [self.annotation respondsToSelector:@selector(subtitle)] ? self.annotation.subtitle : super.accessibilityValue;
|
return [self.annotation respondsToSelector:@selector(subtitle)] ? self.annotation.subtitle
|
||||||
|
: super.accessibilityValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)accessibilityHint {
|
- (NSString *)accessibilityHint {
|
||||||
return NSLocalizedStringWithDefaultValue(@"ANNOTATION_A11Y_HINT", nil, nil, @"Shows more info", @"Accessibility hint");
|
return NSLocalizedStringWithDefaultValue(@"ANNOTATION_A11Y_HINT", nil, nil, @"Shows more info",
|
||||||
|
@"Accessibility hint");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)accessibilityFrame {
|
- (CGRect)accessibilityFrame {
|
||||||
CGRect accessibilityFrame = self.frame;
|
CGRect accessibilityFrame = self.frame;
|
||||||
CGRect minimumFrame = CGRectInset({ self.center, CGSizeZero },
|
CGRect minimumFrame = CGRectInset({self.center, CGSizeZero},
|
||||||
-MLNAnnotationAccessibilityElementMinimumSize.width / 2,
|
-MLNAnnotationAccessibilityElementMinimumSize.width / 2,
|
||||||
-MLNAnnotationAccessibilityElementMinimumSize.height / 2);
|
-MLNAnnotationAccessibilityElementMinimumSize.height / 2);
|
||||||
accessibilityFrame = CGRectUnion(accessibilityFrame, minimumFrame);
|
accessibilityFrame = CGRectUnion(accessibilityFrame, minimumFrame);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user