Merge branch 'release-agua' back to master.

This commit is contained in:
Fabian Guerra
2018-01-04 17:13:55 -06:00
125 changed files with 3968 additions and 678 deletions

View File

@@ -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:

View File

@@ -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: |

View File

@@ -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

View File

@@ -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 {

View File

@@ -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)
}
}
}

View File

@@ -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"/>

View File

@@ -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;
}
}

View File

@@ -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
+ '}';
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}
/**

View File

@@ -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
*/

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);
}
});

View File

@@ -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

View File

@@ -28,7 +28,7 @@ public class MapSnapshot {
}
/**
* @return the bitmap
* @return the large
*/
public Bitmap getBitmap() {
return bitmap;

View File

@@ -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;
}
}
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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\">&copy; Mapbox</a> <a href=\"http://www.openstreetmap.org/about/\" target=\"_blank\">&copy; 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\">&copy; Mapbox</a> <a href=\"http://www.openstreetmap.org/about/\" target=\"_blank\">&copy; 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\">&copy; 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)
);
}
}

View File

@@ -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();
}
}

View File

@@ -806,4 +806,4 @@
<!-- android:value="true" /> -->
</application>
</manifest>
</manifest>

View File

@@ -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

View File

@@ -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) {

View File

@@ -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");

View File

@@ -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>

View File

@@ -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}"
]

View File

@@ -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";

View 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>

View File

@@ -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";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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

View File

@@ -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];
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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

View File

@@ -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().

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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)

View 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>";

View File

@@ -32,6 +32,9 @@ custom_categories:
- MGLMapCamera
- MGLMapView
- MGLMapViewDelegate
- MGLMapSnapshot
- MGLMapSnapshotOptions
- MGLMapSnapshotter
- MGLUserTrackingMode
- name: Primitive Shapes
children:

View File

@@ -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";

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "mapbox_helmet.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

File diff suppressed because one or more lines are too long

View File

@@ -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";

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -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"

View File

@@ -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 */;
}

View File

@@ -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
}
}

View File

@@ -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"
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -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"
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -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