Merge branch 'release-agua' back to master.
This commit is contained in:
22
Makefile
22
Makefile
@@ -608,32 +608,32 @@ run-android-unit-test: platform/android/configuration.gradle
|
||||
run-android-unit-test-%: platform/android/configuration.gradle
|
||||
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:testDebugUnitTest --tests "$*"
|
||||
|
||||
# Run Instrumentation tests on AWS device farm, requires additional authentication through gradle.properties
|
||||
.PHONY: run-android-ui-test-aws
|
||||
run-android-ui-test-aws: platform/android/configuration.gradle
|
||||
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all devicefarmUpload
|
||||
|
||||
# Builds a release package of the Android SDK
|
||||
.PHONY: apackage
|
||||
apackage: platform/android/configuration.gradle
|
||||
make android-lib-arm-v5 && make android-lib-arm-v7 && make android-lib-arm-v8 && make android-lib-x86 && make android-lib-x86-64 && make android-lib-mips
|
||||
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all assemble$(BUILDTYPE)
|
||||
|
||||
# Build test app instrumentation tests apk and test app apk for all abi's
|
||||
.PHONY: android-ui-test
|
||||
android-ui-test: platform/android/configuration.gradle
|
||||
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all :MapboxGLAndroidSDKTestApp:assembleDebug :MapboxGLAndroidSDKTestApp:assembleAndroidTest
|
||||
|
||||
# Uploads the compiled Android SDK to Maven
|
||||
.PHONY: run-android-upload-archives
|
||||
run-android-upload-archives: platform/android/configuration.gradle
|
||||
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all :MapboxGLAndroidSDK:uploadArchives
|
||||
cd platform/android && export IS_LOCAL_DEVELOPMENT=false && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all :MapboxGLAndroidSDK:uploadArchives
|
||||
|
||||
# Uploads the compiled Android SDK to ~/.m2/repository/com/mapbox/mapboxsdk
|
||||
.PHONY: run-android-upload-archives-local
|
||||
run-android-upload-archives-local: platform/android/configuration.gradle
|
||||
cd platform/android && export IS_LOCAL_DEVELOPMENT=true && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all :MapboxGLAndroidSDK:uploadArchives
|
||||
|
||||
# Dump system graphics information for the test app
|
||||
.PHONY: android-gfxinfo
|
||||
android-gfxinfo:
|
||||
adb shell dumpsys gfxinfo com.mapbox.mapboxsdk.testapp reset
|
||||
|
||||
# Runs Android UI tests on all connected devices using Spoon
|
||||
.PHONY: run-android-ui-test-spoon
|
||||
run-android-ui-test-spoon: platform/android/configuration.gradle
|
||||
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis="$(MBGL_ANDROID_ACTIVE_ARCHS)" spoon
|
||||
|
||||
# Generates Activity sanity tests
|
||||
.PHONY: test-code-android
|
||||
test-code-android:
|
||||
|
||||
15
circle.yml
15
circle.yml
@@ -238,6 +238,7 @@ jobs:
|
||||
- run:
|
||||
name: Run Clang checks
|
||||
command: make check
|
||||
no_output_timeout: 20m
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
android-debug-arm-v7:
|
||||
@@ -256,6 +257,9 @@ jobs:
|
||||
- *restore-cache
|
||||
- *restore-gradle-cache
|
||||
- *reset-ccache-stats
|
||||
- run:
|
||||
name: Check code style
|
||||
command: make android-check
|
||||
- run:
|
||||
name: Build libmapbox-gl.so for arm-v7
|
||||
command: make android-lib-arm-v7
|
||||
@@ -268,17 +272,6 @@ jobs:
|
||||
- run:
|
||||
name: Generate Espresso sanity tests
|
||||
command: make test-code-android
|
||||
- run:
|
||||
name: Check Java code style
|
||||
command: make android-checkstyle
|
||||
- run:
|
||||
name: Check Android modules for potential bugs (Lint SDK)
|
||||
command: |
|
||||
make android-lint-sdk
|
||||
- run:
|
||||
name: Check Android modules for potential bugs (Lint Test App)
|
||||
command: |
|
||||
make android-lint-test-app
|
||||
- run:
|
||||
name: Build Test APK
|
||||
command: |
|
||||
|
||||
@@ -2,7 +2,49 @@
|
||||
|
||||
Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started.
|
||||
|
||||
## 5.2.0 - TBA
|
||||
## 5.3.0 - December 20, 2017
|
||||
- Add support for TinySDF [#10706](https://github.com/mapbox/mapbox-gl-native/pull/10706)
|
||||
- Save restore MyLocationViewSettings [#10746](https://github.com/mapbox/mapbox-gl-native/pull/10746)
|
||||
- Post animation callback invocation [#10664](https://github.com/mapbox/mapbox-gl-native/pull/10664)
|
||||
- Allow configuring Http logging [#10681](https://github.com/mapbox/mapbox-gl-native/pull/10681)
|
||||
- Fix reverse scale gesture [#10688](https://github.com/mapbox/mapbox-gl-native/pull/10688)
|
||||
- Update offline region metadata documentation [#10693](https://github.com/mapbox/mapbox-gl-native/pull/10693)
|
||||
- Post camera listener invocation [#10690](https://github.com/mapbox/mapbox-gl-native/pull/10690)
|
||||
- Activate filesource for offline region creation [#10718](https://github.com/mapbox/mapbox-gl-native/pull/10718)
|
||||
- Update Spanish/Vietnamese translations [#10740](https://github.com/mapbox/mapbox-gl-native/pull/10740)
|
||||
- Update instrumented make target [#10724](https://github.com/mapbox/mapbox-gl-native/pull/10724)
|
||||
- Remove black flash on start for fragments [#10717](https://github.com/mapbox/mapbox-gl-native/pull/10717)
|
||||
- CompassView decode crash [#10717](https://github.com/mapbox/mapbox-gl-native/pull/10717)
|
||||
- Android SDK renaming [#10609](https://github.com/mapbox/mapbox-gl-native/pull/10609)
|
||||
- Map touch listener based lists [#10749](https://github.com/mapbox/mapbox-gl-native/pull/10749)
|
||||
|
||||
## 5.2.1 - December 6, 2017
|
||||
- Close race condition in RunLoop [#10537](https://github.com/mapbox/mapbox-gl-native/pull/10537)
|
||||
- OkHttp 3.9.1 [#10515](https://github.com/mapbox/mapbox-gl-native/pull/10515)
|
||||
- Attribution anchor point fix [#10558](https://github.com/mapbox/mapbox-gl-native/pull/10558)
|
||||
- Pre API 19 VerifyError [#10579](https://github.com/mapbox/mapbox-gl-native/pull/10579)
|
||||
- Set larger Http request limit [#10567](https://github.com/mapbox/mapbox-gl-native/pull/10567)
|
||||
- Remove jar generation from maven publish [#10625](https://github.com/mapbox/mapbox-gl-native/pull/10625)
|
||||
- Enable Map Rendering when paused for multiple window support [#10509](https://github.com/mapbox/mapbox-gl-native/pull/10509)
|
||||
- Activate FileSource when listing offline regions [#10531](https://github.com/mapbox/mapbox-gl-native/pull/10531)
|
||||
- Harden MarkerView integration by checking for null bitmap [#10532](https://github.com/mapbox/mapbox-gl-native/pull/10532)
|
||||
- Use concurrent lists for camera change listeners [#10542](https://github.com/mapbox/mapbox-gl-native/pull/10542)
|
||||
- Handle destroy activity as part of theme switching [#10589](https://github.com/mapbox/mapbox-gl-native/pull/10589)
|
||||
- add FileSource activation/deactivation to MapSnapshotter [#10556](https://github.com/mapbox/mapbox-gl-native/pull/10556)
|
||||
|
||||
## 5.2.0 - November 17, 2017
|
||||
|
||||
- Monkey crashes [#10472](https://github.com/mapbox/mapbox-gl-native/pull/10472)
|
||||
|
||||
## 5.2.0-beta.5 - November 14, 2017
|
||||
|
||||
- MapSnapshot attribution [#10362](https://github.com/mapbox/mapbox-gl-native/pull/10362)
|
||||
- Downgrade min sdk to 14 [#10355](https://github.com/mapbox/mapbox-gl-native/pull/10355)
|
||||
- Harden deselection mechanism for markers [#10403](https://github.com/mapbox/mapbox-gl-native/pull/10403)
|
||||
- Cherry picks to agua [#10442](https://github.com/mapbox/mapbox-gl-native/pull/10442)
|
||||
- Rework test activity gen script setup [#10365](https://github.com/mapbox/mapbox-gl-native/pull/10365)
|
||||
- Fix broken android unit tests, update test make target to SDK [#10387](https://github.com/mapbox/mapbox-gl-native/pull/10387)
|
||||
- Check for null value when calling mapboxMap.clear [#10388](https://github.com/mapbox/mapbox-gl-native/pull/10388)
|
||||
|
||||
## 5.2.0-beta.4 - November 3, 2017
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ dependencies {
|
||||
testImplementation rootProject.ext.dep.lost
|
||||
testImplementation rootProject.ext.dep.junit
|
||||
testImplementation rootProject.ext.dep.mockito
|
||||
testImplementation rootProject.ext.dep.robolectric
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -119,7 +120,9 @@ android {
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests.returnDefaultValues = true
|
||||
unitTests{
|
||||
returnDefaultValues = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
@@ -14,22 +14,17 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
// From https://raw.github.com/mcxiaoke/gradle-mvn-push/master/jar.gradle
|
||||
android.libraryVariants.all { variant ->
|
||||
def jarTask = project.tasks.create(name: "jar${variant.name.capitalize()}", type: Jar) {
|
||||
from variant.javaCompile.destinationDir
|
||||
exclude "**/R.class"
|
||||
exclude "**/BuildConfig.class"
|
||||
}
|
||||
jarTask.dependsOn variant.javaCompile
|
||||
artifacts.add('archives', jarTask);
|
||||
}
|
||||
|
||||
// From https://raw.github.com/mcxiaoke/gradle-mvn-push/master/gradle-mvn-push.gradle
|
||||
def isReleaseBuild() {
|
||||
return VERSION_NAME.contains("SNAPSHOT") == false
|
||||
}
|
||||
|
||||
def isLocalBuild() {
|
||||
if (System.getenv('IS_LOCAL_DEVELOPMENT') != null) {
|
||||
return System.getenv('IS_LOCAL_DEVELOPMENT').toBoolean()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
def getReleaseRepositoryUrl() {
|
||||
return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL :
|
||||
"https://oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||
@@ -40,6 +35,10 @@ def getSnapshotRepositoryUrl() {
|
||||
"https://oss.sonatype.org/content/repositories/snapshots/"
|
||||
}
|
||||
|
||||
def obtainMavenLocalUrl() {
|
||||
return getRepositories().mavenLocal().getUrl()
|
||||
}
|
||||
|
||||
def getRepositoryUsername() {
|
||||
return hasProperty('USERNAME') ? USERNAME :
|
||||
(hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "")
|
||||
@@ -50,22 +49,6 @@ def getRepositoryPassword() {
|
||||
(hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "")
|
||||
}
|
||||
|
||||
task apklib(type: Zip) {
|
||||
appendix = extension = 'apklib'
|
||||
|
||||
from 'AndroidManifest.xml'
|
||||
into('res') {
|
||||
from 'res'
|
||||
}
|
||||
into('src') {
|
||||
from 'src'
|
||||
}
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives apklib
|
||||
}
|
||||
|
||||
afterEvaluate { project ->
|
||||
uploadArchives {
|
||||
repositories {
|
||||
@@ -76,24 +59,16 @@ afterEvaluate { project ->
|
||||
pom.artifactId = POM_ARTIFACT_ID
|
||||
pom.version = VERSION_NAME
|
||||
|
||||
repository(url: getReleaseRepositoryUrl()) {
|
||||
authentication(userName: getRepositoryUsername(),
|
||||
password: getRepositoryPassword())
|
||||
if (isLocalBuild()) {
|
||||
repository(url: obtainMavenLocalUrl())
|
||||
} else {
|
||||
repository(url: getReleaseRepositoryUrl()) {
|
||||
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
|
||||
}
|
||||
snapshotRepository(url: getSnapshotRepositoryUrl()) {
|
||||
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
|
||||
}
|
||||
}
|
||||
snapshotRepository(url: getSnapshotRepositoryUrl()) {
|
||||
authentication(userName: getRepositoryUsername(),
|
||||
password: getRepositoryPassword())
|
||||
}
|
||||
|
||||
/*
|
||||
// Leaving out as artifact was incorrectly named when found
|
||||
addFilter('aar') { artifact, file ->
|
||||
artifact.name == archivesBaseName
|
||||
}
|
||||
addFilter('apklib') { artifact, file ->
|
||||
artifact.name == archivesBaseName + '-apklib'
|
||||
}
|
||||
*/
|
||||
|
||||
pom.project {
|
||||
name POM_NAME
|
||||
@@ -151,3 +126,12 @@ afterEvaluate { project ->
|
||||
archives androidJavadocsJar
|
||||
}
|
||||
}
|
||||
|
||||
// See: https://github.com/chrisbanes/gradle-mvn-push/issues/43#issuecomment-84140513
|
||||
afterEvaluate { project ->
|
||||
android.libraryVariants.all { variant ->
|
||||
tasks.androidJavadocs.doFirst {
|
||||
classpath += files(variant.javaCompile.classpath.files)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mapbox.mapboxsdk">
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.mapbox.mapboxsdk">
|
||||
|
||||
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
|
||||
<uses-feature android:name="android.hardware.wifi" android:required="false" /> <!-- Implied by ACCESS_WIFI_STATE. -->
|
||||
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
|
||||
<uses-sdk tools:overrideLibrary="com.mapzen.lost"/>
|
||||
|
||||
<application>
|
||||
<!-- Include the telemetry service to simplify set up (https://www.mapbox.com/telemetry) -->
|
||||
<service android:name="com.mapbox.services.android.telemetry.service.TelemetryService"/>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.mapbox.mapboxsdk.attribution;
|
||||
|
||||
public class Attribution {
|
||||
|
||||
private static final String OPENSTREETMAP = "OpenStreetMap";
|
||||
private static final String OPENSTREETMAP_ABBR = "OSM";
|
||||
static final String TELEMETRY = "Telemetry Settings";
|
||||
|
||||
static final String IMPROVE_MAP_URL = "https://www.mapbox.com/map-feedback/";
|
||||
static final String MAPBOX_URL = "https://www.mapbox.com/about/maps/";
|
||||
static final String TELEMETRY_URL = "https://www.mapbox.com/telemetry/";
|
||||
|
||||
private String title;
|
||||
private String url;
|
||||
|
||||
Attribution(String title, String url) {
|
||||
this.title = title;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getTitleAbbreviated() {
|
||||
if (title.equals(OPENSTREETMAP)) {
|
||||
return OPENSTREETMAP_ABBR;
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Attribution that = (Attribution) o;
|
||||
|
||||
if (title != null ? !title.equals(that.title) : that.title != null) {
|
||||
return false;
|
||||
}
|
||||
return url != null ? url.equals(that.url) : that.url == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = title != null ? title.hashCode() : 0;
|
||||
result = 31 * result + (url != null ? url.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.mapbox.mapboxsdk.attribution;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PointF;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
public class AttributionLayout {
|
||||
|
||||
private Bitmap logo;
|
||||
private PointF anchorPoint;
|
||||
private boolean shortText;
|
||||
|
||||
public AttributionLayout(@Nullable Bitmap logo, @Nullable PointF anchorPoint, boolean shortText) {
|
||||
this.logo = logo;
|
||||
this.anchorPoint = anchorPoint;
|
||||
this.shortText = shortText;
|
||||
}
|
||||
|
||||
public Bitmap getLogo() {
|
||||
return logo;
|
||||
}
|
||||
|
||||
public PointF getAnchorPoint() {
|
||||
return anchorPoint;
|
||||
}
|
||||
|
||||
public boolean isShortText() {
|
||||
return shortText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AttributionLayout that = (AttributionLayout) o;
|
||||
|
||||
if (logo != null ? !logo.equals(that.logo) : that.logo != null) {
|
||||
return false;
|
||||
}
|
||||
return anchorPoint != null ? anchorPoint.equals(that.anchorPoint) : that.anchorPoint == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = logo != null ? logo.hashCode() : 0;
|
||||
result = 31 * result + (anchorPoint != null ? anchorPoint.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AttributionLayout{"
|
||||
+ "logo=" + logo
|
||||
+ ", anchorPoint=" + anchorPoint
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package com.mapbox.mapboxsdk.attribution;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PointF;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class AttributionMeasure {
|
||||
|
||||
private Bitmap logo;
|
||||
private Bitmap logoSmall;
|
||||
private Bitmap snapshot;
|
||||
private TextView textView;
|
||||
private TextView textViewShort;
|
||||
private float margin;
|
||||
|
||||
private boolean shorterText;
|
||||
|
||||
AttributionMeasure(Bitmap snapshot, Bitmap logo, Bitmap logoSmall, TextView tv, TextView tvShort, float margin) {
|
||||
this.snapshot = snapshot;
|
||||
this.logo = logo;
|
||||
this.logoSmall = logoSmall;
|
||||
this.textView = tv;
|
||||
this.textViewShort = tvShort;
|
||||
this.margin = margin;
|
||||
}
|
||||
|
||||
public AttributionLayout measure() {
|
||||
Chain chain = new Chain(
|
||||
new FullLogoLongTextCommand(),
|
||||
new FullLogoShortTextCommand(),
|
||||
new SmallLogoLongTextCommand(),
|
||||
new SmallLogoShortTextCommand(),
|
||||
new LongTextCommand(),
|
||||
new ShortTextCommand(),
|
||||
new NoTextCommand()
|
||||
);
|
||||
|
||||
AttributionLayout attributionLayout = chain.start(this);
|
||||
shorterText = attributionLayout.isShortText();
|
||||
return attributionLayout;
|
||||
}
|
||||
|
||||
|
||||
private static class FullLogoLongTextCommand implements Command {
|
||||
public AttributionLayout execute(AttributionMeasure measure) {
|
||||
float width = measure.getLogoContainerWidth() + measure.getTextViewContainerWidth();
|
||||
boolean fitBounds = width <= measure.getMaxSize();
|
||||
if (fitBounds) {
|
||||
PointF anchor = calculateAnchor(measure.snapshot, measure.textView, measure.margin);
|
||||
return new AttributionLayout(measure.logo, anchor, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FullLogoShortTextCommand implements Command {
|
||||
@Override
|
||||
public AttributionLayout execute(AttributionMeasure measure) {
|
||||
float width = measure.getLogoContainerWidth() + measure.getTextViewShortContainerWidth();
|
||||
boolean fitBounds = width <= measure.getMaxSizeShort();
|
||||
if (fitBounds) {
|
||||
PointF anchor = calculateAnchor(measure.snapshot, measure.textViewShort, measure.margin);
|
||||
return new AttributionLayout(measure.logo, anchor, true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SmallLogoLongTextCommand implements Command {
|
||||
@Override
|
||||
public AttributionLayout execute(AttributionMeasure measure) {
|
||||
float width = measure.getLogoSmallContainerWidth() + measure.getTextViewContainerWidth();
|
||||
boolean fitBounds = width <= measure.getMaxSize();
|
||||
if (fitBounds) {
|
||||
PointF anchor = calculateAnchor(measure.snapshot, measure.textView, measure.margin);
|
||||
return new AttributionLayout(measure.logoSmall, anchor, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SmallLogoShortTextCommand implements Command {
|
||||
@Override
|
||||
public AttributionLayout execute(AttributionMeasure measure) {
|
||||
float width = measure.getLogoContainerWidth() + measure.getTextViewShortContainerWidth();
|
||||
boolean fitBounds = width <= measure.getMaxSizeShort();
|
||||
if (fitBounds) {
|
||||
PointF anchor = calculateAnchor(measure.snapshot, measure.textViewShort, measure.margin);
|
||||
return new AttributionLayout(measure.logoSmall, anchor, true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class LongTextCommand implements Command {
|
||||
@Override
|
||||
public AttributionLayout execute(AttributionMeasure measure) {
|
||||
float width = measure.getTextViewContainerWidth() + measure.margin;
|
||||
boolean fitBounds = width <= measure.getMaxSize();
|
||||
if (fitBounds) {
|
||||
return new AttributionLayout(null, calculateAnchor(measure.snapshot, measure.textView, measure.margin), false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ShortTextCommand implements Command {
|
||||
@Override
|
||||
public AttributionLayout execute(AttributionMeasure measure) {
|
||||
float width = measure.getTextViewShortContainerWidth() + measure.margin;
|
||||
boolean fitBounds = width <= measure.getMaxSizeShort();
|
||||
if (fitBounds) {
|
||||
PointF anchor = calculateAnchor(measure.snapshot, measure.textViewShort, measure.margin);
|
||||
return new AttributionLayout(null, anchor, true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class NoTextCommand implements Command {
|
||||
@Override
|
||||
public AttributionLayout execute(AttributionMeasure measure) {
|
||||
return new AttributionLayout(null, null, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static PointF calculateAnchor(Bitmap snapshot, TextView textView, float margin) {
|
||||
return new PointF(
|
||||
snapshot.getWidth() - textView.getMeasuredWidth() - margin,
|
||||
snapshot.getHeight() - margin - textView.getMeasuredHeight()
|
||||
);
|
||||
}
|
||||
|
||||
public TextView getTextView() {
|
||||
return shorterText ? textViewShort : textView;
|
||||
}
|
||||
|
||||
private class Chain {
|
||||
public List<Command> commands;
|
||||
|
||||
Chain(Command... commands) {
|
||||
this.commands = Arrays.asList(commands);
|
||||
}
|
||||
|
||||
public AttributionLayout start(AttributionMeasure measure) {
|
||||
AttributionLayout attributionLayout = null;
|
||||
for (Command command : commands) {
|
||||
attributionLayout = command.execute(measure);
|
||||
if (attributionLayout != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return attributionLayout;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Command {
|
||||
AttributionLayout execute(AttributionMeasure measure);
|
||||
}
|
||||
|
||||
private float getTextViewContainerWidth() {
|
||||
return textView.getMeasuredWidth() + margin;
|
||||
}
|
||||
|
||||
private float getLogoContainerWidth() {
|
||||
return logo.getWidth() + (2 * margin);
|
||||
}
|
||||
|
||||
private float getTextViewShortContainerWidth() {
|
||||
return textViewShort.getMeasuredWidth() + margin;
|
||||
}
|
||||
|
||||
private float getLogoSmallContainerWidth() {
|
||||
return logoSmall.getWidth() + (2 * margin);
|
||||
}
|
||||
|
||||
private float getMaxSize() {
|
||||
return snapshot.getWidth() * 8 / 10;
|
||||
}
|
||||
|
||||
private float getMaxSizeShort() {
|
||||
return snapshot.getWidth();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Bitmap snapshot;
|
||||
private Bitmap logo;
|
||||
private Bitmap logoSmall;
|
||||
private TextView textView;
|
||||
private TextView textViewShort;
|
||||
private float marginPadding;
|
||||
|
||||
public Builder setSnapshot(Bitmap snapshot) {
|
||||
this.snapshot = snapshot;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setLogo(Bitmap logo) {
|
||||
this.logo = logo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setLogoSmall(Bitmap logoSmall) {
|
||||
this.logoSmall = logoSmall;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTextView(TextView textView) {
|
||||
this.textView = textView;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTextViewShort(TextView textViewShort) {
|
||||
this.textViewShort = textViewShort;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMarginPadding(float marginPadding) {
|
||||
this.marginPadding = marginPadding;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AttributionMeasure build() {
|
||||
return new AttributionMeasure(snapshot, logo, logoSmall, textView, textViewShort, marginPadding);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
package com.mapbox.mapboxsdk.attribution;
|
||||
|
||||
import android.text.Html;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.URLSpan;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Responsible for parsing attribution data coming from Sources and MapSnapshot.
|
||||
* <p>
|
||||
* Exposes multiple configuration options to manipulate data being parsed.
|
||||
* Use the Options object to build these configurations.
|
||||
* </p>
|
||||
*/
|
||||
public class AttributionParser {
|
||||
|
||||
private final Set<Attribution> attributions = new LinkedHashSet<>();
|
||||
private final String attributionData;
|
||||
private final boolean withImproveMap;
|
||||
private final boolean withCopyrightSign;
|
||||
private final boolean withTelemetryAttribution;
|
||||
private final boolean withMapboxAttribution;
|
||||
|
||||
AttributionParser(String attributionData, boolean withImproveMap, boolean withCopyrightSign,
|
||||
boolean withTelemetryAttribution, boolean withMapboxAttribution) {
|
||||
this.attributionData = attributionData;
|
||||
this.withImproveMap = withImproveMap;
|
||||
this.withCopyrightSign = withCopyrightSign;
|
||||
this.withTelemetryAttribution = withTelemetryAttribution;
|
||||
this.withMapboxAttribution = withMapboxAttribution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parsed attributions.
|
||||
*
|
||||
* @return the attributions
|
||||
*/
|
||||
public Set<Attribution> getAttributions() {
|
||||
return attributions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parsed attribution string.
|
||||
*
|
||||
* @return the parsed attribution string
|
||||
*/
|
||||
public String createAttributionString() {
|
||||
return createAttributionString(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parsed attribution string.
|
||||
*
|
||||
* @param shortenedOutput if attribution string should contain shortened output
|
||||
* @return the parsed attribution string
|
||||
*/
|
||||
public String createAttributionString(boolean shortenedOutput) {
|
||||
StringBuilder stringBuilder = new StringBuilder(withCopyrightSign ? "" : "© ");
|
||||
int counter = 0;
|
||||
for (Attribution attribution : attributions) {
|
||||
counter++;
|
||||
stringBuilder.append(!shortenedOutput ? attribution.getTitle() : attribution.getTitleAbbreviated());
|
||||
if (counter != attributions.size()) {
|
||||
stringBuilder.append(" / ");
|
||||
}
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main attribution for configuration
|
||||
*/
|
||||
protected void parse() {
|
||||
parseAttributions();
|
||||
addAdditionalAttributions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse attributions
|
||||
*/
|
||||
private void parseAttributions() {
|
||||
SpannableStringBuilder htmlBuilder = (SpannableStringBuilder) fromHtml(attributionData);
|
||||
URLSpan[] urlSpans = htmlBuilder.getSpans(0, htmlBuilder.length(), URLSpan.class);
|
||||
for (URLSpan urlSpan : urlSpans) {
|
||||
parseUrlSpan(htmlBuilder, urlSpan);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an URLSpan containing an attribution.
|
||||
*
|
||||
* @param htmlBuilder the html builder
|
||||
* @param urlSpan the url span to be parsed
|
||||
*/
|
||||
private void parseUrlSpan(SpannableStringBuilder htmlBuilder, URLSpan urlSpan) {
|
||||
String url = urlSpan.getURL();
|
||||
if (isUrlValid(url)) {
|
||||
String anchor = parseAnchorValue(htmlBuilder, urlSpan);
|
||||
attributions.add(new Attribution(anchor, url));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to validate if an url is valid to be included in the final attribution.
|
||||
*
|
||||
* @param url the url to be validated
|
||||
* @return if the url is valid
|
||||
*/
|
||||
private boolean isUrlValid(String url) {
|
||||
return isValidForImproveThisMap(url) && isValidForMapbox(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to validate if an url is valid for the improve map configuration.
|
||||
*
|
||||
* @param url the url to be validated
|
||||
* @return if the url is valid for improve this map
|
||||
*/
|
||||
private boolean isValidForImproveThisMap(String url) {
|
||||
return withImproveMap || !url.equals(Attribution.IMPROVE_MAP_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to validate if an url is valid for the Mapbox configuration.
|
||||
*
|
||||
* @param url the url to be validated
|
||||
* @return if the url is valid for Mapbox
|
||||
*/
|
||||
private boolean isValidForMapbox(String url) {
|
||||
return withMapboxAttribution || !url.equals(Attribution.MAPBOX_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the attribution by parsing the anchor value of html href tag.
|
||||
*
|
||||
* @param htmlBuilder the html builder
|
||||
* @param urlSpan the current urlSpan
|
||||
* @return the parsed anchor value
|
||||
*/
|
||||
private String parseAnchorValue(SpannableStringBuilder htmlBuilder, URLSpan urlSpan) {
|
||||
int start = htmlBuilder.getSpanStart(urlSpan);
|
||||
int end = htmlBuilder.getSpanEnd(urlSpan);
|
||||
int length = end - start;
|
||||
char[] charKey = new char[length];
|
||||
htmlBuilder.getChars(start, end, charKey, 0);
|
||||
return stripCopyright(String.valueOf(charKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to strip the copyright sign from an attribution
|
||||
*
|
||||
* @param anchor the attribution string to strip
|
||||
* @return the stripped attribution string without the copyright sign
|
||||
*/
|
||||
private String stripCopyright(String anchor) {
|
||||
if (!withCopyrightSign && anchor.startsWith("© ")) {
|
||||
anchor = anchor.substring(2, anchor.length());
|
||||
}
|
||||
return anchor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to manually add attributions
|
||||
*/
|
||||
private void addAdditionalAttributions() {
|
||||
if (withTelemetryAttribution) {
|
||||
attributions.add(new Attribution(Attribution.TELEMETRY, Attribution.TELEMETRY_URL));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to a spanned html representation.
|
||||
*
|
||||
* @param html the string to convert
|
||||
* @return the spanned html representation
|
||||
*/
|
||||
private static Spanned fromHtml(String html) {
|
||||
Spanned result;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
|
||||
result = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY);
|
||||
} else {
|
||||
result = Html.fromHtml(html);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder to configure using an AttributionParser.
|
||||
* <p>
|
||||
* AttributionData, set with {@link #withAttributionData(String...)}, is the only required property to build
|
||||
* the underlying AttributionParser. Other properties include trimming the copyright sign, adding telemetry
|
||||
* attribution or hiding attribution as improve this map and Mapbox.
|
||||
* </p>
|
||||
*/
|
||||
public static class Options {
|
||||
private boolean withImproveMap = true;
|
||||
private boolean withCopyrightSign = true;
|
||||
private boolean withTelemetryAttribution = false;
|
||||
private boolean withMapboxAttribution = true;
|
||||
private String[] attributionDataStringArray;
|
||||
|
||||
public Options withAttributionData(String... attributionData) {
|
||||
this.attributionDataStringArray = attributionData;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Options withImproveMap(boolean withImproveMap) {
|
||||
this.withImproveMap = withImproveMap;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Options withCopyrightSign(boolean withCopyrightSign) {
|
||||
this.withCopyrightSign = withCopyrightSign;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Options withTelemetryAttribution(boolean withTelemetryAttribution) {
|
||||
this.withTelemetryAttribution = withTelemetryAttribution;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Options withMapboxAttribution(boolean withMapboxAttribution) {
|
||||
this.withMapboxAttribution = withMapboxAttribution;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AttributionParser build() {
|
||||
if (attributionDataStringArray == null) {
|
||||
throw new IllegalStateException("Using builder without providing attribution data");
|
||||
}
|
||||
|
||||
String fullAttributionString = parseAttribution(attributionDataStringArray);
|
||||
AttributionParser attributionParser = new AttributionParser(
|
||||
fullAttributionString,
|
||||
withImproveMap,
|
||||
withCopyrightSign,
|
||||
withTelemetryAttribution,
|
||||
withMapboxAttribution
|
||||
);
|
||||
attributionParser.parse();
|
||||
return attributionParser;
|
||||
}
|
||||
|
||||
private String parseAttribution(String[] attribution) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String attr : attribution) {
|
||||
if (!attr.isEmpty()) {
|
||||
builder.append(attr);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import javax.net.ssl.SSLException;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.Dispatcher;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
@@ -31,7 +32,8 @@ import timber.log.Timber;
|
||||
|
||||
class HTTPRequest implements Callback {
|
||||
|
||||
private static OkHttpClient mClient = new OkHttpClient();
|
||||
private static OkHttpClient mClient = new OkHttpClient.Builder().dispatcher(getDispatcher()).build();
|
||||
private static boolean logEnabled = true;
|
||||
private String USER_AGENT_STRING = null;
|
||||
|
||||
private static final int CONNECTION_ERROR = 0;
|
||||
@@ -47,6 +49,14 @@ class HTTPRequest implements Callback {
|
||||
private Call mCall;
|
||||
private Request mRequest;
|
||||
|
||||
private static Dispatcher getDispatcher() {
|
||||
Dispatcher dispatcher = new Dispatcher();
|
||||
// Matches core limit set on
|
||||
// https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/src/http_file_source.cpp#L192
|
||||
dispatcher.setMaxRequestsPerHost(20);
|
||||
return dispatcher;
|
||||
}
|
||||
|
||||
private native void nativeOnFailure(int type, String message);
|
||||
|
||||
private native void nativeOnResponse(int code, String etag, String modified, String cacheControl, String expires,
|
||||
@@ -85,14 +95,7 @@ class HTTPRequest implements Callback {
|
||||
}
|
||||
mRequest = builder.build();
|
||||
mCall = mClient.newCall(mRequest);
|
||||
|
||||
// TODO remove code block for workaround in #10303
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
|
||||
mCall.enqueue(this);
|
||||
} else {
|
||||
// Calling execute instead of enqueue is a workaround for #10303
|
||||
onResponse(mCall, mCall.execute());
|
||||
}
|
||||
mCall.enqueue(this);
|
||||
} catch (Exception exception) {
|
||||
onFailure(exception);
|
||||
}
|
||||
@@ -116,12 +119,15 @@ class HTTPRequest implements Callback {
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
if (response.isSuccessful()) {
|
||||
Timber.v("[HTTP] Request was successful (code = %s).", response.code());
|
||||
} else {
|
||||
// We don't want to call this unsuccessful because a 304 isn't really an error
|
||||
String message = !TextUtils.isEmpty(response.message()) ? response.message() : "No additional information";
|
||||
Timber.d("[HTTP] Request with response code = %s: %s", response.code(), message);
|
||||
|
||||
if (logEnabled) {
|
||||
if (response.isSuccessful()) {
|
||||
Timber.v("[HTTP] Request was successful (code = %s).", response.code());
|
||||
} else {
|
||||
// We don't want to call this unsuccessful because a 304 isn't really an error
|
||||
String message = !TextUtils.isEmpty(response.message()) ? response.message() : "No additional information";
|
||||
Timber.d("[HTTP] Request with response code = %s: %s", response.code(), message);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] body;
|
||||
@@ -165,13 +171,15 @@ class HTTPRequest implements Callback {
|
||||
|
||||
String errorMessage = e.getMessage() != null ? e.getMessage() : "Error processing the request";
|
||||
|
||||
if (type == TEMPORARY_ERROR) {
|
||||
Timber.d("Request failed due to a temporary error: %s", errorMessage);
|
||||
} else if (type == CONNECTION_ERROR) {
|
||||
Timber.i("Request failed due to a connection error: %s", errorMessage);
|
||||
} else {
|
||||
// PERMANENT_ERROR
|
||||
Timber.w("Request failed due to a permanent error: %s", errorMessage);
|
||||
if (logEnabled) {
|
||||
if (type == TEMPORARY_ERROR) {
|
||||
Timber.d("Request failed due to a temporary error: %s", errorMessage);
|
||||
} else if (type == CONNECTION_ERROR) {
|
||||
Timber.i("Request failed due to a connection error: %s", errorMessage);
|
||||
} else {
|
||||
// PERMANENT_ERROR
|
||||
Timber.w("Request failed due to a permanent error: %s", errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
mLock.lock();
|
||||
@@ -205,4 +213,8 @@ class HTTPRequest implements Callback {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static void enableLog(boolean enabled) {
|
||||
logEnabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.mapbox.mapboxsdk.http;
|
||||
|
||||
/**
|
||||
* Utility class for setting HttpRequest configurations
|
||||
*/
|
||||
public class HttpRequestUtil {
|
||||
|
||||
/**
|
||||
* Set the log state of HttpRequest.
|
||||
* <p>
|
||||
* This configuration will outlast the lifecycle of the Map.
|
||||
* </p>
|
||||
*
|
||||
* @param enabled True will enable logging, false will disable
|
||||
*/
|
||||
public static void setLogEnabled(boolean enabled) {
|
||||
HTTPRequest.enableLog(enabled);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ import com.mapzen.android.lost.api.LostApiClient;
|
||||
* @deprecated Use a {@link Mapbox#getLocationEngine()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LocationSource extends LocationEngine implements LocationListener {
|
||||
public class LocationSource extends LocationEngine implements LostApiClient.ConnectionCallbacks, LocationListener {
|
||||
|
||||
private Context context;
|
||||
private LostApiClient lostApiClient;
|
||||
@@ -45,7 +45,9 @@ public class LocationSource extends LocationEngine implements LocationListener {
|
||||
public LocationSource(Context context) {
|
||||
super();
|
||||
this.context = context.getApplicationContext();
|
||||
lostApiClient = new LostApiClient.Builder(this.context).build();
|
||||
lostApiClient = new LostApiClient.Builder(this.context)
|
||||
.addConnectionCallbacks(this)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,12 +63,7 @@ public class LocationSource extends LocationEngine implements LocationListener {
|
||||
*/
|
||||
@Override
|
||||
public void activate() {
|
||||
if (!lostApiClient.isConnected()) {
|
||||
lostApiClient.connect();
|
||||
}
|
||||
for (LocationEngineListener listener : locationListeners) {
|
||||
listener.onConnected();
|
||||
}
|
||||
connect();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +73,7 @@ public class LocationSource extends LocationEngine implements LocationListener {
|
||||
*/
|
||||
@Override
|
||||
public void deactivate() {
|
||||
if (lostApiClient.isConnected()) {
|
||||
if (lostApiClient != null && lostApiClient.isConnected()) {
|
||||
lostApiClient.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -92,6 +89,24 @@ public class LocationSource extends LocationEngine implements LocationListener {
|
||||
return lostApiClient.isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the location provider has connected.
|
||||
*/
|
||||
@Override
|
||||
public void onConnected() {
|
||||
for (LocationEngineListener listener : locationListeners) {
|
||||
listener.onConnected();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the location provider connection has been suspended.
|
||||
*/
|
||||
@Override
|
||||
public void onConnectionSuspended() {
|
||||
// Empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Last known location is the location provider is connected and location permissions are granted.
|
||||
*
|
||||
@@ -102,7 +117,7 @@ public class LocationSource extends LocationEngine implements LocationListener {
|
||||
public Location getLastLocation() {
|
||||
if (lostApiClient.isConnected()) {
|
||||
//noinspection MissingPermission
|
||||
return LocationServices.FusedLocationApi.getLastLocation();
|
||||
return LocationServices.FusedLocationApi.getLastLocation(lostApiClient);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -136,7 +151,7 @@ public class LocationSource extends LocationEngine implements LocationListener {
|
||||
|
||||
if (lostApiClient.isConnected()) {
|
||||
//noinspection MissingPermission
|
||||
LocationServices.FusedLocationApi.requestLocationUpdates(request, this);
|
||||
LocationServices.FusedLocationApi.requestLocationUpdates(lostApiClient, request, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +161,7 @@ public class LocationSource extends LocationEngine implements LocationListener {
|
||||
@Override
|
||||
public void removeLocationUpdates() {
|
||||
if (lostApiClient.isConnected()) {
|
||||
LocationServices.FusedLocationApi.removeLocationUpdates(this);
|
||||
LocationServices.FusedLocationApi.removeLocationUpdates(lostApiClient, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,4 +186,14 @@ public class LocationSource extends LocationEngine implements LocationListener {
|
||||
listener.onLocationChanged(location);
|
||||
}
|
||||
}
|
||||
|
||||
private void connect() {
|
||||
if (lostApiClient != null) {
|
||||
if (lostApiClient.isConnected()) {
|
||||
onConnected();
|
||||
} else {
|
||||
lostApiClient.connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,12 +302,14 @@ class AnnotationManager {
|
||||
}
|
||||
|
||||
for (Marker marker : selectedMarkers) {
|
||||
if (marker.isInfoWindowShown()) {
|
||||
marker.hideInfoWindow();
|
||||
}
|
||||
if (marker != null) {
|
||||
if (marker.isInfoWindowShown()) {
|
||||
marker.hideInfoWindow();
|
||||
}
|
||||
|
||||
if (marker instanceof MarkerView) {
|
||||
markerViewManager.deselect((MarkerView) marker, false);
|
||||
if (marker instanceof MarkerView) {
|
||||
markerViewManager.deselect((MarkerView) marker, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,22 +7,20 @@ import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.URLSpan;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
import com.mapbox.mapboxsdk.attribution.Attribution;
|
||||
import com.mapbox.mapboxsdk.attribution.AttributionParser;
|
||||
import com.mapbox.mapboxsdk.camera.CameraPosition;
|
||||
import com.mapbox.mapboxsdk.style.sources.Source;
|
||||
import com.mapbox.services.android.telemetry.MapboxTelemetry;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Responsible for managing attribution interactions on the map.
|
||||
@@ -39,8 +37,8 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
|
||||
|
||||
private final Context context;
|
||||
private final MapboxMap mapboxMap;
|
||||
private String[] attributionKeys;
|
||||
private HashMap<String, String> attributionMap;
|
||||
private String[] attributionTitles;
|
||||
private Set<Attribution> attributionSet;
|
||||
|
||||
AttributionDialogManager(@NonNull Context context, @NonNull MapboxMap mapboxMap) {
|
||||
this.context = context;
|
||||
@@ -50,18 +48,26 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
|
||||
// Called when someone presses the attribution icon on the map
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
attributionMap = new AttributionBuilder(context, mapboxMap).build();
|
||||
attributionSet = new AttributionBuilder(mapboxMap).build();
|
||||
showAttributionDialog();
|
||||
}
|
||||
|
||||
private void showAttributionDialog() {
|
||||
attributionKeys = attributionMap.keySet().toArray(new String[attributionMap.size()]);
|
||||
attributionTitles = getAttributionTitles();
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setTitle(R.string.mapbox_attributionsDialogTitle);
|
||||
builder.setAdapter(new ArrayAdapter<>(context, R.layout.mapbox_attribution_list_item, attributionKeys), this);
|
||||
builder.setAdapter(new ArrayAdapter<>(context, R.layout.mapbox_attribution_list_item, attributionTitles), this);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private String[] getAttributionTitles() {
|
||||
List<String> titles = new ArrayList<>();
|
||||
for (Attribution attribution : attributionSet) {
|
||||
titles.add(attribution.getTitle());
|
||||
}
|
||||
return titles.toArray(new String[titles.size()]);
|
||||
}
|
||||
|
||||
// Called when someone selects an attribution or telemetry settings from the dialog
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
@@ -73,7 +79,7 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
|
||||
}
|
||||
|
||||
private boolean isLatestEntry(int attributionKeyIndex) {
|
||||
return attributionKeyIndex == attributionKeys.length - 1;
|
||||
return attributionKeyIndex == attributionTitles.length - 1;
|
||||
}
|
||||
|
||||
private void showTelemetryDialog() {
|
||||
@@ -105,7 +111,8 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
|
||||
}
|
||||
|
||||
private void showMapFeedbackWebPage(int which) {
|
||||
String url = attributionMap.get(attributionKeys[which]);
|
||||
Attribution[] attributions = attributionSet.toArray(new Attribution[attributionSet.size()]);
|
||||
String url = attributions[which].getUrl();
|
||||
if (url.contains(MAP_FEEDBACK_URL)) {
|
||||
url = buildMapFeedbackMapUrl(mapboxMap.getCameraPosition());
|
||||
}
|
||||
@@ -132,46 +139,24 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
|
||||
|
||||
private static class AttributionBuilder {
|
||||
|
||||
private final HashMap<String, String> map = new LinkedHashMap<>();
|
||||
private final Context context;
|
||||
private final MapboxMap mapboxMap;
|
||||
|
||||
AttributionBuilder(Context context, MapboxMap mapboxMap) {
|
||||
this.context = context.getApplicationContext();
|
||||
AttributionBuilder(MapboxMap mapboxMap) {
|
||||
this.mapboxMap = mapboxMap;
|
||||
}
|
||||
|
||||
private HashMap<String, String> build() {
|
||||
private Set<Attribution> build() {
|
||||
List<String> attributions = new ArrayList<>();
|
||||
for (Source source : mapboxMap.getSources()) {
|
||||
parseAttribution(source.getAttribution());
|
||||
attributions.add(source.getAttribution());
|
||||
}
|
||||
addTelemetryEntryToAttributionMap();
|
||||
return map;
|
||||
}
|
||||
|
||||
private void parseAttribution(String attributionSource) {
|
||||
if (!TextUtils.isEmpty(attributionSource)) {
|
||||
SpannableStringBuilder htmlBuilder = (SpannableStringBuilder) Html.fromHtml(attributionSource);
|
||||
URLSpan[] urlSpans = htmlBuilder.getSpans(0, htmlBuilder.length(), URLSpan.class);
|
||||
for (URLSpan urlSpan : urlSpans) {
|
||||
map.put(resolveAnchorValue(htmlBuilder, urlSpan), urlSpan.getURL());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveAnchorValue(SpannableStringBuilder htmlBuilder, URLSpan urlSpan) {
|
||||
int start = htmlBuilder.getSpanStart(urlSpan);
|
||||
int end = htmlBuilder.getSpanEnd(urlSpan);
|
||||
int length = end - start;
|
||||
char[] charKey = new char[length];
|
||||
htmlBuilder.getChars(start, end, charKey, 0);
|
||||
return String.valueOf(charKey);
|
||||
}
|
||||
|
||||
private void addTelemetryEntryToAttributionMap() {
|
||||
String telemetryKey = context.getString(R.string.mapbox_telemetrySettings);
|
||||
String telemetryLink = context.getString(R.string.mapbox_telemetryLink);
|
||||
map.put(telemetryKey, telemetryLink);
|
||||
return new AttributionParser.Options()
|
||||
.withCopyrightSign(true)
|
||||
.withImproveMap(true)
|
||||
.withTelemetryAttribution(true)
|
||||
.withAttributionData(attributions.toArray(new String[attributions.size()]))
|
||||
.build().getAttributions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.mapbox.mapboxsdk.maps;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
@@ -9,10 +10,16 @@ import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveCanceledListener;
|
||||
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener;
|
||||
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener;
|
||||
|
||||
/**
|
||||
* Class responsible for dispatching camera change events to registered listeners.
|
||||
*/
|
||||
class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, MapboxMap.OnCameraMoveListener,
|
||||
MapboxMap.OnCameraMoveCanceledListener, OnCameraIdleListener {
|
||||
|
||||
private final Handler handler = new Handler();
|
||||
|
||||
private boolean idle = true;
|
||||
private int moveStartedReason;
|
||||
|
||||
private final CopyOnWriteArrayList<OnCameraMoveStartedListener> onCameraMoveStarted = new CopyOnWriteArrayList<>();
|
||||
private final CopyOnWriteArrayList<OnCameraMoveCanceledListener> onCameraMoveCanceled = new CopyOnWriteArrayList<>();
|
||||
@@ -24,6 +31,74 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
|
||||
private OnCameraMoveListener onCameraMoveListener;
|
||||
private OnCameraIdleListener onCameraIdleListener;
|
||||
|
||||
private final Runnable onCameraMoveStartedRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// deprecated API
|
||||
if (onCameraMoveStartedListener != null) {
|
||||
onCameraMoveStartedListener.onCameraMoveStarted(moveStartedReason);
|
||||
}
|
||||
|
||||
// new API
|
||||
if (!onCameraMoveStarted.isEmpty()) {
|
||||
for (OnCameraMoveStartedListener cameraMoveStartedListener : onCameraMoveStarted) {
|
||||
cameraMoveStartedListener.onCameraMoveStarted(moveStartedReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable onCameraMoveRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// deprecated API
|
||||
if (onCameraMoveListener != null && !idle) {
|
||||
onCameraMoveListener.onCameraMove();
|
||||
}
|
||||
|
||||
// new API
|
||||
if (!onCameraMove.isEmpty() && !idle) {
|
||||
for (OnCameraMoveListener cameraMoveListener : onCameraMove) {
|
||||
cameraMoveListener.onCameraMove();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable onCameraMoveCancelRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// deprecated API
|
||||
if (onCameraMoveCanceledListener != null && !idle) {
|
||||
onCameraMoveCanceledListener.onCameraMoveCanceled();
|
||||
}
|
||||
|
||||
// new API
|
||||
if (!onCameraMoveCanceled.isEmpty() && !idle) {
|
||||
for (OnCameraMoveCanceledListener cameraMoveCanceledListener : onCameraMoveCanceled) {
|
||||
cameraMoveCanceledListener.onCameraMoveCanceled();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable onCameraIdleRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// deprecated API
|
||||
if (onCameraIdleListener != null) {
|
||||
onCameraIdleListener.onCameraIdle();
|
||||
}
|
||||
|
||||
// new API
|
||||
if (!onCameraIdle.isEmpty()) {
|
||||
for (OnCameraIdleListener cameraIdleListener : onCameraIdle) {
|
||||
cameraIdleListener.onCameraIdle();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Deprecated
|
||||
void setOnCameraMoveStartedListener(OnCameraMoveStartedListener onCameraMoveStartedListener) {
|
||||
this.onCameraMoveStartedListener = onCameraMoveStartedListener;
|
||||
@@ -45,16 +120,13 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraMoveStarted(int reason) {
|
||||
public void onCameraMoveStarted(final int reason) {
|
||||
if (!idle) {
|
||||
return;
|
||||
}
|
||||
idle = false;
|
||||
|
||||
// deprecated API
|
||||
if (onCameraMoveStartedListener != null) {
|
||||
onCameraMoveStartedListener.onCameraMoveStarted(reason);
|
||||
}
|
||||
moveStartedReason = reason;
|
||||
handler.post(onCameraMoveStartedRunnable);
|
||||
|
||||
// new API
|
||||
if (!onCameraMoveStarted.isEmpty()) {
|
||||
@@ -66,7 +138,7 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
|
||||
|
||||
@Override
|
||||
public void onCameraMove() {
|
||||
// deprecated API
|
||||
handler.post(onCameraMoveRunnable);
|
||||
if (onCameraMoveListener != null && !idle) {
|
||||
onCameraMoveListener.onCameraMove();
|
||||
}
|
||||
@@ -81,7 +153,7 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
|
||||
|
||||
@Override
|
||||
public void onCameraMoveCanceled() {
|
||||
// deprecated API
|
||||
handler.post(onCameraMoveCancelRunnable);
|
||||
if (onCameraMoveCanceledListener != null && !idle) {
|
||||
onCameraMoveCanceledListener.onCameraMoveCanceled();
|
||||
}
|
||||
@@ -98,7 +170,7 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
|
||||
public void onCameraIdle() {
|
||||
if (!idle) {
|
||||
idle = true;
|
||||
// deprecated API
|
||||
handler.post(onCameraIdleRunnable);
|
||||
if (onCameraIdleListener != null) {
|
||||
onCameraIdleListener.onCameraIdle();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ import android.view.ViewGroup;
|
||||
|
||||
import com.mapbox.mapboxsdk.utils.MapFragmentUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Fragment wrapper around a map view.
|
||||
* <p>
|
||||
@@ -25,10 +28,11 @@ import com.mapbox.mapboxsdk.utils.MapFragmentUtils;
|
||||
*
|
||||
* @see #getMapAsync(OnMapReadyCallback)
|
||||
*/
|
||||
public final class MapFragment extends Fragment {
|
||||
public final class MapFragment extends Fragment implements OnMapReadyCallback {
|
||||
|
||||
private final List<OnMapReadyCallback> mapReadyCallbackList = new ArrayList<>();
|
||||
private MapboxMap mapboxMap;
|
||||
private MapView map;
|
||||
private OnMapReadyCallback onMapReadyCallback;
|
||||
|
||||
/**
|
||||
* Creates a default MapFragment instance
|
||||
@@ -63,7 +67,9 @@ public final class MapFragment extends Fragment {
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
Context context = inflater.getContext();
|
||||
return map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments()));
|
||||
map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments()));
|
||||
map.setVisibility(View.INVISIBLE);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,9 +82,16 @@ public final class MapFragment extends Fragment {
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
map.onCreate(savedInstanceState);
|
||||
if (onMapReadyCallback != null) {
|
||||
map.getMapAsync(onMapReadyCallback);
|
||||
map.getMapAsync(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapReady(MapboxMap mapboxMap) {
|
||||
this.mapboxMap = mapboxMap;
|
||||
for (OnMapReadyCallback onMapReadyCallback : mapReadyCallbackList) {
|
||||
onMapReadyCallback.onMapReady(mapboxMap);
|
||||
}
|
||||
map.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,6 +157,7 @@ public final class MapFragment extends Fragment {
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
map.onDestroy();
|
||||
mapReadyCallbackList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,10 +166,10 @@ public final class MapFragment extends Fragment {
|
||||
* @param onMapReadyCallback The callback to be invoked.
|
||||
*/
|
||||
public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) {
|
||||
if (map == null) {
|
||||
this.onMapReadyCallback = onMapReadyCallback;
|
||||
if (mapboxMap == null) {
|
||||
mapReadyCallbackList.add(onMapReadyCallback);
|
||||
} else {
|
||||
map.getMapAsync(onMapReadyCallback);
|
||||
onMapReadyCallback.onMapReady(mapboxMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,7 +550,7 @@ final class MapGestureDetector {
|
||||
return super.onScale(detector);
|
||||
}
|
||||
|
||||
wasZoomingIn = (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) >= 0;
|
||||
wasZoomingIn = (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) > 0;
|
||||
if (tiltGestureOccurred) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -712,19 +712,21 @@ public final class MapboxMap {
|
||||
* @param callback the callback to be invoked when an animation finishes or is canceled
|
||||
*/
|
||||
public final void moveCamera(final CameraUpdate update, final MapboxMap.CancelableCallback callback) {
|
||||
new Handler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
transform.moveCamera(MapboxMap.this, update, callback);
|
||||
// MapChange.REGION_DID_CHANGE_ANIMATED is not called for `jumpTo`
|
||||
// invalidate camera position to provide OnCameraChange event.
|
||||
invalidateCameraPosition();
|
||||
transform.moveCamera(MapboxMap.this, update, callback);
|
||||
// MapChange.REGION_DID_CHANGE_ANIMATED is not called for `jumpTo`
|
||||
// invalidate camera position to provide OnCameraChange event.
|
||||
invalidateCameraPosition();
|
||||
|
||||
if (callback != null) {
|
||||
callback.onFinish();
|
||||
if (callback != null) {
|
||||
new Handler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (callback != null) {
|
||||
callback.onFinish();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -848,12 +850,7 @@ public final class MapboxMap {
|
||||
if (durationMs <= 0) {
|
||||
throw new IllegalArgumentException("Null duration passed into easeCamera");
|
||||
}
|
||||
new Handler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, callback, isDismissable);
|
||||
}
|
||||
});
|
||||
transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, callback, isDismissable);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -923,12 +920,8 @@ public final class MapboxMap {
|
||||
if (durationMs <= 0) {
|
||||
throw new IllegalArgumentException("Null duration passed into animageCamera");
|
||||
}
|
||||
new Handler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
transform.animateCamera(MapboxMap.this, update, durationMs, callback);
|
||||
}
|
||||
});
|
||||
|
||||
transform.animateCamera(MapboxMap.this, update, durationMs, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -725,9 +725,12 @@ public class MapboxMapOptions implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the font-family for generating glyphs locally for ideographs in the ‘CJK Unified Ideographs’
|
||||
* Set the font family for generating glyphs locally for ideographs in the ‘CJK Unified Ideographs’
|
||||
* and ‘Hangul Syllables’ ranges.
|
||||
*
|
||||
* The font family argument is passed to {@link android.graphics.Typeface#create(String, int)}.
|
||||
* Default system fonts are defined in '/system/etc/fonts.xml'
|
||||
*
|
||||
* @param fontFamily font family for local ideograph generation.
|
||||
* @return This
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,9 @@ import android.view.ViewGroup;
|
||||
|
||||
import com.mapbox.mapboxsdk.utils.MapFragmentUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Support Fragment wrapper around a map view.
|
||||
* <p>
|
||||
@@ -25,10 +28,11 @@ import com.mapbox.mapboxsdk.utils.MapFragmentUtils;
|
||||
*
|
||||
* @see #getMapAsync(OnMapReadyCallback)
|
||||
*/
|
||||
public class SupportMapFragment extends Fragment {
|
||||
public class SupportMapFragment extends Fragment implements OnMapReadyCallback {
|
||||
|
||||
private final List<OnMapReadyCallback> mapReadyCallbackList = new ArrayList<>();
|
||||
private MapboxMap mapboxMap;
|
||||
private MapView map;
|
||||
private OnMapReadyCallback onMapReadyCallback;
|
||||
|
||||
/**
|
||||
* Creates a default MapFragment instance
|
||||
@@ -63,7 +67,9 @@ public class SupportMapFragment extends Fragment {
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
Context context = inflater.getContext();
|
||||
return map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments()));
|
||||
map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments()));
|
||||
map.setVisibility(View.INVISIBLE);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,9 +82,16 @@ public class SupportMapFragment extends Fragment {
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
map.onCreate(savedInstanceState);
|
||||
if (onMapReadyCallback != null) {
|
||||
map.getMapAsync(onMapReadyCallback);
|
||||
map.getMapAsync(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapReady(MapboxMap mapboxMap) {
|
||||
this.mapboxMap = mapboxMap;
|
||||
for (OnMapReadyCallback onMapReadyCallback : mapReadyCallbackList) {
|
||||
onMapReadyCallback.onMapReady(mapboxMap);
|
||||
}
|
||||
map.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,6 +157,7 @@ public class SupportMapFragment extends Fragment {
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
map.onDestroy();
|
||||
mapReadyCallbackList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,10 +166,10 @@ public class SupportMapFragment extends Fragment {
|
||||
* @param onMapReadyCallback The callback to be invoked.
|
||||
*/
|
||||
public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) {
|
||||
if (map == null) {
|
||||
this.onMapReadyCallback = onMapReadyCallback;
|
||||
if (mapboxMap == null) {
|
||||
mapReadyCallbackList.add(onMapReadyCallback);
|
||||
} else {
|
||||
map.getMapAsync(onMapReadyCallback);
|
||||
onMapReadyCallback.onMapReady(mapboxMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.mapbox.mapboxsdk.maps;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
@@ -82,8 +83,15 @@ final class Transform implements MapView.OnMapChangedListener {
|
||||
if (change == REGION_DID_CHANGE_ANIMATED) {
|
||||
updateCameraPosition(invalidateCameraPosition());
|
||||
if (cameraCancelableCallback != null) {
|
||||
cameraCancelableCallback.onFinish();
|
||||
cameraCancelableCallback = null;
|
||||
new Handler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (cameraCancelableCallback != null) {
|
||||
cameraCancelableCallback.onFinish();
|
||||
cameraCancelableCallback = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
cameraChangeDispatcher.onCameraIdle();
|
||||
mapView.removeOnMapChangedListener(this);
|
||||
@@ -175,8 +183,15 @@ final class Transform implements MapView.OnMapChangedListener {
|
||||
// notify animateCamera and easeCamera about cancelling
|
||||
if (cameraCancelableCallback != null) {
|
||||
cameraChangeDispatcher.onCameraIdle();
|
||||
cameraCancelableCallback.onCancel();
|
||||
cameraCancelableCallback = null;
|
||||
new Handler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (cameraCancelableCallback != null) {
|
||||
cameraCancelableCallback.onCancel();
|
||||
cameraCancelableCallback = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// cancel ongoing transitions
|
||||
|
||||
@@ -33,7 +33,6 @@ import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
|
||||
import com.mapbox.mapboxsdk.constants.MyBearingTracking;
|
||||
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.location.LocationSource;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.Projection;
|
||||
import com.mapbox.services.android.telemetry.location.LocationEngine;
|
||||
@@ -162,7 +161,7 @@ public class MyLocationView extends View {
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void init(LocationSource locationSource) {
|
||||
public void init(LocationEngine locationSource) {
|
||||
this.locationEngine = locationSource;
|
||||
}
|
||||
|
||||
|
||||
@@ -204,6 +204,7 @@ public class OfflineManager {
|
||||
}
|
||||
|
||||
ConnectivityReceiver.instance(context).activate();
|
||||
FileSource.getInstance(context).activate();
|
||||
createOfflineRegion(fileSource, definition, metadata, new CreateOfflineRegionCallback() {
|
||||
|
||||
@Override
|
||||
@@ -212,6 +213,7 @@ public class OfflineManager {
|
||||
@Override
|
||||
public void run() {
|
||||
ConnectivityReceiver.instance(context).deactivate();
|
||||
FileSource.getInstance(context).deactivate();
|
||||
callback.onCreate(offlineRegion);
|
||||
}
|
||||
});
|
||||
@@ -223,6 +225,7 @@ public class OfflineManager {
|
||||
@Override
|
||||
public void run() {
|
||||
ConnectivityReceiver.instance(context).deactivate();
|
||||
FileSource.getInstance(context).deactivate();
|
||||
callback.onError(error);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -400,9 +400,6 @@ public class OfflineRegion {
|
||||
* When the operation is complete or encounters an error, the given callback will be
|
||||
* executed on the main thread.
|
||||
* </p>
|
||||
* <p>
|
||||
* After you call this method, you may not call any additional methods on this object.
|
||||
* </p>
|
||||
*
|
||||
* @param bytes the metadata in bytes
|
||||
* @param callback the callback to be invoked
|
||||
|
||||
@@ -28,7 +28,7 @@ public class MapSnapshot {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the bitmap
|
||||
* @return the large
|
||||
*/
|
||||
public Bitmap getBitmap() {
|
||||
return bitmap;
|
||||
|
||||
@@ -5,19 +5,31 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.PointF;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.content.res.ResourcesCompat;
|
||||
import android.text.Html;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
import com.mapbox.mapboxsdk.attribution.AttributionLayout;
|
||||
import com.mapbox.mapboxsdk.attribution.AttributionMeasure;
|
||||
import com.mapbox.mapboxsdk.attribution.AttributionParser;
|
||||
import com.mapbox.mapboxsdk.camera.CameraPosition;
|
||||
import com.mapbox.mapboxsdk.constants.Style;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
|
||||
import com.mapbox.mapboxsdk.storage.FileSource;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* The map snapshotter creates a bitmap of the map, rendered
|
||||
* The map snapshotter creates a large of the map, rendered
|
||||
* off the UI thread. The snapshotter itself must be used on
|
||||
* the UI thread (for access to the main looper)
|
||||
*/
|
||||
@@ -269,43 +281,126 @@ public class MapSnapshotter {
|
||||
* @param mapSnapshot the map snapshot to draw the overlay on
|
||||
*/
|
||||
protected void addOverlay(MapSnapshot mapSnapshot) {
|
||||
Bitmap original = mapSnapshot.getBitmap();
|
||||
Canvas canvas = new Canvas(original);
|
||||
addLogo(canvas, original);
|
||||
Bitmap snapshot = mapSnapshot.getBitmap();
|
||||
Canvas canvas = new Canvas(snapshot);
|
||||
int margin = (int) context.getResources().getDisplayMetrics().density * LOGO_MARGIN_DP;
|
||||
drawOverlay(mapSnapshot, snapshot, canvas, margin);
|
||||
}
|
||||
|
||||
private void drawOverlay(MapSnapshot mapSnapshot, Bitmap snapshot, Canvas canvas, int margin) {
|
||||
AttributionMeasure measure = getAttributionMeasure(mapSnapshot, snapshot, margin);
|
||||
AttributionLayout layout = measure.measure();
|
||||
drawLogo(mapSnapshot, canvas, margin, layout);
|
||||
drawAttribution(mapSnapshot, canvas, measure, layout);
|
||||
}
|
||||
|
||||
private AttributionMeasure getAttributionMeasure(MapSnapshot mapSnapshot, Bitmap snapshot, int margin) {
|
||||
Logo logo = createScaledLogo(snapshot);
|
||||
TextView longText = createTextView(mapSnapshot, false, logo.getScale());
|
||||
TextView shortText = createTextView(mapSnapshot, true, logo.getScale());
|
||||
|
||||
return new AttributionMeasure.Builder()
|
||||
.setSnapshot(snapshot)
|
||||
.setLogo(logo.getLarge())
|
||||
.setLogoSmall(logo.getSmall())
|
||||
.setTextView(longText)
|
||||
.setTextViewShort(shortText)
|
||||
.setMarginPadding(margin)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void drawLogo(MapSnapshot mapSnapshot, Canvas canvas, int margin, AttributionLayout layout) {
|
||||
if (mapSnapshot.isShowLogo()) {
|
||||
drawLogo(mapSnapshot.getBitmap(), canvas, margin, layout);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawLogo(Bitmap snapshot, Canvas canvas, int margin, AttributionLayout placement) {
|
||||
Bitmap selectedLogo = placement.getLogo();
|
||||
if (selectedLogo != null) {
|
||||
canvas.drawBitmap(selectedLogo, margin, snapshot.getHeight() - selectedLogo.getHeight() - margin, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawAttribution(MapSnapshot mapSnapshot, Canvas canvas,
|
||||
AttributionMeasure measure, AttributionLayout layout) {
|
||||
// draw attribution
|
||||
PointF anchorPoint = layout.getAnchorPoint();
|
||||
if (anchorPoint != null) {
|
||||
drawAttribution(canvas, measure, anchorPoint);
|
||||
} else {
|
||||
Bitmap snapshot = mapSnapshot.getBitmap();
|
||||
Timber.e("Could not generate attribution for snapshot size: %s x %s."
|
||||
+ " You are required to provide your own attribution for the used sources: %s",
|
||||
snapshot.getWidth(), snapshot.getHeight(), mapSnapshot.getAttributions());
|
||||
}
|
||||
}
|
||||
|
||||
private void drawAttribution(Canvas canvas, AttributionMeasure measure, PointF anchorPoint) {
|
||||
canvas.save();
|
||||
canvas.translate(anchorPoint.x, anchorPoint.y);
|
||||
measure.getTextView().draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
private TextView createTextView(MapSnapshot mapSnapshot, boolean shortText, float scale) {
|
||||
int textColor = ResourcesCompat.getColor(context.getResources(), R.color.mapbox_gray_dark, context.getTheme());
|
||||
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
|
||||
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
|
||||
TextView textView = new TextView(context);
|
||||
textView.setLayoutParams(new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
);
|
||||
textView.setSingleLine(true);
|
||||
textView.setTextSize(10 * scale);
|
||||
textView.setTextColor(textColor);
|
||||
textView.setBackgroundResource(R.drawable.mapbox_rounded_corner);
|
||||
textView.setText(Html.fromHtml(createAttributionString(mapSnapshot, shortText)));
|
||||
textView.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
|
||||
return textView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a logo on the canvas created from the map snapshot.
|
||||
* Create the attribution string.
|
||||
*
|
||||
* @param canvas the canvas to draw the bitmap on
|
||||
* @param original the map snapshot image
|
||||
* @param mapSnapshot the map snapshot to create the attribution for
|
||||
* @param shortText indicates if the short variant of the string should be parsed
|
||||
* @return the parsed attribution string
|
||||
*/
|
||||
private void addLogo(Canvas canvas, Bitmap original) {
|
||||
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
|
||||
float margin = displayMetrics.density * LOGO_MARGIN_DP;
|
||||
Bitmap logo = createScaledLogo(original);
|
||||
canvas.drawBitmap(logo, margin, original.getHeight() - logo.getHeight() - margin, null);
|
||||
private String createAttributionString(MapSnapshot mapSnapshot, boolean shortText) {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(mapSnapshot.getAttributions())
|
||||
.withCopyrightSign(false)
|
||||
.withImproveMap(false)
|
||||
.build();
|
||||
|
||||
return attributionParser.createAttributionString(shortText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a scaled logo for a map snapshot.
|
||||
*
|
||||
* @param snapshot the map snapshot where the logo should be placed on
|
||||
* @return the scaled bitmap logo
|
||||
* @return the scaled large logo
|
||||
*/
|
||||
private Bitmap createScaledLogo(Bitmap snapshot) {
|
||||
private Logo createScaledLogo(@NonNull Bitmap snapshot) {
|
||||
Bitmap logo = BitmapFactory.decodeResource(context.getResources(), R.drawable.mapbox_logo_icon, null);
|
||||
float scale = calculateLogoScale(snapshot, logo);
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.postScale(scale, scale);
|
||||
return Bitmap.createBitmap(logo, 0, 0, logo.getWidth(), logo.getHeight(), matrix, true);
|
||||
Bitmap helmet = BitmapFactory.decodeResource(context.getResources(), R.drawable.mapbox_logo_helmet, null);
|
||||
Bitmap large = Bitmap.createBitmap(logo, 0, 0, logo.getWidth(), logo.getHeight(), matrix, true);
|
||||
Bitmap small = Bitmap.createBitmap(helmet, 0, 0, helmet.getWidth(), helmet.getHeight(), matrix, true);
|
||||
return new Logo(large, small, scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the scale of the logo, only allow downscaling.
|
||||
*
|
||||
* @param snapshot the bitmap of the map snapshot
|
||||
* @param logo the bitmap of the mapbox logo
|
||||
* @param snapshot the large of the map snapshot
|
||||
* @param logo the large of the mapbox logo
|
||||
* @return the scale value
|
||||
*/
|
||||
private float calculateLogoScale(Bitmap snapshot, Bitmap logo) {
|
||||
@@ -315,7 +410,14 @@ public class MapSnapshotter {
|
||||
float prefWidth = logo.getWidth() / widthRatio;
|
||||
float prefHeight = logo.getHeight() / heightRatio;
|
||||
float calculatedScale = Math.min(prefWidth / logo.getWidth(), prefHeight / logo.getHeight()) * 2;
|
||||
return calculatedScale < 1 ? calculatedScale : 1.0f;
|
||||
if (calculatedScale > 1) {
|
||||
// don't allow over-scaling
|
||||
calculatedScale = 1.0f;
|
||||
} else if (calculatedScale < 0.60f) {
|
||||
// don't scale to low either
|
||||
calculatedScale = 0.60f;
|
||||
}
|
||||
return calculatedScale;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,14 +426,17 @@ public class MapSnapshotter {
|
||||
*
|
||||
* @param snapshot the generated snapshot
|
||||
*/
|
||||
protected void onSnapshotReady(MapSnapshot snapshot) {
|
||||
if (callback != null) {
|
||||
if (snapshot.isShowLogo()) {
|
||||
addOverlay(snapshot);
|
||||
protected void onSnapshotReady(final MapSnapshot snapshot) {
|
||||
new Handler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (callback != null) {
|
||||
addOverlay(snapshot);
|
||||
callback.onSnapshotReady(snapshot);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
callback.onSnapshotReady(snapshot);
|
||||
reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,4 +469,28 @@ public class MapSnapshotter {
|
||||
|
||||
@Override
|
||||
protected native void finalize() throws Throwable;
|
||||
|
||||
private class Logo {
|
||||
private Bitmap large;
|
||||
private Bitmap small;
|
||||
private float scale;
|
||||
|
||||
public Logo(Bitmap large, Bitmap small, float scale) {
|
||||
this.large = large;
|
||||
this.small = small;
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
public Bitmap getLarge() {
|
||||
return large;
|
||||
}
|
||||
|
||||
public Bitmap getSmall() {
|
||||
return small;
|
||||
}
|
||||
|
||||
public float getScale() {
|
||||
return scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,14 @@ public class LocalGlyphRasterizer {
|
||||
|
||||
/***
|
||||
* Uses Android-native drawing code to rasterize a single glyph
|
||||
* to a square @{link Bitmap} which can be returned to portable
|
||||
* to a square {@link Bitmap} which can be returned to portable
|
||||
* code for transformation into a Signed Distance Field glyph.
|
||||
*
|
||||
* @param fontFamily Font family string to pass to Typeface.create
|
||||
* @param bold If true, use Typeface.BOLD option
|
||||
* @param glyphID 16-bit Unicode BMP codepoint to draw
|
||||
*
|
||||
* @return Return a @{link Bitmap} to be displayed in the requested tile.
|
||||
* @return Return a {@link Bitmap} to be displayed in the requested tile.
|
||||
*/
|
||||
@WorkerThread
|
||||
protected static Bitmap drawGlyphBitmap(String fontFamily, boolean bold, char glyphID) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.mapbox.mapboxsdk.utils;
|
||||
|
||||
/**
|
||||
* Comparisons from std sdk, which aren't available in API level <= 15
|
||||
* Comparisons from std sdk, which aren't available in API level 15 and below
|
||||
*/
|
||||
public class Compare {
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 950 B |
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#A6FFFFFF"/>
|
||||
<padding
|
||||
android:left="7.5dp"
|
||||
android:right="7.5dp"
|
||||
android:top="2dp"
|
||||
android:bottom="3dp"/>
|
||||
<corners android:radius="7.5dp"/>
|
||||
</shape>
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="mapbox_gray_dark">#5F5F5F</color>
|
||||
<color name="mapbox_gray">#7D7F80</color>
|
||||
<color name="mapbox_blue">#1E8CAB</color>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
package com.mapbox.mapboxsdk.attribution;
|
||||
|
||||
import com.mapbox.mapboxsdk.BuildConfig;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(constants = BuildConfig.class)
|
||||
public class AttributionParseTest {
|
||||
|
||||
private static final String STREETS_ATTRIBUTION = "<a href=\"https://www.mapbox.com/about/maps/\" target=\"_blank\">© Mapbox</a> <a href=\"http://www.openstreetmap.org/about/\" target=\"_blank\">© OpenStreetMap</a> <a class=\"mapbox-improve-map\" href=\"https://www.mapbox.com/map-feedback/\" target=\"_blank\">Improve this map</a>\n";
|
||||
private static final String SATELLITE_ATTRIBUTION = "<a href=\"https://www.mapbox.com/about/maps/\" target=\"_blank\">© Mapbox</a> <a href=\"http://www.openstreetmap.org/about/\" target=\"_blank\">© OpenStreetMap</a> <a class=\"mapbox-improve-map\" href=\"https://www.mapbox.com/map-feedback/\" target=\"_blank\">Improve this map</a> <a href=\"https://www.digitalglobe.com/\" target=\"_blank\">© DigitalGlobe</a>\n";
|
||||
|
||||
@Test
|
||||
public void testParseAttributionStringSatellite() throws Exception {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(SATELLITE_ATTRIBUTION)
|
||||
.build();
|
||||
|
||||
Set<Attribution> attributionList = attributionParser.getAttributions();
|
||||
assertEquals("Size of list should match", 4, attributionList.size());
|
||||
|
||||
int counter = 0;
|
||||
for (Attribution attribution : attributionList) {
|
||||
switch (counter) {
|
||||
case 0:
|
||||
assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl());
|
||||
assertEquals("Title mapbox should match", "© Mapbox", attribution.getTitle());
|
||||
break;
|
||||
case 1:
|
||||
assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl());
|
||||
assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle());
|
||||
break;
|
||||
case 2:
|
||||
assertEquals("URL improve map should match", "https://www.mapbox.com/map-feedback/", attribution.getUrl());
|
||||
assertEquals("Title improve map should match", "Improve this map", attribution.getTitle());
|
||||
break;
|
||||
case 3:
|
||||
assertEquals("URL digital globe should match", "https://www.digitalglobe.com/", attribution.getUrl());
|
||||
assertEquals("Title digital globe should match", "© DigitalGlobe", attribution.getTitle());
|
||||
break;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAttributionStringStreets() throws Exception {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(STREETS_ATTRIBUTION)
|
||||
.build();
|
||||
|
||||
Set<Attribution> attributionList = attributionParser.getAttributions();
|
||||
assertEquals("Size of list should match", 3, attributionList.size());
|
||||
|
||||
int counter = 0;
|
||||
for (Attribution attribution : attributionList) {
|
||||
switch (counter) {
|
||||
case 0:
|
||||
assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl());
|
||||
assertEquals("Title mapbox should match", "© Mapbox", attribution.getTitle());
|
||||
break;
|
||||
case 1:
|
||||
assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl());
|
||||
assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle());
|
||||
break;
|
||||
case 2:
|
||||
assertEquals("URL improve map should match", "https://www.mapbox.com/map-feedback/", attribution.getUrl());
|
||||
assertEquals("Title improve map should match", "Improve this map", attribution.getTitle());
|
||||
break;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAttributionWithoutMapbox() throws Exception {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(STREETS_ATTRIBUTION)
|
||||
.withMapboxAttribution(false)
|
||||
.build();
|
||||
|
||||
Set<Attribution> attributionList = attributionParser.getAttributions();
|
||||
assertEquals("Size of list should match", 2, attributionList.size());
|
||||
|
||||
int counter = 0;
|
||||
for (Attribution attribution : attributionList) {
|
||||
switch (counter) {
|
||||
case 0:
|
||||
assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl());
|
||||
assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle());
|
||||
break;
|
||||
case 1:
|
||||
assertEquals("URL improve map should match", "https://www.mapbox.com/map-feedback/", attribution.getUrl());
|
||||
assertEquals("Title improve map should match", "Improve this map", attribution.getTitle());
|
||||
break;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAttributionArrayString() throws Exception {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(new String[] {STREETS_ATTRIBUTION, "", SATELLITE_ATTRIBUTION})
|
||||
.build();
|
||||
Set<Attribution> attributionList = attributionParser.getAttributions();
|
||||
assertEquals("Size of list should match", 4, attributionList.size());
|
||||
|
||||
int counter = 0;
|
||||
for (Attribution attribution : attributionList) {
|
||||
switch (counter) {
|
||||
case 0:
|
||||
assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl());
|
||||
assertEquals("Title mapbox should match", "© Mapbox", attribution.getTitle());
|
||||
break;
|
||||
case 1:
|
||||
assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl());
|
||||
assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle());
|
||||
break;
|
||||
case 2:
|
||||
assertEquals("URL improve map should match", "https://www.mapbox.com/map-feedback/", attribution.getUrl());
|
||||
assertEquals("Title improve map should match", "Improve this map", attribution.getTitle());
|
||||
break;
|
||||
case 3:
|
||||
assertEquals("URL digital globe should match", "https://www.digitalglobe.com/", attribution.getUrl());
|
||||
assertEquals("Title digital globe should match", "© DigitalGlobe", attribution.getTitle());
|
||||
break;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHideImproveThisMapAttributionArrayString() throws Exception {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(SATELLITE_ATTRIBUTION)
|
||||
.withImproveMap(false)
|
||||
.build();
|
||||
Set<Attribution> attributionList = attributionParser.getAttributions();
|
||||
assertEquals("Size of list should match", 3, attributionList.size());
|
||||
|
||||
int counter = 0;
|
||||
for (Attribution attribution : attributionList) {
|
||||
switch (counter) {
|
||||
case 0:
|
||||
assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl());
|
||||
assertEquals("Title mapbox should match", "© Mapbox", attribution.getTitle());
|
||||
break;
|
||||
case 1:
|
||||
assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl());
|
||||
assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle());
|
||||
break;
|
||||
case 2:
|
||||
assertEquals("URL digital globe should match", "https://www.digitalglobe.com/", attribution.getUrl());
|
||||
assertEquals("Title digital globe should match", "© DigitalGlobe", attribution.getTitle());
|
||||
break;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseHideCopyrightAttributionArrayString() throws Exception {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(STREETS_ATTRIBUTION, "", SATELLITE_ATTRIBUTION)
|
||||
.withCopyrightSign(false)
|
||||
.build();
|
||||
Set<Attribution> attributionList = attributionParser.getAttributions();
|
||||
assertEquals("Size of list should match", 4, attributionList.size());
|
||||
|
||||
int counter = 0;
|
||||
for (Attribution attribution : attributionList) {
|
||||
switch (counter) {
|
||||
case 0:
|
||||
assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl());
|
||||
assertEquals("Title mapbox should match", "Mapbox", attribution.getTitle());
|
||||
break;
|
||||
case 1:
|
||||
assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl());
|
||||
assertEquals("Title openstreetmap should match", "OpenStreetMap", attribution.getTitle());
|
||||
break;
|
||||
case 2:
|
||||
assertEquals("URL improve map should match", "https://www.mapbox.com/map-feedback/", attribution.getUrl());
|
||||
assertEquals("Title improve map should match", "Improve this map", attribution.getTitle());
|
||||
break;
|
||||
case 3:
|
||||
assertEquals("URL digital globe should match", "https://www.digitalglobe.com/", attribution.getUrl());
|
||||
assertEquals("Title digital globe should match", "DigitalGlobe", attribution.getTitle());
|
||||
break;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutputWithoutCopyRightString() throws Exception {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(STREETS_ATTRIBUTION)
|
||||
.withCopyrightSign(false)
|
||||
.withImproveMap(false)
|
||||
.build();
|
||||
|
||||
assertEquals(
|
||||
"Attribution string should match",
|
||||
"© Mapbox / OpenStreetMap",
|
||||
attributionParser.createAttributionString()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testOutputWithCopyRightString() throws Exception {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(STREETS_ATTRIBUTION)
|
||||
.withImproveMap(false)
|
||||
.build();
|
||||
|
||||
assertEquals(
|
||||
"Attribution string should match",
|
||||
"© Mapbox / © OpenStreetMap",
|
||||
attributionParser.createAttributionString()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutputWithoutCopyRightWithoutMapboxString() throws Exception {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(STREETS_ATTRIBUTION)
|
||||
.withCopyrightSign(false)
|
||||
.withImproveMap(false)
|
||||
.withMapboxAttribution(false)
|
||||
.build();
|
||||
|
||||
assertEquals(
|
||||
"Attribution string should match",
|
||||
"© OpenStreetMap",
|
||||
attributionParser.createAttributionString()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutputWithCopyRightWithoutMapboxString() throws Exception {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(STREETS_ATTRIBUTION)
|
||||
.withImproveMap(false)
|
||||
.withMapboxAttribution(false)
|
||||
.build();
|
||||
|
||||
assertEquals(
|
||||
"Attribution string should match",
|
||||
"© OpenStreetMap",
|
||||
attributionParser.createAttributionString()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutputSatelliteString() throws Exception {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(STREETS_ATTRIBUTION, SATELLITE_ATTRIBUTION, "blabla", "")
|
||||
.withImproveMap(false)
|
||||
.withCopyrightSign(false)
|
||||
.withMapboxAttribution(false)
|
||||
.build();
|
||||
|
||||
assertEquals(
|
||||
"Attribution string should match",
|
||||
"© OpenStreetMap / DigitalGlobe",
|
||||
attributionParser.createAttributionString()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShortOpenStreetMapString() throws Exception {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(STREETS_ATTRIBUTION, SATELLITE_ATTRIBUTION, "blabla", "")
|
||||
.withImproveMap(false)
|
||||
.withCopyrightSign(false)
|
||||
.withMapboxAttribution(false)
|
||||
.build();
|
||||
|
||||
assertEquals(
|
||||
"Attribution string should match",
|
||||
"© OSM / DigitalGlobe",
|
||||
attributionParser.createAttributionString(true)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShortOpenStreetMapWithoutCopyrightString() throws Exception {
|
||||
AttributionParser attributionParser = new AttributionParser.Options()
|
||||
.withAttributionData(STREETS_ATTRIBUTION, SATELLITE_ATTRIBUTION, "blabla", "")
|
||||
.withImproveMap(false)
|
||||
.withCopyrightSign(false)
|
||||
.build();
|
||||
|
||||
assertEquals(
|
||||
"Attribution string should match",
|
||||
"© Mapbox / OSM / DigitalGlobe",
|
||||
attributionParser.createAttributionString(true)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.maps;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
public class CameraChangeDispatcherTest {
|
||||
|
||||
@Test
|
||||
public void testSetCameraIdleListener() {
|
||||
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
|
||||
MapboxMap.OnCameraIdleListener listener = mock(MapboxMap.OnCameraIdleListener.class);
|
||||
dispatcher.setOnCameraIdleListener(listener);
|
||||
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
|
||||
dispatcher.onCameraIdle();
|
||||
verify(listener).onCameraIdle();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetCameraMoveStartedListener() {
|
||||
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
|
||||
MapboxMap.OnCameraMoveStartedListener listener = mock(MapboxMap.OnCameraMoveStartedListener.class);
|
||||
dispatcher.setOnCameraMoveStartedListener(listener);
|
||||
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
|
||||
verify(listener).onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetCameraMoveCancelListener() {
|
||||
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
|
||||
MapboxMap.OnCameraMoveCanceledListener listener = mock(MapboxMap.OnCameraMoveCanceledListener.class);
|
||||
dispatcher.setOnCameraMoveCanceledListener(listener);
|
||||
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
|
||||
dispatcher.onCameraMoveCanceled();
|
||||
verify(listener).onCameraMoveCanceled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetCameraMoveListener() {
|
||||
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
|
||||
MapboxMap.OnCameraMoveListener listener = mock(MapboxMap.OnCameraMoveListener.class);
|
||||
dispatcher.setOnCameraMoveListener(listener);
|
||||
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
|
||||
dispatcher.onCameraMove();
|
||||
verify(listener).onCameraMove();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddCameraIdleListener() {
|
||||
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
|
||||
MapboxMap.OnCameraIdleListener listener = mock(MapboxMap.OnCameraIdleListener.class);
|
||||
dispatcher.addOnCameraIdleListener(listener);
|
||||
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
|
||||
dispatcher.onCameraIdle();
|
||||
verify(listener).onCameraIdle();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddCameraMoveStartedListener() {
|
||||
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
|
||||
MapboxMap.OnCameraMoveStartedListener listener = mock(MapboxMap.OnCameraMoveStartedListener.class);
|
||||
dispatcher.addOnCameraMoveStartedListener(listener);
|
||||
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
|
||||
verify(listener).onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddCameraMoveCancelListener() {
|
||||
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
|
||||
MapboxMap.OnCameraMoveCanceledListener listener = mock(MapboxMap.OnCameraMoveCanceledListener.class);
|
||||
dispatcher.addOnCameraMoveCancelListener(listener);
|
||||
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
|
||||
dispatcher.onCameraMoveCanceled();
|
||||
verify(listener).onCameraMoveCanceled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddCameraMoveListener() {
|
||||
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
|
||||
MapboxMap.OnCameraMoveListener listener = mock(MapboxMap.OnCameraMoveListener.class);
|
||||
dispatcher.addOnCameraMoveListener(listener);
|
||||
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
|
||||
dispatcher.onCameraMove();
|
||||
verify(listener).onCameraMove();
|
||||
}
|
||||
}
|
||||
@@ -806,4 +806,4 @@
|
||||
<!-- android:value="true" /> -->
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
||||
@@ -16,6 +16,7 @@ import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.mapbox.mapboxsdk.constants.Style;
|
||||
import com.mapbox.mapboxsdk.http.HttpRequestUtil;
|
||||
import com.mapbox.mapboxsdk.maps.MapView;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
|
||||
@@ -54,6 +55,7 @@ public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCa
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
HttpRequestUtil.setLogEnabled(false);
|
||||
setContentView(R.layout.activity_debug_mode);
|
||||
setupToolbar();
|
||||
setupMapView(savedInstanceState);
|
||||
@@ -204,6 +206,7 @@ public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCa
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mapView.onDestroy();
|
||||
HttpRequestUtil.setLogEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -68,7 +68,7 @@ public class MapSnapshotterActivity extends AppCompatActivity {
|
||||
.withPixelRatio(1)
|
||||
|
||||
// Optionally the style
|
||||
.withStyle((column + row) % 2 == 0 ? Style.TRAFFIC_DAY : Style.DARK);
|
||||
.withStyle((column + row) % 2 == 0 ? Style.MAPBOX_STREETS : Style.DARK);
|
||||
|
||||
// Optionally the visible region
|
||||
if (row % 2 == 0) {
|
||||
|
||||
@@ -9,14 +9,12 @@ import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.mapbox.mapboxsdk.camera.CameraPosition;
|
||||
import com.mapbox.mapboxsdk.constants.Style;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.snapshotter.MapSnapshot;
|
||||
import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter;
|
||||
import com.mapbox.mapboxsdk.testapp.R;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
@@ -46,7 +44,7 @@ public class MapSnapshotterMarkerActivity extends AppCompatActivity implements M
|
||||
getApplicationContext(),
|
||||
new MapSnapshotter
|
||||
.Options(Math.min(container.getMeasuredWidth(), 1024), Math.min(container.getMeasuredHeight(), 1024))
|
||||
.withStyle(Style.TRAFFIC_DAY)
|
||||
.withStyle(Style.OUTDOORS)
|
||||
.withCameraPosition(new CameraPosition.Builder().target(new LatLng(52.090737, 5.121420)).zoom(15).build())
|
||||
);
|
||||
mapSnapshotter.start(MapSnapshotterMarkerActivity.this);
|
||||
@@ -54,6 +52,12 @@ public class MapSnapshotterMarkerActivity extends AppCompatActivity implements M
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
mapSnapshotter.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSnapshotReady(MapSnapshot snapshot) {
|
||||
Timber.i("Snapshot ready");
|
||||
|
||||
@@ -69,4 +69,4 @@
|
||||
<string name="description_textureview_animate">Animate a map rendered on a TextureView</string>
|
||||
<string name="description_grid_source">Example Custom Geometry Source</string>
|
||||
<string name="description_local_glyph">Suzhou using Droid Sans for Chinese glyphs</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ext {
|
||||
minSdkVersion = 15
|
||||
minSdkVersion = 14
|
||||
targetSdkVersion = 25
|
||||
compileSdkVersion = 25
|
||||
buildToolsVersion = "26.0.3"
|
||||
@@ -7,7 +7,7 @@ ext {
|
||||
versionCode = 12
|
||||
versionName = "6.0.0"
|
||||
|
||||
mapboxServicesVersion = "2.2.8"
|
||||
mapboxServicesVersion = "2.2.9"
|
||||
supportLibVersion = "25.4.0"
|
||||
espressoVersion = '3.0.1'
|
||||
testRunnerVersion = '1.0.1'
|
||||
@@ -20,11 +20,12 @@ ext {
|
||||
mapboxAndroidTelemetry : "com.mapbox.mapboxsdk:mapbox-android-telemetry:${mapboxServicesVersion}@aar",
|
||||
|
||||
// mapzen lost
|
||||
lost : 'com.mapzen.android:lost:1.1.1',
|
||||
lost : 'com.mapzen.android:lost:3.0.4',
|
||||
|
||||
// unit test
|
||||
junit : 'junit:junit:4.12',
|
||||
mockito : 'org.mockito:mockito-core:2.10.0',
|
||||
robolectric : 'org.robolectric:robolectric:3.5.1',
|
||||
|
||||
// instrumentation test
|
||||
testRunner : "com.android.support.test:runner:${testRunnerVersion}",
|
||||
@@ -41,7 +42,7 @@ ext {
|
||||
|
||||
// square crew
|
||||
timber : 'com.jakewharton.timber:timber:4.5.1',
|
||||
okhttp3 : 'com.squareup.okhttp3:okhttp:3.9.0',
|
||||
okhttp3 : 'com.squareup.okhttp3:okhttp:3.9.1',
|
||||
leakCanaryDebug : "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}",
|
||||
leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}"
|
||||
]
|
||||
|
||||
@@ -289,3 +289,9 @@
|
||||
/* West longitude format, short: {longitude} */
|
||||
"COORD_W_SHORT" = "%@W";
|
||||
|
||||
/* OpenStreetMap full name attribution */
|
||||
"OSM_FULL_NAME" = "OpenStreetMap";
|
||||
|
||||
/* OpenStreetMap short name attribution */
|
||||
"OSM_SHORT_NAME" = "OSM";
|
||||
|
||||
|
||||
78
platform/darwin/resources/ar.lproj/Foundation.stringsdict
Normal file
78
platform/darwin/resources/ar.lproj/Foundation.stringsdict
Normal file
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>COORD_DEG_LONG</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@degrees@</string>
|
||||
<key>degrees</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>zero</key>
|
||||
<string>0 درجة</string>
|
||||
<key>one</key>
|
||||
<string>درجة واحدة</string>
|
||||
<key>two</key>
|
||||
<string>درجتان</string>
|
||||
<key>few</key>
|
||||
<string>%d درجات</string>
|
||||
<key>many</key>
|
||||
<string>%d درجة</string>
|
||||
<key>other</key>
|
||||
<string>%d درجة</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>COORD_MIN_LONG</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@minutes@</string>
|
||||
<key>minutes</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>zero</key>
|
||||
<string>أقل من دقيقة</string>
|
||||
<key>one</key>
|
||||
<string>دقيقة واحدة</string>
|
||||
<key>two</key>
|
||||
<string>دقيقتان</string>
|
||||
<key>few</key>
|
||||
<string>%d دقائق</string>
|
||||
<key>many</key>
|
||||
<string>%d دقيقة</string>
|
||||
<key>other</key>
|
||||
<string>%d دقيقة</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>COORD_SEC_LONG</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@seconds@</string>
|
||||
<key>seconds</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>zero</key>
|
||||
<string>أقل من ثانية</string>
|
||||
<key>one</key>
|
||||
<string>ثانية واحدة</string>
|
||||
<key>two</key>
|
||||
<string>ثانيتان</string>
|
||||
<key>few</key>
|
||||
<string>%d ثوان</string>
|
||||
<key>many</key>
|
||||
<string>%d ثانية</string>
|
||||
<key>other</key>
|
||||
<string>%d ثانية</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Clock position format, long: {hours} o’clock */
|
||||
/* Clock position format, long: {hours} o’clock */
|
||||
"CLOCK_FMT_LONG" = "%@ часа";
|
||||
|
||||
/* Clock position format, medium: {hours} o’clock */
|
||||
@@ -289,3 +289,8 @@
|
||||
/* West longitude format, short: {longitude} */
|
||||
"COORD_W_SHORT" = "%@З";
|
||||
|
||||
/* OpenStreetMap full name attribution */
|
||||
"OSM_FULL_NAME" = "OpenStreetMap";
|
||||
|
||||
/* OpenStreetMap short name attribution */
|
||||
"OSM_SHORT_NAME" = "OSM";
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
/* Clock position format, long: {hours} o’clock */
|
||||
/* Clock position format, long: {hours} o’clock */
|
||||
"CLOCK_FMT_LONG" = "%@ en punto";
|
||||
|
||||
/* Clock position format, medium: {hours} o’clock */
|
||||
@@ -289,3 +289,9 @@
|
||||
/* West longitude format, short: {longitude} */
|
||||
"COORD_W_SHORT" = "%@O";
|
||||
|
||||
/* OpenStreetMap full name attribution */
|
||||
"OSM_FULL_NAME" = "OpenStreetMap";
|
||||
|
||||
/* OpenStreetMap short name attribution */
|
||||
"OSM_SHORT_NAME" = "OSM";
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
/* Clock position format, long: {hours} o’clock */
|
||||
/* Clock position format, long: {hours} o’clock */
|
||||
"CLOCK_FMT_LONG" = "%@ giờ";
|
||||
|
||||
/* Clock position format, medium: {hours} o’clock */
|
||||
@@ -289,3 +289,9 @@
|
||||
/* West longitude format, short: {longitude} */
|
||||
"COORD_W_SHORT" = "%@T";
|
||||
|
||||
/* OpenStreetMap full name attribution */
|
||||
"OSM_FULL_NAME" = "OpenStreetMap";
|
||||
|
||||
/* OpenStreetMap short name attribution */
|
||||
"OSM_SHORT_NAME" = "OSM";
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -7,6 +7,24 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
The attribution info is represented in the longest format available.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, MGLAttributionInfoStyle) {
|
||||
/**
|
||||
Specifies a short attribution info style.
|
||||
*/
|
||||
MGLAttributionInfoStyleShort = 1,
|
||||
/**
|
||||
Specifies a medium attribution info style.
|
||||
*/
|
||||
MGLAttributionInfoStyleMedium,
|
||||
/**
|
||||
Specifies a long attribution info style.
|
||||
*/
|
||||
MGLAttributionInfoStyleLong
|
||||
};
|
||||
|
||||
/**
|
||||
Information about an attribution statement, usually a copyright or trademark
|
||||
statement, associated with a map content source.
|
||||
@@ -59,6 +77,18 @@ MGL_EXPORT
|
||||
*/
|
||||
- (nullable NSURL *)feedbackURLAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel;
|
||||
|
||||
/**
|
||||
Returns a copy of the current `title` formatted accordingly to `style`.
|
||||
|
||||
Example: If the `style` property is set to `MGLAttributionInfoStyleShort` and the
|
||||
`title` property is set to `OpenStreetMap`, then this method returns `OSM`.
|
||||
|
||||
@param style The attribution info style.
|
||||
|
||||
@return The `NSAttributedString` styled title.
|
||||
*/
|
||||
- (NSAttributedString *)titleWithStyle:(MGLAttributionInfoStyle)style;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -127,6 +127,15 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(nullable NSZone *)zone
|
||||
{
|
||||
MGLAttributionInfo *info = [[[self class] allocWithZone:zone] initWithTitle:_title
|
||||
URL:_URL];
|
||||
info.feedbackLink = _feedbackLink;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
- (nullable NSURL *)feedbackURLAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel {
|
||||
return [self feedbackURLForStyleURL:nil atCenterCoordinate:centerCoordinate zoomLevel:zoomLevel direction:0 pitch:0];
|
||||
}
|
||||
@@ -160,6 +169,25 @@
|
||||
return components.URL;
|
||||
}
|
||||
|
||||
- (NSAttributedString *)titleWithStyle:(MGLAttributionInfoStyle)style
|
||||
{
|
||||
NSString *openStreetMap = NSLocalizedStringWithDefaultValue(@"OSM_FULL_NAME", @"Foundation", nil, @"OpenStreetMap", @"OpenStreetMap full name attribution");
|
||||
NSString *OSM = NSLocalizedStringWithDefaultValue(@"OSM_SHORT_NAME", @"Foundation", nil, @"OSM", @"OpenStreetMap short name attribution");
|
||||
|
||||
NSMutableAttributedString *title = [[NSMutableAttributedString alloc] initWithAttributedString:self.title];
|
||||
[title removeAttribute:NSUnderlineStyleAttributeName range:NSMakeRange(0, [title.string length])];
|
||||
|
||||
BOOL isAbbreviated = (style == MGLAttributionInfoStyleShort);
|
||||
|
||||
if ([title.string rangeOfString:@"OpenStreetMap"].location != NSNotFound) {
|
||||
[title.mutableString replaceOccurrencesOfString:@"OpenStreetMap" withString:isAbbreviated ? OSM : openStreetMap
|
||||
options:NSCaseInsensitiveSearch
|
||||
range:NSMakeRange(0, [title.mutableString length])];
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
return [object isKindOfClass:[self class]] && [[object title] isEqual:self.title] && [[object URL] isEqual:self.URL];
|
||||
}
|
||||
|
||||
@@ -38,11 +38,8 @@ MGL_EXPORT
|
||||
bottomLeft: CLLocationCoordinate2D(latitude: 37.936, longitude: -80.425),
|
||||
bottomRight: CLLocationCoordinate2D(latitude: 37.936, longitude: -71.516),
|
||||
topRight: CLLocationCoordinate2D(latitude: 46.437, longitude: -71.516))
|
||||
let source = MGLImageSource(identifier: "radar-source", coordinateQuad: coordinates, url: URL(string: "https://www.mapbox.com/mapbox-gl-js/assets/radar.gif")!)
|
||||
let source = MGLImageSource(identifier: "radar", coordinateQuad: coordinates, url: URL(string: "https://www.mapbox.com/mapbox-gl-js/assets/radar.gif")!)
|
||||
mapView.style?.addSource(source)
|
||||
|
||||
let layer = MGLRasterStyleLayer(identifier: "radar-layer", source: source)
|
||||
style.addLayer(layer)
|
||||
```
|
||||
*/
|
||||
MGL_EXPORT
|
||||
|
||||
@@ -14,9 +14,10 @@ MGL_EXPORT
|
||||
/**
|
||||
Creates a set of options with the minimum required information.
|
||||
|
||||
@param styleURL URL of the map style to snapshot. The URL may be a full HTTP or HTTPS URL,
|
||||
a Mapbox URL indicating the style’s map ID (`mapbox://styles/{user}/{style`}), or a path
|
||||
to a local file relative to the application’s resource path. Specify `nil` for the default style.
|
||||
@param styleURL URL of the map style to snapshot. The URL may be a full HTTP or
|
||||
HTTPS URL, a Mapbox URL indicating the style’s map ID
|
||||
(`mapbox://styles/{user}/{style}`), or a path to a local file relative to
|
||||
the application’s resource path. Specify `nil` for the default style.
|
||||
@param size The image size.
|
||||
*/
|
||||
- (instancetype)initWithStyleURL:(nullable NSURL *)styleURL camera:(MGLMapCamera *)camera size:(CGSize)size;
|
||||
@@ -31,17 +32,18 @@ MGL_EXPORT
|
||||
/**
|
||||
The zoom level.
|
||||
|
||||
The default zoom level is 0. If this property is non-zero and the camera property
|
||||
is non-nil, the camera’s altitude is ignored in favor of this property’s value.
|
||||
The default zoom level is 0. If this property is non-zero and the camera
|
||||
property is non-nil, the camera’s altitude is ignored in favor of this
|
||||
property’s value.
|
||||
*/
|
||||
@property (nonatomic) double zoomLevel;
|
||||
|
||||
/**
|
||||
A camera representing the viewport visible in the snapshot.
|
||||
|
||||
If this property is non-nil and the `coordinateBounds` property is set to a non-empty
|
||||
coordinate bounds, the camera’s center coordinate and altitude are ignored in favor
|
||||
of the `coordinateBounds` property.
|
||||
If this property is non-nil and the `coordinateBounds` property is set to a
|
||||
non-empty coordinate bounds, the camera’s center coordinate and altitude are
|
||||
ignored in favor of the `coordinateBounds` property.
|
||||
*/
|
||||
@property (nonatomic) MGLMapCamera *camera;
|
||||
|
||||
@@ -78,24 +80,26 @@ MGL_EXPORT
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
/**
|
||||
Converts the specified map coordinate to a point in the coordinate space of the image.
|
||||
Converts the specified map coordinate to a point in the coordinate space of the
|
||||
image.
|
||||
*/
|
||||
- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate;
|
||||
|
||||
/**
|
||||
The image of the map’s content.
|
||||
*/
|
||||
@property(nonatomic, readonly) UIImage *image;
|
||||
@property (nonatomic, readonly) UIImage *image;
|
||||
#else
|
||||
/**
|
||||
Converts the specified map coordinate to a point in the coordinate space of the image.
|
||||
Converts the specified map coordinate to a point in the coordinate space of the
|
||||
image.
|
||||
*/
|
||||
- (NSPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate;
|
||||
|
||||
/**
|
||||
The image of the map’s content.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSImage *image;
|
||||
@property (nonatomic, readonly) NSImage *image;
|
||||
#endif
|
||||
|
||||
@end
|
||||
@@ -103,13 +107,33 @@ MGL_EXPORT
|
||||
/**
|
||||
A block to processes the result or error of a snapshot request.
|
||||
|
||||
@param snapshot The `MGLMapSnapshot` that was generated or `nil` if an error occurred.
|
||||
@param snapshot The `MGLMapSnapshot` that was generated or `nil` if an error
|
||||
occurred.
|
||||
@param error The error that occured or `nil` when successful.
|
||||
*/
|
||||
typedef void (^MGLMapSnapshotCompletionHandler)(MGLMapSnapshot* _Nullable snapshot, NSError* _Nullable error);
|
||||
|
||||
/**
|
||||
An immutable utility object for capturing map-based images.
|
||||
An `MGLMapSnapshotter` generates static raster images of the map. Each snapshot
|
||||
image depicts a portion of a map defined by an `MGLMapSnapshotOptions` object
|
||||
you provide. The snapshotter generates an `MGLMapSnapshot` object
|
||||
asynchronously, passing it into a completion handler once tiles and other
|
||||
resources needed for the snapshot are finished loading.
|
||||
|
||||
You can change the snapshotter’s options at any time and reuse the snapshotter
|
||||
for multiple distinct snapshots; however, the snapshotter can only generate one
|
||||
snapshot at a time. If you need to generate multiple snapshots concurrently,
|
||||
create multiple snapshotter objects.
|
||||
|
||||
For an interactive map, use the `MGLMapView` class. Both `MGLMapSnapshotter`
|
||||
and `MGLMapView` are compatible with offline packs managed by the
|
||||
`MGLOfflineStorage` class.
|
||||
|
||||
From a snapshot, you can obtain an image and convert geographic coordinates to
|
||||
the image’s coordinate space in order to superimpose markers and overlays. If
|
||||
you do not need offline map functionality, you can use the `Snapshot` class in
|
||||
[MapboxStatic.swift](https://github.com/mapbox/MapboxStatic.swift/) to generate
|
||||
static map images with overlays.
|
||||
|
||||
### Example
|
||||
|
||||
@@ -121,18 +145,25 @@ typedef void (^MGLMapSnapshotCompletionHandler)(MGLMapSnapshot* _Nullable snapsh
|
||||
|
||||
let snapshotter = MGLMapSnapshotter(options: options)
|
||||
snapshotter.start { (snapshot, error) in
|
||||
if error != nil {
|
||||
// error handler
|
||||
} else {
|
||||
// image handler
|
||||
if let error = error {
|
||||
fatalError(error.localizedDescription)
|
||||
}
|
||||
|
||||
image = snapshot?.image
|
||||
}
|
||||
```
|
||||
*/
|
||||
MGL_EXPORT
|
||||
@interface MGLMapSnapshotter : NSObject
|
||||
|
||||
- (instancetype)initWithOptions:(MGLMapSnapshotOptions*)options;
|
||||
/**
|
||||
Initializes and returns a map snapshotter object that produces snapshots
|
||||
according to the given options.
|
||||
|
||||
@param options The options to use when generating a map snapshot.
|
||||
@return An initialized map snapshotter.
|
||||
*/
|
||||
- (instancetype)initWithOptions:(MGLMapSnapshotOptions *)options;
|
||||
|
||||
/**
|
||||
Starts the snapshot creation and executes the specified block with the result.
|
||||
@@ -142,7 +173,8 @@ MGL_EXPORT
|
||||
- (void)startWithCompletionHandler:(MGLMapSnapshotCompletionHandler)completionHandler;
|
||||
|
||||
/**
|
||||
Starts the snapshot creation and executes the specified block with the result on the specified queue.
|
||||
Starts the snapshot creation and executes the specified block with the result
|
||||
on the specified queue.
|
||||
|
||||
@param queue The queue to handle the result on.
|
||||
@param completionHandler The block to handle the result in.
|
||||
@@ -152,49 +184,15 @@ MGL_EXPORT
|
||||
/**
|
||||
Cancels the snapshot creation request, if any.
|
||||
|
||||
Once you call this method, you cannot resume the snapshot. In order to obtain the
|
||||
snapshot, create a new `MGLMapSnapshotter` object.
|
||||
Once you call this method, you cannot resume the snapshot. In order to obtain
|
||||
the snapshot, create a new `MGLMapSnapshotter` object.
|
||||
*/
|
||||
- (void)cancel;
|
||||
|
||||
/**
|
||||
The zoom level.
|
||||
|
||||
The default zoom level is 0. If this property is non-zero and the camera property
|
||||
is non-nil, the camera’s altitude is ignored in favor of this property’s value.
|
||||
The options to use when generating a map snapshot.
|
||||
*/
|
||||
@property (nonatomic) double zoomLevel;
|
||||
|
||||
/**
|
||||
A camera representing the viewport visible in the snapshot.
|
||||
|
||||
If this property is non-nil and the `coordinateBounds` property is set to a non-empty
|
||||
coordinate bounds, the camera’s center coordinate and altitude are ignored in favor
|
||||
of the `coordinateBounds` property.
|
||||
*/
|
||||
@property (nonatomic) MGLMapCamera *camera;
|
||||
|
||||
/**
|
||||
The coordinate rectangle that encompasses the bounds to capture.
|
||||
|
||||
If this property is non-empty and the camera property is non-nil, the camera’s
|
||||
center coordinate and altitude are ignored in favor of this property’s value.
|
||||
*/
|
||||
@property (nonatomic) MGLCoordinateBounds coordinateBounds;
|
||||
|
||||
/**
|
||||
URL of the map style to snapshot.
|
||||
|
||||
The URL may be a full HTTP or HTTPS URL, a Mapbox URL indicating the style’s
|
||||
map ID (`mapbox://styles/{user}/{style`}), or a path to a local file relative
|
||||
to the application’s resource path. Specify `nil` for the default style.
|
||||
*/
|
||||
@property (nonatomic, nullable) NSURL *styleURL;
|
||||
|
||||
/**
|
||||
The size of the output image, measured in points.
|
||||
*/
|
||||
@property (nonatomic) CGSize size;
|
||||
@property (nonatomic) MGLMapSnapshotOptions *options;
|
||||
|
||||
/**
|
||||
Indicates whether a snapshot is currently being generated.
|
||||
|
||||
@@ -61,6 +61,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
|
||||
@implementation MGLMapSnapshot {
|
||||
mbgl::MapSnapshotter::PointForFn _pointForFn;
|
||||
}
|
||||
|
||||
- (instancetype)initWithImage:(nullable MGLImage *)image scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn
|
||||
{
|
||||
self = [super init];
|
||||
@@ -80,7 +81,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
|
||||
@end
|
||||
|
||||
@interface MGLMapSnapshotter()
|
||||
@property (nonatomic) MGLMapSnapshotOptions *options;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MGLMapSnapshotter {
|
||||
@@ -88,46 +89,16 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
|
||||
std::shared_ptr<mbgl::ThreadPool> _mbglThreadPool;
|
||||
std::unique_ptr<mbgl::MapSnapshotter> _mbglMapSnapshotter;
|
||||
std::unique_ptr<mbgl::Actor<mbgl::MapSnapshotter::Callback>> _snapshotCallback;
|
||||
NS_ARRAY_OF(MGLAttributionInfo *) *_attributionInfo;
|
||||
}
|
||||
|
||||
- (instancetype)initWithOptions:(MGLMapSnapshotOptions *)options
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_options = options;
|
||||
[self setOptions:options];
|
||||
_loading = false;
|
||||
|
||||
mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
|
||||
_mbglThreadPool = mbgl::sharedThreadPool();
|
||||
|
||||
std::string styleURL = std::string([options.styleURL.absoluteString UTF8String]);
|
||||
|
||||
// Size; taking into account the minimum texture size for OpenGL ES
|
||||
// For non retina screens the ratio is 1:1 MGLSnapshotterMinimumPixelSize
|
||||
mbgl::Size size = {
|
||||
static_cast<uint32_t>(MAX(options.size.width, MGLSnapshotterMinimumPixelSize)),
|
||||
static_cast<uint32_t>(MAX(options.size.height, MGLSnapshotterMinimumPixelSize))
|
||||
};
|
||||
|
||||
float pixelRatio = MAX(options.scale, 1);
|
||||
|
||||
// Camera options
|
||||
mbgl::CameraOptions cameraOptions;
|
||||
if (CLLocationCoordinate2DIsValid(options.camera.centerCoordinate)) {
|
||||
cameraOptions.center = MGLLatLngFromLocationCoordinate2D(options.camera.centerCoordinate);
|
||||
}
|
||||
cameraOptions.angle = MAX(0, options.camera.heading) * mbgl::util::DEG2RAD;
|
||||
cameraOptions.zoom = MAX(0, options.zoomLevel);
|
||||
cameraOptions.pitch = MAX(0, options.camera.pitch);
|
||||
|
||||
// Region
|
||||
mbgl::optional<mbgl::LatLngBounds> coordinateBounds;
|
||||
if (!MGLCoordinateBoundsIsEmpty(options.coordinateBounds)) {
|
||||
coordinateBounds = MGLLatLngBoundsFromCoordinateBounds(options.coordinateBounds);
|
||||
}
|
||||
|
||||
// Create the snapshotter
|
||||
_mbglMapSnapshotter = std::make_unique<mbgl::MapSnapshotter>(*mbglFileSource, *_mbglThreadPool, styleURL, size, pixelRatio, cameraOptions, coordinateBounds);
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -167,14 +138,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
|
||||
[infos growArrayByAddingAttributionInfosFromArray:tileSetInfos];
|
||||
}
|
||||
|
||||
CGSize attributionBackgroundSize = CGSizeMake(10, 0);
|
||||
for (MGLAttributionInfo *info in infos) {
|
||||
if (info.isFeedbackLink) {
|
||||
continue;
|
||||
}
|
||||
attributionBackgroundSize.width += [info.title size].width + 10;
|
||||
attributionBackgroundSize.height = MAX([info.title size].height, attributionBackgroundSize.height);
|
||||
}
|
||||
_attributionInfo = infos;
|
||||
|
||||
if (mbglError) {
|
||||
NSString *description = @(mbgl::util::toString(mbglError).c_str());
|
||||
@@ -198,11 +162,29 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
|
||||
dispatch_queue_t workQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
dispatch_async(workQueue, ^{
|
||||
#if TARGET_OS_IPHONE
|
||||
UIImage *logoImage = [UIImage imageNamed:@"mapbox" inBundle:[NSBundle mgl_frameworkBundle] compatibleWithTraitCollection:nil];
|
||||
MGLAttributionInfoStyle attributionInfoStyle = MGLAttributionInfoStyleLong;
|
||||
for (NSUInteger styleValue = MGLAttributionInfoStyleLong; styleValue >= MGLAttributionInfoStyleShort; styleValue--) {
|
||||
attributionInfoStyle = (MGLAttributionInfoStyle)styleValue;
|
||||
CGSize attributionSize = [self attributionSizeWithLogoStyle:attributionInfoStyle sourceAttributionStyle:attributionInfoStyle];
|
||||
if (attributionSize.width <= mglImage.size.width) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UIImage *logoImage = [self logoImageWithStyle:attributionInfoStyle];
|
||||
CGSize attributionBackgroundSize = [self attributionTextSizeWithStyle:attributionInfoStyle];
|
||||
|
||||
CGRect logoImageRect = CGRectMake(MGLLogoImagePosition.x, mglImage.size.height - (MGLLogoImagePosition.y + logoImage.size.height), logoImage.size.width, logoImage.size.height);
|
||||
CGRect attributionBackgroundFrame = CGRectMake(mglImage.size.width - 10 - attributionBackgroundSize.width,
|
||||
logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2) + 1,
|
||||
CGPoint attributionOrigin = CGPointMake(mglImage.size.width - 10 - attributionBackgroundSize.width,
|
||||
logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2) + 1);
|
||||
if (!logoImage) {
|
||||
CGSize defaultLogoSize = [self mapboxLongStyleLogo].size;
|
||||
logoImageRect = CGRectMake(0, mglImage.size.height - (MGLLogoImagePosition.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,
|
||||
attributionOrigin.y,
|
||||
attributionBackgroundSize.width,
|
||||
attributionBackgroundSize.height);
|
||||
CGPoint attributionTextPosition = CGPointMake(attributionBackgroundFrame.origin.x + 10,
|
||||
@@ -231,20 +213,39 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
|
||||
|
||||
[blurredAttributionBackground drawInRect:attributionBackgroundFrame];
|
||||
|
||||
[self drawAttributionText:infos origin:attributionTextPosition];
|
||||
[self drawAttributionTextWithStyle:attributionInfoStyle origin:attributionTextPosition];
|
||||
|
||||
UIImage *compositedImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
|
||||
UIGraphicsEndImageContext();
|
||||
#else
|
||||
NSImage *logoImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mgl_frameworkBundle] pathForResource:@"mapbox" ofType:@"pdf"]];
|
||||
NSImage *sourceImage = mglImage;
|
||||
|
||||
NSSize targetSize = NSMakeSize(self.options.size.width, self.options.size.height);
|
||||
NSRect targetFrame = NSMakeRect(0, 0, targetSize.width, targetSize.height);
|
||||
|
||||
MGLAttributionInfoStyle attributionInfoStyle = MGLAttributionInfoStyleLong;
|
||||
for (NSUInteger styleValue = MGLAttributionInfoStyleLong; styleValue >= MGLAttributionInfoStyleShort; styleValue--) {
|
||||
attributionInfoStyle = (MGLAttributionInfoStyle)styleValue;
|
||||
CGSize attributionSize = [self attributionSizeWithLogoStyle:attributionInfoStyle sourceAttributionStyle:attributionInfoStyle];
|
||||
if (attributionSize.width <= mglImage.size.width) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NSImage *logoImage = [self logoImageWithStyle:attributionInfoStyle];
|
||||
CGSize attributionBackgroundSize = [self attributionTextSizeWithStyle:attributionInfoStyle];
|
||||
NSImage *sourceImage = mglImage;
|
||||
|
||||
CGRect logoImageRect = CGRectMake(MGLLogoImagePosition.x, MGLLogoImagePosition.y, logoImage.size.width, logoImage.size.height);
|
||||
CGRect attributionBackgroundFrame = CGRectMake(targetFrame.size.width - 10 - attributionBackgroundSize.width,
|
||||
MGLLogoImagePosition.y + 1,
|
||||
CGPoint attributionOrigin = CGPointMake(targetFrame.size.width - 10 - attributionBackgroundSize.width,
|
||||
MGLLogoImagePosition.y + 1);
|
||||
if (!logoImage) {
|
||||
CGSize defaultLogoSize = [self mapboxLongStyleLogo].size;
|
||||
logoImageRect = CGRectMake(0, MGLLogoImagePosition.y, 0, defaultLogoSize.height);
|
||||
attributionOrigin = CGPointMake(10, attributionOrigin.y);
|
||||
}
|
||||
|
||||
CGRect attributionBackgroundFrame = CGRectMake(attributionOrigin.x,
|
||||
attributionOrigin.y,
|
||||
attributionBackgroundSize.width,
|
||||
attributionBackgroundSize.height);
|
||||
CGPoint attributionTextPosition = CGPointMake(attributionBackgroundFrame.origin.x + 10,
|
||||
@@ -261,7 +262,9 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
|
||||
|
||||
[sourceImageRep drawInRect: targetFrame];
|
||||
|
||||
[logoImage drawInRect:logoImageRect];
|
||||
if (logoImage) {
|
||||
[logoImage drawInRect:logoImageRect];
|
||||
}
|
||||
|
||||
NSBitmapImageRep *attributionBackground = [[NSBitmapImageRep alloc] initWithFocusedViewRect:attributionBackgroundFrame];
|
||||
|
||||
@@ -271,7 +274,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
|
||||
|
||||
[blurredAttributionBackground drawInRect:attributionBackgroundFrame];
|
||||
|
||||
[self drawAttributionText:infos origin:attributionTextPosition];
|
||||
[self drawAttributionTextWithStyle:attributionInfoStyle origin:attributionTextPosition];
|
||||
|
||||
[compositedImage unlockFocus];
|
||||
|
||||
@@ -290,15 +293,16 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)drawAttributionText:(NSArray *)attributionInfo origin:(CGPoint)origin
|
||||
- (void)drawAttributionTextWithStyle:(MGLAttributionInfoStyle)attributionInfoStyle origin:(CGPoint)origin
|
||||
{
|
||||
for (MGLAttributionInfo *info in attributionInfo) {
|
||||
for (MGLAttributionInfo *info in _attributionInfo) {
|
||||
if (info.isFeedbackLink) {
|
||||
continue;
|
||||
}
|
||||
[info.title drawAtPoint:origin];
|
||||
NSAttributedString *attribution = [info titleWithStyle:attributionInfoStyle];
|
||||
[attribution drawAtPoint:origin];
|
||||
|
||||
origin.x += [info.title size].width + 10;
|
||||
origin.x += [attribution size].width + 10;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,10 +328,8 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
|
||||
MGLImage *image;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
image = [UIImage imageWithCGImage:cgimg];
|
||||
#else
|
||||
|
||||
image = [[NSImage alloc] initWithCGImage:cgimg size:[backgroundImage extent].size];
|
||||
#endif
|
||||
|
||||
@@ -335,87 +337,110 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
|
||||
return image;
|
||||
}
|
||||
|
||||
- (MGLImage *)logoImageWithStyle:(MGLAttributionInfoStyle)style
|
||||
{
|
||||
MGLImage *logoImage;
|
||||
switch (style) {
|
||||
case MGLAttributionInfoStyleLong:
|
||||
logoImage = [self mapboxLongStyleLogo];
|
||||
break;
|
||||
case MGLAttributionInfoStyleMedium:
|
||||
#if TARGET_OS_IPHONE
|
||||
logoImage = [UIImage imageNamed:@"mapbox_helmet" inBundle:[NSBundle mgl_frameworkBundle] compatibleWithTraitCollection:nil];
|
||||
#else
|
||||
logoImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mgl_frameworkBundle] pathForResource:@"mapbox_helmet" ofType:@"pdf"]];
|
||||
#endif
|
||||
break;
|
||||
case MGLAttributionInfoStyleShort:
|
||||
logoImage = nil;
|
||||
break;
|
||||
}
|
||||
return logoImage;
|
||||
}
|
||||
|
||||
- (MGLImage *)mapboxLongStyleLogo
|
||||
{
|
||||
MGLImage *logoImage;
|
||||
#if TARGET_OS_IPHONE
|
||||
logoImage =[UIImage imageNamed:@"mapbox" inBundle:[NSBundle mgl_frameworkBundle] compatibleWithTraitCollection:nil];
|
||||
#else
|
||||
logoImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mgl_frameworkBundle] pathForResource:@"mapbox" ofType:@"pdf"]];
|
||||
#endif
|
||||
return logoImage;
|
||||
}
|
||||
|
||||
- (CGSize)attributionSizeWithLogoStyle:(MGLAttributionInfoStyle)logoStyle sourceAttributionStyle:(MGLAttributionInfoStyle)attributionStyle
|
||||
{
|
||||
MGLImage *logoImage = [self logoImageWithStyle:logoStyle];
|
||||
|
||||
CGSize attributionBackgroundSize = [self attributionTextSizeWithStyle:attributionStyle];
|
||||
|
||||
CGSize attributionSize = CGSizeZero;
|
||||
|
||||
if (logoImage) {
|
||||
attributionSize.width = MGLLogoImagePosition.x + logoImage.size.width + 10;
|
||||
}
|
||||
attributionSize.width = attributionSize.width + 10 + attributionBackgroundSize.width + 10;
|
||||
attributionSize.height = MAX(logoImage.size.height, attributionBackgroundSize.height);
|
||||
|
||||
return attributionSize;
|
||||
}
|
||||
|
||||
- (CGSize)attributionTextSizeWithStyle:(MGLAttributionInfoStyle)attributionStyle
|
||||
{
|
||||
CGSize attributionBackgroundSize = CGSizeMake(10, 0);
|
||||
for (MGLAttributionInfo *info in _attributionInfo) {
|
||||
if (info.isFeedbackLink) {
|
||||
continue;
|
||||
}
|
||||
CGSize attributionSize = [info titleWithStyle:attributionStyle].size;
|
||||
attributionBackgroundSize.width += attributionSize.width + 10;
|
||||
attributionBackgroundSize.height = MAX(attributionSize.height, attributionBackgroundSize.height);
|
||||
}
|
||||
|
||||
return attributionBackgroundSize;
|
||||
}
|
||||
|
||||
- (void)cancel
|
||||
{
|
||||
_snapshotCallback.reset();
|
||||
_mbglMapSnapshotter.reset();
|
||||
}
|
||||
|
||||
- (NSURL *)styleURL
|
||||
{
|
||||
NSString *styleURLString = @(_mbglMapSnapshotter->getStyleURL().c_str());
|
||||
return styleURLString.length ? [NSURL URLWithString:styleURLString] : nil;
|
||||
}
|
||||
|
||||
- (void)setStyleURL:(NSURL *)url
|
||||
{
|
||||
_mbglMapSnapshotter->setStyleURL(std::string([url.absoluteString UTF8String]));
|
||||
}
|
||||
|
||||
- (CGSize)size
|
||||
{
|
||||
mbgl::Size size = _mbglMapSnapshotter->getSize();
|
||||
return CGSizeMake(size.width, size.height);
|
||||
}
|
||||
|
||||
- (void)setSize:(CGSize)size
|
||||
{
|
||||
_mbglMapSnapshotter->setSize({
|
||||
static_cast<uint32_t>(MAX(size.width, MGLSnapshotterMinimumPixelSize)),
|
||||
static_cast<uint32_t>(MAX(size.height, MGLSnapshotterMinimumPixelSize))
|
||||
});
|
||||
}
|
||||
|
||||
- (MGLMapCamera *)camera
|
||||
{
|
||||
mbgl::CameraOptions cameraOptions = _mbglMapSnapshotter->getCameraOptions();
|
||||
CGFloat pitch = *cameraOptions.pitch;
|
||||
CLLocationDirection heading = mbgl::util::wrap(*cameraOptions.angle, 0., 360.);
|
||||
CLLocationDistance distance = MGLAltitudeForZoomLevel(*cameraOptions.zoom, pitch, cameraOptions.center->latitude(), [self size]);
|
||||
return [MGLMapCamera cameraLookingAtCenterCoordinate:MGLLocationCoordinate2DFromLatLng(*cameraOptions.center)
|
||||
fromDistance:distance
|
||||
pitch:pitch
|
||||
heading:heading];
|
||||
}
|
||||
|
||||
- (void)setCamera:(MGLMapCamera *)camera
|
||||
- (void)setOptions:(MGLMapSnapshotOptions *)options
|
||||
{
|
||||
_options = options;
|
||||
mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
|
||||
_mbglThreadPool = mbgl::sharedThreadPool();
|
||||
|
||||
std::string styleURL = std::string([options.styleURL.absoluteString UTF8String]);
|
||||
|
||||
// Size; taking into account the minimum texture size for OpenGL ES
|
||||
// For non retina screens the ratio is 1:1 MGLSnapshotterMinimumPixelSize
|
||||
mbgl::Size size = {
|
||||
static_cast<uint32_t>(MAX(options.size.width, MGLSnapshotterMinimumPixelSize)),
|
||||
static_cast<uint32_t>(MAX(options.size.height, MGLSnapshotterMinimumPixelSize))
|
||||
};
|
||||
|
||||
float pixelRatio = MAX(options.scale, 1);
|
||||
|
||||
// Camera options
|
||||
mbgl::CameraOptions cameraOptions;
|
||||
CLLocationCoordinate2D center;
|
||||
if (CLLocationCoordinate2DIsValid(camera.centerCoordinate)) {
|
||||
cameraOptions.center = MGLLatLngFromLocationCoordinate2D(camera.centerCoordinate);
|
||||
center = camera.centerCoordinate;
|
||||
} else {
|
||||
// Center is optional, but always set.
|
||||
center = MGLLocationCoordinate2DFromLatLng(*_mbglMapSnapshotter->getCameraOptions().center);
|
||||
if (CLLocationCoordinate2DIsValid(options.camera.centerCoordinate)) {
|
||||
cameraOptions.center = MGLLatLngFromLocationCoordinate2D(options.camera.centerCoordinate);
|
||||
}
|
||||
cameraOptions.angle = MAX(0, options.camera.heading) * mbgl::util::DEG2RAD;
|
||||
cameraOptions.zoom = MAX(0, options.zoomLevel);
|
||||
cameraOptions.pitch = MAX(0, options.camera.pitch);
|
||||
|
||||
// Region
|
||||
mbgl::optional<mbgl::LatLngBounds> coordinateBounds;
|
||||
if (!MGLCoordinateBoundsIsEmpty(options.coordinateBounds)) {
|
||||
coordinateBounds = MGLLatLngBoundsFromCoordinateBounds(options.coordinateBounds);
|
||||
}
|
||||
|
||||
cameraOptions.angle = MAX(0, camera.heading) * mbgl::util::DEG2RAD;
|
||||
cameraOptions.zoom = MAX(0, MGLZoomLevelForAltitude(camera.altitude, camera.pitch, center.latitude, [self size]));
|
||||
cameraOptions.pitch = MAX(0, camera.pitch);
|
||||
}
|
||||
|
||||
- (double)zoomLevel
|
||||
{
|
||||
mbgl::CameraOptions cameraOptions = _mbglMapSnapshotter->getCameraOptions();
|
||||
return MGLAltitudeForZoomLevel(*cameraOptions.zoom, *cameraOptions.pitch, cameraOptions.center->latitude(), [self size]);
|
||||
}
|
||||
|
||||
- (void)setZoomLevel:(double)zoomLevel
|
||||
{
|
||||
mbgl::CameraOptions cameraOptions = _mbglMapSnapshotter->getCameraOptions();
|
||||
cameraOptions.zoom = zoomLevel;
|
||||
_mbglMapSnapshotter->setCameraOptions(cameraOptions);
|
||||
}
|
||||
|
||||
- (MGLCoordinateBounds)coordinateBounds
|
||||
{
|
||||
return MGLCoordinateBoundsFromLatLngBounds(_mbglMapSnapshotter->getRegion());
|
||||
}
|
||||
|
||||
- (void)setCoordinateBounds:(MGLCoordinateBounds)coordinateBounds
|
||||
{
|
||||
_mbglMapSnapshotter->setRegion(MGLLatLngBoundsFromCoordinateBounds(coordinateBounds));
|
||||
// Create the snapshotter
|
||||
_mbglMapSnapshotter = std::make_unique<mbgl::MapSnapshotter>(*mbglFileSource, *_mbglThreadPool, styleURL, size, pixelRatio, cameraOptions, coordinateBounds);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -47,7 +47,7 @@ void MGLDrawCustomStyleLayer(void *context, const mbgl::style::CustomLayerRender
|
||||
when creating an OpenGL style layer.
|
||||
*/
|
||||
void MGLFinishCustomStyleLayer(void *context) {
|
||||
MGLOpenGLStyleLayer *layer = (__bridge MGLOpenGLStyleLayer *)context;
|
||||
MGLOpenGLStyleLayer *layer = (__bridge_transfer MGLOpenGLStyleLayer *)context;
|
||||
[layer willMoveFromMapView:layer.style.mapView];
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ void MGLFinishCustomStyleLayer(void *context) {
|
||||
MGLPrepareCustomStyleLayer,
|
||||
MGLDrawCustomStyleLayer,
|
||||
MGLFinishCustomStyleLayer,
|
||||
(__bridge void *)self);
|
||||
(__bridge_retained void *)self);
|
||||
return self = [super initWithPendingLayer:std::move(layer)];
|
||||
}
|
||||
|
||||
@@ -116,9 +116,7 @@ void MGLFinishCustomStyleLayer(void *context) {
|
||||
[NSException raise:@"MGLLayerReuseException"
|
||||
format:@"%@ cannot be added to more than one MGLStyle at a time.", self];
|
||||
}
|
||||
_style.openGLLayers[self.identifier] = nil;
|
||||
_style = style;
|
||||
_style.openGLLayers[self.identifier] = self;
|
||||
}
|
||||
|
||||
- (void)addToStyle:(MGLStyle *)style belowLayer:(MGLStyleLayer *)otherLayer {
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
@property (nonatomic, readonly, weak) MGLMapView *mapView;
|
||||
@property (nonatomic, readonly) mbgl::style::Style *rawStyle;
|
||||
@property (readonly, copy, nullable) NSURL *URL;
|
||||
@property (nonatomic, readwrite, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers;
|
||||
@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_DICTIONARY_OF(NSObject *, MGLTextLanguage *) *) *localizedLayersByIdentifier;
|
||||
|
||||
@end
|
||||
@@ -169,7 +168,6 @@ static NSURL *MGLStyleURL_trafficNight;
|
||||
if (self = [super init]) {
|
||||
_mapView = mapView;
|
||||
_rawStyle = rawStyle;
|
||||
_openGLLayers = [NSMutableDictionary dictionary];
|
||||
_localizedLayersByIdentifier = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
|
||||
@@ -26,8 +26,6 @@ namespace mbgl {
|
||||
|
||||
- (nullable NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor;
|
||||
|
||||
@property (nonatomic, readonly, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers;
|
||||
|
||||
- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration;
|
||||
|
||||
@end
|
||||
|
||||
@@ -27,11 +27,11 @@ import Mapbox
|
||||
class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
|
||||
var mapView: MGLMapView!
|
||||
var styleLoadingExpectation: XCTestExpectation!
|
||||
static let styleURL = Bundle(for: MGLDocumentationExampleTests.self).url(forResource: "one-liner", withExtension: "json")!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
let styleURL = Bundle(for: MGLDocumentationExampleTests.self).url(forResource: "one-liner", withExtension: "json")
|
||||
mapView = MGLMapView(frame: CGRect(x: 0, y: 0, width: 256, height: 256), styleURL: styleURL)
|
||||
mapView = MGLMapView(frame: CGRect(x: 0, y: 0, width: 256, height: 256), styleURL: MGLDocumentationExampleTests.styleURL)
|
||||
mapView.delegate = self
|
||||
styleLoadingExpectation = expectation(description: "Map view should finish loading style")
|
||||
waitForExpectations(timeout: 1, handler: nil)
|
||||
@@ -279,6 +279,27 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
|
||||
}
|
||||
|
||||
func testMGLMapSnapshotter() {
|
||||
let expectation = self.expectation(description: "MGLMapSnapshotter should produce a snapshot")
|
||||
#if os(macOS)
|
||||
var image: NSImage? {
|
||||
didSet {
|
||||
expectation.fulfill()
|
||||
}
|
||||
}
|
||||
#else
|
||||
var image: UIImage? {
|
||||
didSet {
|
||||
expectation.fulfill()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
class MGLStyle {
|
||||
static func satelliteStreetsStyleURL() -> URL {
|
||||
return MGLDocumentationExampleTests.styleURL
|
||||
}
|
||||
}
|
||||
|
||||
//#-example-code
|
||||
let camera = MGLMapCamera(lookingAtCenter: CLLocationCoordinate2D(latitude: 37.7184, longitude: -122.4365), fromDistance: 100, pitch: 20, heading: 0)
|
||||
|
||||
@@ -287,13 +308,15 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
|
||||
|
||||
let snapshotter = MGLMapSnapshotter(options: options)
|
||||
snapshotter.start { (snapshot, error) in
|
||||
if error != nil {
|
||||
// error handler
|
||||
} else {
|
||||
// image handler
|
||||
if let error = error {
|
||||
fatalError(error.localizedDescription)
|
||||
}
|
||||
|
||||
image = snapshot?.image
|
||||
}
|
||||
//#-end-example-code
|
||||
|
||||
wait(for: [expectation], timeout: 1)
|
||||
}
|
||||
|
||||
// For testMGLMapView().
|
||||
|
||||
@@ -4,10 +4,6 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
|
||||
|
||||
## master
|
||||
|
||||
### Packaging
|
||||
|
||||
* Renamed this SDK from Mapbox iOS SDK to Mapbox Maps SDK for iOS. ([#10610](https://github.com/mapbox/mapbox-gl-native/pull/10610))
|
||||
|
||||
### Annotations and user interaction
|
||||
|
||||
* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
|
||||
@@ -17,7 +13,33 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
|
||||
* Fixed an issue preventing a dynamically-added `MGLRasterStyleLayer` from drawing until the map pans. ([#10270](https://github.com/mapbox/mapbox-gl-native/pull/10270))
|
||||
* Added `MGLComputedShapeSource` source class that allows applications to supply vector data on a per-tile basis.
|
||||
|
||||
## 3.7.0
|
||||
## 3.7.2 - December 21, 2017
|
||||
|
||||
### Packaging
|
||||
|
||||
* Reduced the file size of the dSYM by removing the i386 architecture. Support for the i386 architecture (used by 32-bit simulators) will also be removed from the framework itself in a future release. ([#10781](https://github.com/mapbox/mapbox-gl-native/pull/10781))
|
||||
|
||||
### Other changes
|
||||
|
||||
* Fixed an issue where removing a `MGLOpenGLStyleLayer` from a map might result in a crash. ([#10765](https://github.com/mapbox/mapbox-gl-native/pull/10765))
|
||||
* Added documentation for usage of coordinate bounds that cross the anti-meridian. ([#9804](https://github.com/mapbox/mapbox-gl-native/issues/9804))
|
||||
* Removed duplicated variables in `MGLMapSnapshotter`. ([#10702](https://github.com/mapbox/mapbox-gl-native/pull/10702))
|
||||
|
||||
## 3.7.1 - December 6, 2017
|
||||
|
||||
### Packaging
|
||||
|
||||
* Renamed this SDK from Mapbox iOS SDK to Mapbox Maps SDK for iOS. ([#10610](https://github.com/mapbox/mapbox-gl-native/pull/10610))
|
||||
|
||||
### Annotations
|
||||
|
||||
* Fixed incorrect hit targets for `MGLAnnotationImage`-backed annotations that caused `-[MGLMapViewDelegate mapView:didSelectAnnotation:]` to be called unnecessarily. ([#10538](https://github.com/mapbox/mapbox-gl-native/pull/10538))
|
||||
|
||||
### Other changes
|
||||
|
||||
* Fixed an issue that caused `MGLMapView.minimumZoomLevel` to not be set. ([#10596](https://github.com/mapbox/mapbox-gl-native/pull/10596))
|
||||
|
||||
## 3.7.0 - Novemeber 13, 2017
|
||||
|
||||
### Networking and storage
|
||||
|
||||
@@ -33,6 +55,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
|
||||
* Added `MGLCircleStyleLayer.circlePitchAlignment` and `MGLSymbolStyleLayer.iconPitchAlignment` properties to control whether circles and symbols lie flat against a tilted map. ([#9426](https://github.com/mapbox/mapbox-gl-native/pull/9426), [#9479](https://github.com/mapbox/mapbox-gl-native/pull/9479))
|
||||
* Added an `MGLSymbolStyleLayer.iconAnchor` property to control where an icon is anchored. ([#9849](https://github.com/mapbox/mapbox-gl-native/pull/9849))
|
||||
* The `maximumTextWidth` and `textLetterSpacing` properties of `MGLSymbolStyleLayer` are now compatible with `MGLSourceStyleFunction`s and `MGLCompositeStyleFunction`s, allowing data-driven styling of these properties. ([#9870](https://github.com/mapbox/mapbox-gl-native/pull/9870))
|
||||
* The `MGLSymbolStyleLayer.textAnchor`, `MGLSymbolStyleLayer.textJustification` and `MGLLineStyleLayer.lineJoin` properties are now compatible with `MGLSourceStyleFunction`s and `MGLCompositeStyleFunction`s, allowing data-driven styling of these properties. ([#9583](https://github.com/mapbox/mapbox-gl-native/pull/9583))
|
||||
* Improved the legibility of labels that follow lines when the map is tilted. ([#9009](https://github.com/mapbox/mapbox-gl-native/pull/9009))
|
||||
* Fixed an issue that could cause flickering when a translucent raster style layer was present. ([#9468](https://github.com/mapbox/mapbox-gl-native/pull/9468))
|
||||
* Fixed an issue that could cause antialiasing between polygons on the same layer to fail if the fill layers used data-driven styling for the fill color. ([#9699](https://github.com/mapbox/mapbox-gl-native/pull/9699))
|
||||
@@ -52,6 +75,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
|
||||
|
||||
* Users of VoiceOver can now swipe left and right to navigate among visible places, points of interest, and roads. ([#9950](https://github.com/mapbox/mapbox-gl-native/pull/9950))
|
||||
* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
|
||||
* Fixed an issue where the same value was passed in as the `oldCamera` and `newCamera` parameters to the `-[MGLMapViewDelegate mapView:shouldChangeFromCamera:toCamera:]` method. ([#10433](https://github.com/mapbox/mapbox-gl-native/pull/10433))
|
||||
|
||||
### Other changes
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@ This document explains how to build a development version of Mapbox Maps SDK for
|
||||
|
||||
### Requirements
|
||||
|
||||
The Mapbox Maps SDK for iOS is intended to run on iOS 8.0 and above on the following devices and their simulators:
|
||||
The Mapbox Maps SDK for iOS is intended to run on iOS 8.0 and above on the following devices:
|
||||
|
||||
* iPhone 4S and above (5, 5c, 5s, 6, 6 Plus)
|
||||
* iPad 2 and above (3, 4, Mini, Air, Mini 2, Air 2)
|
||||
* iPhone 4s and above (5, 5c, 5s, 6, 6 Plus, 7, 7 Plus, 8, 8 Plus, X)
|
||||
* iPad 2 and above (3, 4, Mini, Air, Mini 2, Air 2, Pro)
|
||||
* iPod touch 5th generation and above
|
||||
|
||||
The Mapbox Maps SDK for iOS requires:
|
||||
Note that debugging in 32-bit simulators (such as the iPhone 5 or iPad 2) is only partially supported. Support for these simulators will be removed entirely in a future release of this SDK.
|
||||
|
||||
* Xcode 9.1 or higher to compile from source
|
||||
The Mapbox Maps SDK for iOS requires Xcode 8.0 or higher. To use this SDK with Xcode 7.3.1, download and use a symbols build from the [releases](https://github.com/mapbox/mapbox-gl-native/releases) page.
|
||||
* Xcode 8.0 or higher to integrate the compiled framework into an application
|
||||
|
||||
### Building the SDK
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |m|
|
||||
|
||||
version = '3.7.0-beta.4'
|
||||
version = '3.7.2'
|
||||
|
||||
m.name = 'Mapbox-iOS-SDK-nightly-dynamic'
|
||||
m.version = "#{version}-nightly"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |m|
|
||||
|
||||
version = '3.7.0-beta.4'
|
||||
version = '3.7.2'
|
||||
|
||||
m.name = 'Mapbox-iOS-SDK-symbols'
|
||||
m.version = "#{version}-symbols"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |m|
|
||||
|
||||
version = '3.7.0-beta.4'
|
||||
version = '3.7.2'
|
||||
|
||||
m.name = 'Mapbox-iOS-SDK'
|
||||
m.version = version
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
A library based on [Mapbox GL Native](../../README.md) for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 8.0 and above using Objective-C, Swift, or Interface Builder.
|
||||
|
||||
This repository is for day-to-day development of the SDK. Building the SDK yourself requires [a number of dependencies and steps](../../INSTALL.md) that are unnecessary for developing production applications. For production applications, please consider installing an official, production-ready version instead.
|
||||
This repository is for day-to-day development of the SDK. Building the SDK yourself requires [a number of dependencies and steps](../../INSTALL.md) that are unnecessary for developing production applications. For production applications, please consider installing an official, prebuilt release instead; see the [Mapbox iOS SDK website](https://www.mapbox.com/ios-sdk/) for installation instructions.
|
||||
|
||||
* [Integrate the Mapbox Maps SDK for iOS into your application](https://www.mapbox.com/install/ios/).
|
||||
* [Learn about custom builds](INSTALL.md)
|
||||
|
||||
0
platform/ios/app/ar.lproj/Localizable.strings
Normal file
0
platform/ios/app/ar.lproj/Localizable.strings
Normal file
@@ -848,6 +848,8 @@
|
||||
DA737AE91E5917C300AD2CDE /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DA737AEA1E5917EF00AD2CDE /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Root.strings; sourceTree = "<group>"; };
|
||||
DA737EE01D056A4E005BDA16 /* MGLMapViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapViewDelegate.h; sourceTree = "<group>"; };
|
||||
DA80E9601FE84AD90065FC9B /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DA80E9611FE84AEF0065FC9B /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ar; path = ar.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
|
||||
DA821D041CCC6D59007508D4 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
DA821D051CCC6D59007508D4 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
|
||||
DA8847D21CBAF91600AB86E3 /* Mapbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Mapbox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -2142,6 +2144,7 @@
|
||||
nl,
|
||||
hu,
|
||||
bg,
|
||||
ar,
|
||||
);
|
||||
mainGroup = DA1DC9411CB6C1C2006E619F;
|
||||
productRefGroup = DA1DC94B1CB6C1C2006E619F /* Products */;
|
||||
@@ -2558,6 +2561,7 @@
|
||||
DAE8CCAD1E6E8C70009B5CB0 /* nl */,
|
||||
DA5C09BA1EFC48550056B178 /* hu */,
|
||||
DA3389651FA3EE1B001EA329 /* bg */,
|
||||
DA80E9601FE84AD90065FC9B /* ar */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
@@ -2648,6 +2652,7 @@
|
||||
DACFE7981F66EA2100630DA8 /* vi */,
|
||||
DA3389671FA3EE2F001EA329 /* bg */,
|
||||
DA33896B1FA3EF4A001EA329 /* hu */,
|
||||
DA80E9611FE84AEF0065FC9B /* ar */,
|
||||
);
|
||||
name = Foundation.stringsdict;
|
||||
sourceTree = "<group>";
|
||||
|
||||
@@ -32,6 +32,9 @@ custom_categories:
|
||||
- MGLMapCamera
|
||||
- MGLMapView
|
||||
- MGLMapViewDelegate
|
||||
- MGLMapSnapshot
|
||||
- MGLMapSnapshotOptions
|
||||
- MGLMapSnapshotter
|
||||
- MGLUserTrackingMode
|
||||
- name: Primitive Shapes
|
||||
children:
|
||||
|
||||
@@ -64,6 +64,9 @@
|
||||
/* User-friendly error description */
|
||||
"PARSE_STYLE_FAILED_DESC" = "The map failed to load because the style is corrupted.";
|
||||
|
||||
/* String format for accessibility value for road feature; {starting compass direction}, {ending compass direction} */
|
||||
"ROAD_DIRECTION_A11Y_FMT" = "%1$@ to %2$@";
|
||||
|
||||
/* Accessibility value indicating that a road is a divided road (dual carriageway) */
|
||||
"ROAD_DIVIDED_A11Y_VALUE" = "Divided road";
|
||||
|
||||
|
||||
12
platform/ios/resources/Images.xcassets/mapbox_helmet.imageset/Contents.json
vendored
Normal file
12
platform/ios/resources/Images.xcassets/mapbox_helmet.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "mapbox_helmet.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
355
platform/ios/resources/Images.xcassets/mapbox_helmet.imageset/mapbox_helmet.pdf
vendored
Normal file
355
platform/ios/resources/Images.xcassets/mapbox_helmet.imageset/mapbox_helmet.pdf
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -2,16 +2,16 @@
|
||||
"ANNOTATION_A11Y_HINT" = "Показва повече инфо";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"API_CLIENT_400_DESC" = "Неуспешна сесия за данни. Оригинална заявка: %@";
|
||||
"API_CLIENT_400_DESC" = "Незавършена сесия от данни. Оригинална заявка:%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"API_CLIENT_400_REASON" = "Статус кодът беше %ld";
|
||||
"API_CLIENT_400_REASON" = "Статус код %ld";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"CANCEL" = "Отказ";
|
||||
|
||||
/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
|
||||
"CLOSE_CALLOUT_A11Y_HINT" = "Връща към картата";
|
||||
"CLOSE_CALLOUT_A11Y_HINT" = "Обратно към картата";
|
||||
|
||||
/* Accessibility hint */
|
||||
"COMPASS_A11Y_HINT" = "Завърта картата в посока север";
|
||||
@@ -23,17 +23,20 @@
|
||||
"COMPASS_NORTH" = "С";
|
||||
|
||||
/* Instructions in Interface Builder designable; {key}, {plist file name} */
|
||||
"DESIGNABLE" = "За да се показва Mapbox карта тук, добави %1$@ към твоя токен за достъп в %2$@\n\nЗа подробни инструкции, виж:";
|
||||
"DESIGNABLE" = "За да покажеш Mapbox-хоствана карта тук, задай %1$@ към своя токен за достъп в %2$@\nЗа повече детайли, виж:";
|
||||
|
||||
/* Setup documentation URL display string; keep as short as possible */
|
||||
"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk";
|
||||
|
||||
/* Accessibility hint */
|
||||
"INFO_A11Y_HINT" = "Показва кредитите, форма за връзка и още";
|
||||
"INFO_A11Y_HINT" = "Показва благодарности, форма за обратна връзка и още";
|
||||
|
||||
/* Accessibility label */
|
||||
"INFO_A11Y_LABEL" = "За тази карта";
|
||||
|
||||
/* List separator */
|
||||
"LIST_SEPARATOR" = ",";
|
||||
|
||||
/* User-friendly error description */
|
||||
"LOAD_MAP_FAILED_DESC" = "Картата не се зареди поради неизвестна грешка.";
|
||||
|
||||
@@ -46,23 +49,41 @@
|
||||
/* Accessibility label */
|
||||
"MAP_A11Y_LABEL" = "Карта";
|
||||
|
||||
/* Map accessibility value */
|
||||
"MAP_A11Y_VALUE" = "Мащаб %1$dх\n@2$ld видима(и) анотация(и)";
|
||||
/* Map accessibility value; {number of visible annotations} */
|
||||
"MAP_A11Y_VALUE_ANNOTATIONS" = "%ld видима анотация(и).";
|
||||
|
||||
/* Map accessibility value; {list of visible places} */
|
||||
"MAP_A11Y_VALUE_PLACES" = "Видими места: %@.";
|
||||
|
||||
/* Map accessibility value; {number of visible roads} */
|
||||
"MAP_A11Y_VALUE_ROADS" = "%ld видим път(я).";
|
||||
|
||||
/* Map accessibility value; {zoom level} */
|
||||
"MAP_A11Y_VALUE_ZOOM" = "Мащаб %dx.";
|
||||
|
||||
/* User-friendly error description */
|
||||
"PARSE_STYLE_FAILED_DESC" = "Картата не се зареди поради повреден стил.";
|
||||
|
||||
/* Accessibility value indicating that a road is a divided road (dual carriageway) */
|
||||
"ROAD_DIVIDED_A11Y_VALUE" = "Разделен път";
|
||||
|
||||
/* Accessibility value indicating that a road is a one-way road */
|
||||
"ROAD_ONEWAY_A11Y_VALUE" = "Еднопосочно";
|
||||
|
||||
/* String format for accessibility value for road feature; {route number} */
|
||||
"ROAD_REF_A11Y_FMT" = "Маршрут %@";
|
||||
|
||||
/* Action sheet title */
|
||||
"SDK_NAME" = "Mapbox Maps SDK for iOS";
|
||||
"SDK_NAME" = "Mapbox Maps SDK за iOS";
|
||||
|
||||
/* Developer-only SDK update notification; {latest version, in format x.x.x} */
|
||||
"SDK_UPDATE_AVAILABLE" = "Сега е налична Mapbox Maps SDK for iOS версия %@:";
|
||||
"SDK_UPDATE_AVAILABLE" = "Mapbox Maps SDK за iOS версия %@ е вече налична:";
|
||||
|
||||
/* User-friendly error description */
|
||||
"STYLE_NOT_FOUND_DESC" = "Картата не се зареди поради неоткрит или несъвместим стил.";
|
||||
|
||||
/* Telemetry prompt message */
|
||||
"TELEMETRY_DISABLED_MSG" = "Можеш да помогнеш OpenStreetMap и Mapbox да станат по-добри, като предоставиш анонимни данни за потребление.";
|
||||
/* OpenStreetMap full name attribution */
|
||||
|
||||
/* Telemetry prompt button */
|
||||
"TELEMETRY_DISABLED_OFF" = "Не участвам";
|
||||
@@ -71,7 +92,7 @@
|
||||
"TELEMETRY_DISABLED_ON" = "Участвам";
|
||||
|
||||
/* Telemetry prompt message */
|
||||
"TELEMETRY_ENABLED_MSG" = "Помагаш OpenStreetMap и Mapbox да станат по-добри, като предоставяш анонимни данни за потребление.";
|
||||
"TELEMETRY_ENABLED_MSG" = "Ти помагаш OpenStreetMap и Mapbox картите да стават по-добри, като предоставяш анонимни данни за употреба.";
|
||||
|
||||
/* Telemetry prompt button */
|
||||
"TELEMETRY_ENABLED_OFF" = "Спирам участие";
|
||||
@@ -80,14 +101,14 @@
|
||||
"TELEMETRY_ENABLED_ON" = "Продължавам да участвам";
|
||||
|
||||
/* Telemetry prompt button */
|
||||
"TELEMETRY_MORE" = "Искам още инфо";
|
||||
"TELEMETRY_MORE" = "Покажи ми още";
|
||||
|
||||
/* Action in attribution sheet */
|
||||
"TELEMETRY_NAME" = "Mapbox Телеметрия";
|
||||
"TELEMETRY_NAME" = "Mapbox телеметрия";
|
||||
|
||||
/* Telemetry prompt title */
|
||||
"TELEMETRY_TITLE" = "Направи Mapbox картите по-добри";
|
||||
"OSM_FULL_NAME" = "OpenStreetMap";
|
||||
|
||||
/* Default user location annotation title */
|
||||
"USER_DOT_TITLE" = "Сега си тук";
|
||||
/* OpenStreetMap short name attribution */
|
||||
"OSM_SHORT_NAME" = "OSM";
|
||||
|
||||
|
||||
@@ -2,11 +2,42 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>MAP_A11Y_VALUE</key>
|
||||
<key>MAP_A11Y_VALUE_ANNOTATIONS</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@level@
|
||||
%#@count@</string>
|
||||
<string>%#@count@</string>
|
||||
<key>count</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>ld</string>
|
||||
<key>one</key>
|
||||
<string>%d видими анотации</string>
|
||||
<key>other</key>
|
||||
<string>%d видими анотации</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>MAP_A11Y_VALUE_ROADS</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@count@</string>
|
||||
<key>count</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>ld</string>
|
||||
<key>one</key>
|
||||
<string>%d път видимост</string>
|
||||
<key>other</key>
|
||||
<string>%d пътища видимост</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>MAP_A11Y_VALUE_ZOOM</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@level@</string>
|
||||
<key>level</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
@@ -18,17 +49,6 @@
|
||||
<key>other</key>
|
||||
<string>Мащаб %dx</string>
|
||||
</dict>
|
||||
<key>count</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>ld</string>
|
||||
<key>one</key>
|
||||
<string>%d видима анотация</string>
|
||||
<key>other</key>
|
||||
<string>%d видими анотации</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
Binary file not shown.
@@ -2,22 +2,10 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>MAP_A11Y_VALUE</key>
|
||||
<key>MAP_A11Y_VALUE_ANNOTATIONS</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@level@
|
||||
%#@count@</string>
|
||||
<key>level</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Zoom %dx</string>
|
||||
<key>other</key>
|
||||
<string>Zoom %dx</string>
|
||||
</dict>
|
||||
<string>%#@count@</string>
|
||||
<key>count</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
@@ -30,5 +18,37 @@
|
||||
<string>%d anotaciones visibles</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>MAP_A11Y_VALUE_ROADS</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@count@</string>
|
||||
<key>count</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>ld</string>
|
||||
<key>one</key>
|
||||
<string>%d camino visible</string>
|
||||
<key>other</key>
|
||||
<string>%d caminos visibles</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>MAP_A11Y_VALUE_ZOOM</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@level@</string>
|
||||
<key>level</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Zoom %dx</string>
|
||||
<key>other</key>
|
||||
<string>Zoom %dx</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
Binary file not shown.
@@ -2,20 +2,10 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>MAP_A11Y_VALUE</key>
|
||||
<key>MAP_A11Y_VALUE_ANNOTATIONS</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@level@
|
||||
%#@count@</string>
|
||||
<key>level</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>other</key>
|
||||
<string>Thu phóng gấp %d lần</string>
|
||||
</dict>
|
||||
<string>%#@count@</string>
|
||||
<key>count</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
@@ -26,5 +16,33 @@
|
||||
<string>%d chú thích đang xuất hiện</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>MAP_A11Y_VALUE_ROADS</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@count@</string>
|
||||
<key>count</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>ld</string>
|
||||
<key>other</key>
|
||||
<string>%d con đường đang xuất hiện</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>MAP_A11Y_VALUE_ZOOM</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@level@</string>
|
||||
<key>level</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>other</key>
|
||||
<string>Thu phóng gấp %d lần</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -7,13 +7,11 @@ set -o pipefail
|
||||
xcrun bitcode_strip build/ios/pkg/dynamic/Mapbox.framework/Mapbox -r -o build/ios/pkg/dynamic/Mapbox-stripped
|
||||
lipo build/ios/pkg/dynamic/Mapbox-stripped -extract armv7 -output build/ios/pkg/dynamic/Mapbox-stripped-armv7
|
||||
lipo build/ios/pkg/dynamic/Mapbox-stripped -extract arm64 -output build/ios/pkg/dynamic/Mapbox-stripped-arm64
|
||||
lipo build/ios/pkg/dynamic/Mapbox-stripped -extract i386 -output build/ios/pkg/dynamic/Mapbox-stripped-i386
|
||||
lipo build/ios/pkg/dynamic/Mapbox-stripped -extract x86_64 -output build/ios/pkg/dynamic/Mapbox-stripped-x86_64
|
||||
|
||||
# Track individual architectures
|
||||
scripts/log_binary_size.sh "build/ios/pkg/dynamic/Mapbox-stripped-armv7" "Platform=iOS,Arch=armv7"
|
||||
scripts/log_binary_size.sh "build/ios/pkg/dynamic/Mapbox-stripped-arm64" "Platform=iOS,Arch=arm64"
|
||||
scripts/log_binary_size.sh "build/ios/pkg/dynamic/Mapbox-stripped-i386" "Platform=iOS,Arch=i386"
|
||||
scripts/log_binary_size.sh "build/ios/pkg/dynamic/Mapbox-stripped-x86_64" "Platform=iOS,Arch=x86_64"
|
||||
|
||||
# Track overall library size
|
||||
|
||||
@@ -194,6 +194,11 @@ if [[ ${BUILD_DYNAMIC} == true && ${BUILDTYPE} == Release ]]; then
|
||||
validate_dsym \
|
||||
"${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}" \
|
||||
"${OUTPUT}/dynamic/${NAME}.framework/${NAME}"
|
||||
|
||||
# To-do: remove this in 4.0.0, as we intend to remove support for i386 entirely.
|
||||
step "Removing i386 slice from dSYM"
|
||||
lipo -remove i386 "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}" -o "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}"
|
||||
lipo -info "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}"
|
||||
fi
|
||||
|
||||
function create_podspec {
|
||||
@@ -242,6 +247,23 @@ fi
|
||||
if [[ ${BUILD_DYNAMIC} == true && ${BUILD_FOR_DEVICE} == true ]]; then
|
||||
step "Copying bitcode symbol maps…"
|
||||
find "${PRODUCTS}/${BUILDTYPE}-iphoneos" -name '*.bcsymbolmap' -type f -exec cp -pv {} "${OUTPUT}/dynamic/" \;
|
||||
|
||||
step "Copying demo project and sym linking to published framework…"
|
||||
cp -rv platform/ios/scripts/script_resources/MapboxDemo "${OUTPUT}"
|
||||
cd "${OUTPUT}/MapboxDemo"
|
||||
ln -sv "../dynamic/${NAME}.framework"
|
||||
cd -
|
||||
|
||||
step "Building demo project…"
|
||||
xcodebuild -quiet -project build/ios/pkg/MapboxDemo/MapboxDemo.xcodeproj -scheme MapboxDemo build ONLY_ACTIVE_ARCH=YES -destination 'platform=iOS Simulator,name=iPhone 7' clean build &> /tmp/iosdemobuildoutput || true
|
||||
if grep -Fxq "** BUILD FAILED **" /tmp/iosdemobuildoutput
|
||||
then
|
||||
echo "Could not build demo project with this version of the SDK."
|
||||
rm -rf "${OUTPUT}/MapboxDemo"
|
||||
else
|
||||
echo "Built and packaged demo project."
|
||||
fi
|
||||
rm /tmp/iosdemobuildoutput
|
||||
fi
|
||||
sed -n -e '/^## /,$p' platform/ios/CHANGELOG.md > "${OUTPUT}/CHANGELOG.md"
|
||||
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 48;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
40625C791FB5340B00B90D3E /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 40625C781FB5340B00B90D3E /* Mapbox.framework */; };
|
||||
40625C7A1FB5340B00B90D3E /* Mapbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 40625C781FB5340B00B90D3E /* Mapbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
40625C9B1FBA59B800B90D3E /* yose.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 40625C9A1FBA59B800B90D3E /* yose.geojson */; };
|
||||
40B9C65E1FB380E3002B6532 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B9C65D1FB380E3002B6532 /* AppDelegate.swift */; };
|
||||
40B9C6601FB380E3002B6532 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B9C65F1FB380E3002B6532 /* ViewController.swift */; };
|
||||
40B9C6631FB380E3002B6532 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 40B9C6611FB380E3002B6532 /* Main.storyboard */; };
|
||||
40B9C6651FB380E3002B6532 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 40B9C6641FB380E3002B6532 /* Assets.xcassets */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
40B9C6721FB3810B002B6532 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
40625C7A1FB5340B00B90D3E /* Mapbox.framework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
40625C6F1FB5336D00B90D3E /* MapboxDemo.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = MapboxDemo.xcodeproj; sourceTree = "<group>"; };
|
||||
40625C781FB5340B00B90D3E /* Mapbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Mapbox.framework; path = ../dynamic/Mapbox.framework; sourceTree = "<group>"; };
|
||||
40625C9A1FBA59B800B90D3E /* yose.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = yose.geojson; path = MapboxDemo/data/yose.geojson; sourceTree = SOURCE_ROOT; };
|
||||
40B9C65A1FB380E3002B6532 /* MapboxDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MapboxDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
40B9C65D1FB380E3002B6532 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
40B9C65F1FB380E3002B6532 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
40B9C6621FB380E3002B6532 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
40B9C6641FB380E3002B6532 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
40B9C6691FB380E3002B6532 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
40B9C6571FB380E3002B6532 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
40625C791FB5340B00B90D3E /* Mapbox.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
40625C701FB5336D00B90D3E /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
40625C9C1FBA59C300B90D3E /* data */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
40625C9A1FBA59B800B90D3E /* yose.geojson */,
|
||||
);
|
||||
path = data;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
40B9C6511FB380E3002B6532 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
40625C781FB5340B00B90D3E /* Mapbox.framework */,
|
||||
40625C6F1FB5336D00B90D3E /* MapboxDemo.xcodeproj */,
|
||||
40B9C65C1FB380E3002B6532 /* MapboxDemo */,
|
||||
40B9C65B1FB380E3002B6532 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
40B9C65B1FB380E3002B6532 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
40B9C65A1FB380E3002B6532 /* MapboxDemo.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
40B9C65C1FB380E3002B6532 /* MapboxDemo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
40625C9C1FBA59C300B90D3E /* data */,
|
||||
40B9C65D1FB380E3002B6532 /* AppDelegate.swift */,
|
||||
40B9C65F1FB380E3002B6532 /* ViewController.swift */,
|
||||
40B9C6611FB380E3002B6532 /* Main.storyboard */,
|
||||
40B9C6641FB380E3002B6532 /* Assets.xcassets */,
|
||||
40B9C6691FB380E3002B6532 /* Info.plist */,
|
||||
);
|
||||
path = MapboxDemo;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
40B9C6591FB380E3002B6532 /* MapboxDemo */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 40B9C66C1FB380E3002B6532 /* Build configuration list for PBXNativeTarget "MapboxDemo" */;
|
||||
buildPhases = (
|
||||
40B9C6561FB380E3002B6532 /* Sources */,
|
||||
40B9C6571FB380E3002B6532 /* Frameworks */,
|
||||
40B9C6581FB380E3002B6532 /* Resources */,
|
||||
40B9C6721FB3810B002B6532 /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = MapboxDemo;
|
||||
productName = MapboxDemo;
|
||||
productReference = 40B9C65A1FB380E3002B6532 /* MapboxDemo.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
40B9C6521FB380E3002B6532 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0910;
|
||||
LastUpgradeCheck = 0910;
|
||||
ORGANIZATIONNAME = Mapbox;
|
||||
TargetAttributes = {
|
||||
40B9C6591FB380E3002B6532 = {
|
||||
CreatedOnToolsVersion = 9.1;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 40B9C6551FB380E3002B6532 /* Build configuration list for PBXProject "MapboxDemo" */;
|
||||
compatibilityVersion = "Xcode 8.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 40B9C6511FB380E3002B6532;
|
||||
productRefGroup = 40B9C65B1FB380E3002B6532 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectReferences = (
|
||||
{
|
||||
ProductGroup = 40625C701FB5336D00B90D3E /* Products */;
|
||||
ProjectRef = 40625C6F1FB5336D00B90D3E /* MapboxDemo.xcodeproj */;
|
||||
},
|
||||
);
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
40B9C6591FB380E3002B6532 /* MapboxDemo */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
40B9C6581FB380E3002B6532 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
40B9C6651FB380E3002B6532 /* Assets.xcassets in Resources */,
|
||||
40B9C6631FB380E3002B6532 /* Main.storyboard in Resources */,
|
||||
40625C9B1FBA59B800B90D3E /* yose.geojson in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
40B9C6561FB380E3002B6532 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
40B9C6601FB380E3002B6532 /* ViewController.swift in Sources */,
|
||||
40B9C65E1FB380E3002B6532 /* AppDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
40B9C6611FB380E3002B6532 /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
40B9C6621FB380E3002B6532 /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
40B9C66A1FB380E3002B6532 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.1;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
40B9C66B1FB380E3002B6532 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.1;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
40B9C66D1FB380E3002B6532 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
INFOPLIST_FILE = MapboxDemo/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxMapsSDKDemoiOS;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
40B9C66E1FB380E3002B6532 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
INFOPLIST_FILE = MapboxDemo/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxMapsSDKDemoiOS;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
40B9C6551FB380E3002B6532 /* Build configuration list for PBXProject "MapboxDemo" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
40B9C66A1FB380E3002B6532 /* Debug */,
|
||||
40B9C66B1FB380E3002B6532 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
40B9C66C1FB380E3002B6532 /* Build configuration list for PBXNativeTarget "MapboxDemo" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
40B9C66D1FB380E3002B6532 /* Debug */,
|
||||
40B9C66E1FB380E3002B6532 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 40B9C6521FB380E3002B6532 /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import UIKit
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "83.5x83.5",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"size" : "1024x1024",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "Screen Shot 2017-11-20 at 1.43.33 PM.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 292 KiB |
@@ -0,0 +1,189 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13528" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<device id="retina5_9" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13526"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="MapboxDemo" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XSu-1W-VIV" userLabel="Map View Container">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lwK-ip-Hxt" userLabel="Annotation Context View">
|
||||
<rect key="frame" x="8" y="589" width="359" height="184"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="ZvJ-iH-zYi">
|
||||
<rect key="frame" x="9" y="20" width="212" height="39"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Yosemite National Park" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Zmm-Me-fJ7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="212" height="19.333333333333332"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="16"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="National park in California" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="b0i-sD-LX4">
|
||||
<rect key="frame" x="0.0" y="23.333333333333371" width="182" height="15.666666666666664"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="b0i-sD-LX4" secondAttribute="trailing" constant="30" id="Ip7-ua-rFe"/>
|
||||
<constraint firstItem="b0i-sD-LX4" firstAttribute="top" secondItem="Zmm-Me-fJ7" secondAttribute="bottom" constant="4" id="Pfr-mw-dJ8"/>
|
||||
<constraint firstAttribute="bottom" secondItem="b0i-sD-LX4" secondAttribute="bottom" id="RSc-NM-RIj"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Zmm-Me-fJ7" secondAttribute="trailing" id="oLM-SU-yfO"/>
|
||||
<constraint firstItem="Zmm-Me-fJ7" firstAttribute="leading" secondItem="ZvJ-iH-zYi" secondAttribute="leading" id="pfX-hL-BN7"/>
|
||||
<constraint firstItem="Zmm-Me-fJ7" firstAttribute="top" secondItem="ZvJ-iH-zYi" secondAttribute="top" id="uYT-ZJ-thB"/>
|
||||
<constraint firstItem="b0i-sD-LX4" firstAttribute="leading" secondItem="ZvJ-iH-zYi" secondAttribute="leading" id="uy9-B6-Yam"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="N94-jc-tmy">
|
||||
<rect key="frame" x="9" y="90.666666666666629" width="212" height="63"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KZr-jR-aVQ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="117.33333333333333" height="15.666666666666666"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Address:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2tA-7E-47j">
|
||||
<rect key="frame" x="0.0" y="0.0" width="58" height="15.666666666666666"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="13"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="California" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="82x-XH-DQ9">
|
||||
<rect key="frame" x="58" y="0.0" width="59.333333333333343" height="15.666666666666666"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="13"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qlW-uF-qaD">
|
||||
<rect key="frame" x="0.0" y="23.666666666666742" width="90.666666666666671" height="15.666666666666664"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Area:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7Ch-ww-vSu">
|
||||
<rect key="frame" x="0.0" y="0.0" width="34.666666666666664" height="15.666666666666666"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="13"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="1,169 mi²" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="K7u-ik-UvN">
|
||||
<rect key="frame" x="34.666666666666664" y="0.0" width="55.999999999999993" height="15.666666666666666"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="13"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6te-N3-8us">
|
||||
<rect key="frame" x="0.0" y="47.333333333333371" width="179.33333333333334" height="15.666666666666664"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Established:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qtG-X7-FOk">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="15.666666666666666"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="13"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="October 1, 1890" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Cqi-cb-JwT">
|
||||
<rect key="frame" x="80" y="0.0" width="99.333333333333314" height="15.666666666666666"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="13"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="yose" translatesAutoresizingMaskIntoConstraints="NO" id="rd3-Yy-cvo">
|
||||
<rect key="frame" x="221" y="17" width="130" height="150"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="130" id="OCo-25-BHY"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="rd3-Yy-cvo" firstAttribute="leading" secondItem="ZvJ-iH-zYi" secondAttribute="trailing" id="I9D-Re-tKL"/>
|
||||
<constraint firstAttribute="bottom" secondItem="rd3-Yy-cvo" secondAttribute="bottom" constant="17" id="PCX-nz-oO0"/>
|
||||
<constraint firstItem="N94-jc-tmy" firstAttribute="trailing" secondItem="ZvJ-iH-zYi" secondAttribute="trailing" id="XNb-sa-t8f"/>
|
||||
<constraint firstAttribute="height" constant="184" id="cft-d6-DqS"/>
|
||||
<constraint firstItem="ZvJ-iH-zYi" firstAttribute="leading" secondItem="lwK-ip-Hxt" secondAttribute="leading" constant="9" id="dUn-Jr-sKC"/>
|
||||
<constraint firstItem="rd3-Yy-cvo" firstAttribute="top" secondItem="lwK-ip-Hxt" secondAttribute="top" constant="17" id="e1r-Un-sYD"/>
|
||||
<constraint firstItem="N94-jc-tmy" firstAttribute="leading" secondItem="ZvJ-iH-zYi" secondAttribute="leading" id="lhD-Zk-Q6J"/>
|
||||
<constraint firstItem="ZvJ-iH-zYi" firstAttribute="top" secondItem="lwK-ip-Hxt" secondAttribute="top" constant="20" id="ntM-Gm-41V"/>
|
||||
<constraint firstAttribute="trailing" secondItem="rd3-Yy-cvo" secondAttribute="trailing" constant="8" id="s43-oz-imQ"/>
|
||||
<constraint firstItem="N94-jc-tmy" firstAttribute="top" secondItem="ZvJ-iH-zYi" secondAttribute="bottom" constant="31.5" id="xGe-aO-CeH"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3ue-Nm-4DV">
|
||||
<rect key="frame" x="8" y="45" width="359" height="64"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="748" text="Using demo access token" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YI7-EJ-eE0">
|
||||
<rect key="frame" x="8" y="7.9999999999999982" width="343" height="25.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="21"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Add your token to Info.plist to remove this warning" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dSA-EU-IHj">
|
||||
<rect key="frame" x="8" y="38.666666666666671" width="343" height="17.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="thin" pointSize="13"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="0.20988883259999999" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="dSA-EU-IHj" firstAttribute="leading" secondItem="3ue-Nm-4DV" secondAttribute="leading" constant="8" id="0R9-qo-Q1i"/>
|
||||
<constraint firstAttribute="height" constant="64" id="7R5-Pe-rmK"/>
|
||||
<constraint firstItem="YI7-EJ-eE0" firstAttribute="top" secondItem="3ue-Nm-4DV" secondAttribute="top" constant="8" id="9pK-2q-O4h"/>
|
||||
<constraint firstAttribute="trailing" secondItem="dSA-EU-IHj" secondAttribute="trailing" constant="8" id="GLP-Pa-Zch"/>
|
||||
<constraint firstItem="dSA-EU-IHj" firstAttribute="top" secondItem="YI7-EJ-eE0" secondAttribute="bottom" constant="6" id="SHC-aF-NJl"/>
|
||||
<constraint firstItem="YI7-EJ-eE0" firstAttribute="leading" secondItem="3ue-Nm-4DV" secondAttribute="leading" constant="8" id="Wrd-dd-wPi"/>
|
||||
<constraint firstItem="dSA-EU-IHj" firstAttribute="top" secondItem="YI7-EJ-eE0" secondAttribute="bottom" constant="6" id="j4p-FY-LtB"/>
|
||||
<constraint firstAttribute="bottom" secondItem="dSA-EU-IHj" secondAttribute="bottom" constant="8" id="oWc-uF-wN0"/>
|
||||
<constraint firstAttribute="trailing" secondItem="YI7-EJ-eE0" secondAttribute="trailing" constant="8" id="zkN-O6-Ia3"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="3ue-Nm-4DV" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="1" id="8JX-S2-WBd"/>
|
||||
<constraint firstAttribute="bottom" secondItem="XSu-1W-VIV" secondAttribute="bottom" id="GQ6-Tb-oi2"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="lwK-ip-Hxt" secondAttribute="bottom" constant="5" id="P10-zJ-xYw"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="3ue-Nm-4DV" secondAttribute="trailing" constant="8" id="PVA-pw-f68"/>
|
||||
<constraint firstItem="3ue-Nm-4DV" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="8" id="WzU-Nx-XQs"/>
|
||||
<constraint firstItem="lwK-ip-Hxt" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="8" id="bHF-Am-EVL"/>
|
||||
<constraint firstItem="XSu-1W-VIV" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="top" id="fbC-je-nE9"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="lwK-ip-Hxt" secondAttribute="trailing" constant="8" id="gPG-VH-Rc9"/>
|
||||
<constraint firstItem="XSu-1W-VIV" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="l7b-3R-yTd"/>
|
||||
<constraint firstItem="XSu-1W-VIV" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="qSW-PH-cqC"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="accessTokenWarningView" destination="3ue-Nm-4DV" id="p7e-Ux-82T"/>
|
||||
<outlet property="annotationContextView" destination="lwK-ip-Hxt" id="53f-xt-pJT"/>
|
||||
<outlet property="mapViewContainer" destination="XSu-1W-VIV" id="CyQ-EP-Qwf"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="136.80000000000001" y="132.26600985221674"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="yose" width="234" height="209"/>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>MGLMapboxAccessToken</key>
|
||||
<string>pk.eyJ1IjoibWFwYm94IiwiYSI6ImNqYThuNnZ3NTA5MGMyd3F1cmF1eW1xaGEifQ.TdBTSHHPeT1pfLZ_6x_1vA</string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,239 @@
|
||||
import UIKit
|
||||
import Mapbox
|
||||
|
||||
/*
|
||||
A simple example that uses the Mapbox Outdoors style to view
|
||||
Yosemite National Park, USA with a polygon visualization of the
|
||||
park boundary that can be interacted with.
|
||||
|
||||
A demo access token has been provided for convenience and it may
|
||||
stop working in the future.
|
||||
|
||||
You can obtain your own access token from the
|
||||
[Mapbox account page](https://www.mapbox.com/studio/account/tokens/)
|
||||
and add it to this application's Info.plist as the value for
|
||||
MGLMapboxAccessToken
|
||||
*/
|
||||
class ViewController: UIViewController {
|
||||
|
||||
@IBOutlet weak var mapViewContainer: UIView!
|
||||
@IBOutlet weak var annotationContextView: UIView!
|
||||
@IBOutlet weak var accessTokenWarningView: UIView!
|
||||
|
||||
var mapView: MGLMapView!
|
||||
|
||||
// Define a name for our annotation
|
||||
let annotationIdentifier = "yosemite-fill-layer"
|
||||
|
||||
// Define the name of the map label layer we will want our annotation to be placed under
|
||||
let styleLabelLayer = "mountain-peak-label"
|
||||
|
||||
// The initial center geometric coordinate for the map to display - this is Yosemite National Park, USA
|
||||
let centerCoordinate = CLLocationCoordinate2DMake(37.742241, -119.576923)
|
||||
|
||||
// MARK: - View controller life cycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
setupAnnotationContextView()
|
||||
setupAndAddMapView()
|
||||
setupGestureHandling()
|
||||
}
|
||||
|
||||
// MARK: - Map object interaction
|
||||
|
||||
@objc func didTapMap(sender: UITapGestureRecognizer) {
|
||||
|
||||
// Search for annotation at tap point
|
||||
// If the annotation is found at the tap point then modify some of its properties
|
||||
// If the annotation is not found at the tap point, reset the annotation back to the way it was when originally added
|
||||
if let layer = mapView.style?.layer(withIdentifier: annotationIdentifier) as? MGLFillExtrusionStyleLayer {
|
||||
let point = sender.location(in: mapView)
|
||||
let features = mapView.visibleFeatures(at: point, styleLayerIdentifiers: [annotationIdentifier])
|
||||
if features.count > 0 {
|
||||
layer.fillExtrusionOpacity = MGLStyleValue(rawValue: 0.9)
|
||||
layer.fillExtrusionHeight = MGLStyleValue(rawValue: 5000)
|
||||
let camera = MGLMapCamera(lookingAtCenter: centerCoordinate, fromDistance: 100000, pitch: 60, heading: 180)
|
||||
mapView.fly(to: camera, withDuration: 8, completionHandler: nil)
|
||||
annotationContextView.alpha = 1.0
|
||||
} else {
|
||||
layer.fillExtrusionOpacity = MGLStyleValue(rawValue: 0.5)
|
||||
layer.fillExtrusionHeight = MGLStyleValue(rawValue: 0)
|
||||
let camera = MGLMapCamera(lookingAtCenter: centerCoordinate, fromDistance: 100000, pitch: 60, heading: 0)
|
||||
mapView.fly(to: camera, withDuration: 1, completionHandler: nil)
|
||||
annotationContextView.alpha = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Initialization and setup
|
||||
|
||||
/*
|
||||
Simple (but somewhat styled) stub of a view to present
|
||||
contextual information about tapped objects on the map.
|
||||
*/
|
||||
fileprivate func setupAnnotationContextView() {
|
||||
annotationContextView.alpha = 0
|
||||
let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
|
||||
blurView.frame = annotationContextView.bounds
|
||||
blurView.layer.cornerRadius = 5
|
||||
blurView.clipsToBounds = true
|
||||
blurView.translatesAutoresizingMaskIntoConstraints = false
|
||||
annotationContextView.insertSubview(blurView, at: 0)
|
||||
blurView.topAnchor.constraint(equalTo: annotationContextView.topAnchor).isActive = true
|
||||
blurView.leftAnchor.constraint(equalTo: annotationContextView.leftAnchor).isActive = true
|
||||
blurView.rightAnchor.constraint(equalTo: annotationContextView.rightAnchor).isActive = true
|
||||
blurView.bottomAnchor.constraint(equalTo: annotationContextView.bottomAnchor).isActive = true
|
||||
}
|
||||
|
||||
/*
|
||||
Create and add a Mapbox map view to the the view
|
||||
intended to contain it. Setup with the style, initial
|
||||
location in the world, and make this object the delegate
|
||||
of the map. Finally, set up a camera to control how the
|
||||
user will view the map initially.
|
||||
*/
|
||||
fileprivate func setupAndAddMapView() {
|
||||
mapView = MGLMapView(frame: mapViewContainer.bounds)
|
||||
mapView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
|
||||
mapView.styleURL = MGLStyle.outdoorsStyleURL(withVersion: 10)
|
||||
mapView.setCenter(centerCoordinate, zoomLevel: 10, animated: false)
|
||||
mapView.delegate = self
|
||||
mapViewContainer.addSubview(mapView)
|
||||
|
||||
accessTokenWarningView.layer.cornerRadius = 5
|
||||
accessTokenWarningView.isHidden = true
|
||||
|
||||
// This demo access token has been provided for convenience and it may
|
||||
// stop working in the future.
|
||||
// You can obtain your own access token from the
|
||||
// [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)
|
||||
// and add it to this application's Info.plist as the value for MGLMapboxAccessToken
|
||||
if MGLAccountManager.accessToken() == "pk.eyJ1IjoibWFwYm94IiwiYSI6ImNqYThuNnZ3NTA5MGMyd3F1cmF1eW1xaGEifQ.TdBTSHHPeT1pfLZ_6x_1vA" {
|
||||
accessTokenWarningView.isHidden = false
|
||||
}
|
||||
|
||||
let camera = MGLMapCamera(lookingAtCenter: centerCoordinate, fromDistance: 100000, pitch: 60, heading: 0)
|
||||
mapView.setCamera(camera, animated: false)
|
||||
}
|
||||
|
||||
/*
|
||||
Add a gesture recognizer that will be used to handle user
|
||||
interactions with the map.
|
||||
*/
|
||||
fileprivate func setupGestureHandling() {
|
||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapMap))
|
||||
mapView.addGestureRecognizer(tapGestureRecognizer)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Implementation of Mapbox map view delegate methods relevant to this application
|
||||
*/
|
||||
extension ViewController: MGLMapViewDelegate {
|
||||
|
||||
/*
|
||||
When the map has finished loading the style it's a great time
|
||||
to do work like adding annotations.
|
||||
*/
|
||||
func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) {
|
||||
// This project's bundle contains a geojson file that represents the park boundary of Yosemite National Park in the USA
|
||||
let url = URL(fileURLWithPath: Bundle.main.path(forResource: "yose", ofType: "geojson")!)
|
||||
|
||||
// Create an instance of a structure to represent a polygon
|
||||
// fill visualization that can be injeced into the map's style layers
|
||||
var annotation = StyledAnnotation(identifier: annotationIdentifier, resource: url, type: .extrusion)
|
||||
annotation.color = UIColor(red:1.00, green:0.95, blue:0.75, alpha:1.0)
|
||||
annotation.opacity = 0.75
|
||||
annotation.transitionDuration = 0.35
|
||||
|
||||
// Inject the annotation below the label layer so some map labels
|
||||
// are still visible
|
||||
mapView.addStyledAnnotation(annotation, belowLayer: styleLabelLayer)
|
||||
|
||||
// Alternatively, a more traditional annotation / marker API
|
||||
let pointFeatureAnnotation = MGLPointFeature()
|
||||
let offsetCoordinate = CLLocationCoordinate2D(latitude: centerCoordinate.latitude+0.1, longitude: centerCoordinate.longitude-0.01)
|
||||
pointFeatureAnnotation.coordinate = offsetCoordinate
|
||||
mapView.addAnnotation(pointFeatureAnnotation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
An extention of the Mapbox map view class that offers a simplified
|
||||
API for a small subset of the runtime styling APIs used in this application.
|
||||
This could be extended.
|
||||
*/
|
||||
extension MGLMapView {
|
||||
|
||||
func addStyledAnnotation(_ annotation: StyledAnnotation, belowLayer layerName: String) {
|
||||
if let labelsLayer = self.style?.layer(withIdentifier: "mountain-peak-label") {
|
||||
let source = addSource(for: annotation)
|
||||
let layer = addLayer(for: annotation, source: source)
|
||||
self.style?.insertLayer(layer, below: labelsLayer)
|
||||
}
|
||||
}
|
||||
|
||||
func addStyledAnnotation(_ annotation: StyledAnnotation) {
|
||||
let source = addSource(for: annotation)
|
||||
let layer = addLayer(for: annotation, source: source)
|
||||
self.style?.addLayer(layer)
|
||||
}
|
||||
|
||||
func addSource(for styledAnnotation: StyledAnnotation) -> MGLSource {
|
||||
let source = MGLShapeSource(identifier: UUID().uuidString, url: styledAnnotation.resource, options: nil)
|
||||
self.style?.addSource(source)
|
||||
return source
|
||||
}
|
||||
|
||||
func addLayer(for styledAnnotation: StyledAnnotation, source: MGLSource) -> MGLStyleLayer {
|
||||
switch styledAnnotation.type {
|
||||
case .extrusion:
|
||||
let layer = MGLFillExtrusionStyleLayer(identifier: styledAnnotation.identifier, source: source)
|
||||
if let height = styledAnnotation.height {
|
||||
layer.fillExtrusionHeight = MGLStyleValue<NSNumber>(rawValue: NSNumber(floatLiteral: Double(height)))
|
||||
}
|
||||
if let color = styledAnnotation.color {
|
||||
layer.fillExtrusionColor = MGLStyleValue(rawValue: color)
|
||||
}
|
||||
if let opacity = styledAnnotation.opacity {
|
||||
layer.fillExtrusionOpacity = MGLStyleValue<NSNumber>(rawValue: NSNumber(floatLiteral: Double(opacity)))
|
||||
}
|
||||
if let duration = styledAnnotation.transitionDuration {
|
||||
layer.fillExtrusionHeightTransition = MGLTransitionMake(Double(duration), 0)
|
||||
layer.fillExtrusionOpacityTransition = MGLTransitionMake(Double(duration), 0)
|
||||
}
|
||||
return layer
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
A struct to represent a small subset of the annotations that
|
||||
are possible to add to a Mapbox map. This could be extended.
|
||||
*/
|
||||
struct StyledAnnotation {
|
||||
|
||||
enum StyledAnnotationType {
|
||||
case extrusion
|
||||
}
|
||||
|
||||
init(identifier: String, resource: URL, type: StyledAnnotationType) {
|
||||
self.identifier = identifier
|
||||
self.resource = resource
|
||||
self.type = type
|
||||
}
|
||||
|
||||
var identifier: String
|
||||
var resource: URL
|
||||
var type: StyledAnnotationType
|
||||
|
||||
var height: Float?
|
||||
var color: UIColor?
|
||||
var opacity: Float?
|
||||
var transitionDuration: Float?
|
||||
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -54,7 +54,7 @@ function verifyFramework() {
|
||||
|
||||
# Verify the static lib at least has simulator and the two common ARM architectures
|
||||
local PRESENT_ARCHITECTURES=$( xcrun lipo -info "${BINARY_PATH}" )
|
||||
for arch in "armv7" "arm64" "i386" "x86_64"; do
|
||||
for arch in "armv7" "arm64" "x86_64"; do
|
||||
if [[ ! $PRESENT_ARCHITECTURES == *$arch* ]]; then
|
||||
printf "ERROR: Architecture ${arch} not found in ${FRAMEWORK_NAME}\n";
|
||||
exit 6;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user