Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed custom map type on iOS version #3391

Open
wants to merge 28 commits into
base: ucr
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5eba6d5
Fix buildserver long extension error in Windows Java 11
ewpatton Mar 8, 2024
334f73e
Further Windows improvements
ewpatton Mar 12, 2024
45e58b6
Only copy .dex files to the bundle dex dir
ewpatton Mar 13, 2024
2fe3327
Trial a custom URL
Mar 29, 2024
13d959a
Add get/setCustomUrl to Map & MapControllers
Mar 29, 2024
d5b7054
Working, except UI and {} problems
Apr 2, 2024
9d339a1
Fix example
Apr 2, 2024
2140d22
Auto updated map component docs
Apr 2, 2024
07a5895
Merge branch 'hotfix/windows-java11' into add-custom-url-for-map-comp…
Apr 10, 2024
2e1843e
PR review feedback
Apr 10, 2024
1212957
Add project upgrade paths as per review
Apr 10, 2024
a1ad559
Review documentation
Apr 10, 2024
2ccc826
Fixed bad documentation i.e. [z] using HTML escape characters
Apr 23, 2024
b91feeb
Fix missing 4th option in YoungAndroidMapTypePropertyEditor
Apr 24, 2024
9f65523
Add validation for CustomURL
Apr 28, 2024
21247b6
Store broken attempt N using –CountDownLatch
May 13, 2024
8b905f1
Finished sample tile validation check
May 13, 2024
bec7a47
Initial commit
Jun 9, 2024
a842c2b
Ready for first build with static customUrl
Jun 9, 2024
a34b9d7
Missed privtae var
Jun 9, 2024
c508dcc
Add getter/setter
Jun 9, 2024
49fd429
Add MockMap updates
Jun 9, 2024
d27ec58
Merge branch 'mit-cml:master' into ios-testing
petersmythe Jun 9, 2024
ece8a7d
Fixed MockMap - set CustomUrl
Jun 9, 2024
a1b6a1e
Merge branch 'ios-testing' of https://github.com/petersmythe/appinven…
Jun 9, 2024
9637f2a
Merge remote-tracking branch 'upstream/ucr' into upstream/pull/3160/head
ghu999 Feb 25, 2025
b4826ad
Fixed Custom MapType on iOS version
ghu999 Feb 25, 2025
a6e20f2
Update CustomURL to Advanced
ghu999 Feb 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -5363,6 +5363,14 @@ String newerVersionComponentException(String componentType, int srcCompVersion,
@Description("Terrain map type")
String mapTypeTerrain();

@DefaultMessage("Custom")
@Description("Custom map type")
String mapTypeCustom();

@DefaultMessage("CustomUrl")
@Description("The URL of the custom tile layer to use as the base of the map")
String mapCustomUrl();

@DefaultMessage("Metric")
@Description("Display name for the metric unit system")
String mapScaleUnitsMetric();
Expand Down Expand Up @@ -5431,6 +5439,22 @@ String newerVersionComponentException(String componentType, int srcCompVersion,
@Description("")
String expectedLatLongPair(String property);

@DefaultMessage("The provided URL {0} does not contain placeholders for {1}.") // Can't use {x} here, Java compiler tries to interpret the variable x
@Description("")
String customUrlNoPlaceholders(String property, String placeholders);

@DefaultMessage("The provided URL {0}, when tested, failed authentication (with HTTP status code {1}).")
@Description("")
String customUrlBadAuthentication(String property, int statusCode);

@DefaultMessage("The provided URL {0}, when tested, returned a bad HTTP status code ({1}).")
@Description("")
String customUrlBadStatusCode(String property, int statusCode);

@DefaultMessage("The provided URL {0}, when tested, returned an exception ({1}).")
@Description("")
String customUrlException(String property, String e);

@DefaultMessage("Notice!")
@Description("Title for the Warning Dialog Box")
String NoticeTitle();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public final class MockMap extends MockContainer {
protected static final String PROPERTY_NAME_LATITUDE = "Latitude";
protected static final String PROPERTY_NAME_LONGITUDE = "Longitude";
protected static final String PROPERTY_NAME_MAP_TYPE = "MapType";
protected static final String PROPERTY_NAME_CUSTOM_URL = "CustomUrl";
protected static final String PROPERTY_NAME_CENTER_FROM_STRING = "CenterFromString";
protected static final String PROPERTY_NAME_ZOOM_LEVEL = "ZoomLevel";
protected static final String PROPERTY_NAME_SHOW_COMPASS = "ShowCompass";
Expand Down Expand Up @@ -181,6 +182,8 @@ public void onPropertyChange(String propertyName, String newValue) {
invalidateMap();
} else if (propertyName.equals(PROPERTY_NAME_MAP_TYPE)) {
setMapType(newValue);
} else if (propertyName.equals(PROPERTY_NAME_CUSTOM_URL)) {
setCustomUrl(newValue);
} else if (propertyName.equals(PROPERTY_NAME_CENTER_FROM_STRING)) {
setCenter(newValue);
} else if (propertyName.equals(PROPERTY_NAME_ZOOM_LEVEL)) {
Expand Down Expand Up @@ -222,6 +225,10 @@ private void setMapType(String tileLayerId) {
}
}

private void setCustomUrl(String newCustomUrl) {
updateCustomUrl(newCustomUrl);
}

private void setCenter(String center) {
String[] parts = center.split(",");
if (parts.length != 2) {
Expand Down Expand Up @@ -484,7 +491,10 @@ private native void initPanel()/*-{
attribution: 'Satellite imagery &copy; <a href="http://mapquest.com">USGS</a>'}),
L.tileLayer('//basemap.nationalmap.gov/ArcGIS/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}',
{minZoom: 0, maxZoom: 15,
attribution: 'Map data &copy; <a href="http://www.usgs.gov">USGS</a>'})
attribution: 'Map data &copy; <a href="http://www.usgs.gov">USGS</a>'}),
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',
{minZoom: 0, maxZoom: 18,
attribution: 'Custom map'})
];
this.@com.google.appinventor.client.editor.simple.components.MockMap::tileLayers = tileLayers;
this.@com.google.appinventor.client.editor.simple.components.MockMap::baseLayer =
Expand Down Expand Up @@ -606,6 +616,23 @@ private native void updateMapType(int type)/*-{
}
}-*/;

private native void updateCustomUrl(String customUrl)/*-{
var L = $wnd.top.L;
var map = this.@com.google.appinventor.client.editor.simple.components.MockMap::mapInstance;
var tileLayers = this.@com.google.appinventor.client.editor.simple.components.MockMap::tileLayers;
var baseLayer = this.@com.google.appinventor.client.editor.simple.components.MockMap::baseLayer;
if (map && baseLayer && tileLayers) {
tileLayers[4] = L.tileLayer(customUrl,
{minZoom: 0, maxZoom: 18,
attribution: 'Custom map data'});
map.removeLayer(baseLayer);
baseLayer = tileLayers[4];
map.addLayer(baseLayer);
baseLayer.bringToBack();
this.@com.google.appinventor.client.editor.simple.components.MockMap::tileLayers = tileLayers;
}
}-*/;

native LatLng projectFromXY(int x, int y)/*-{
var map = this.@com.google.appinventor.client.editor.simple.components.MockMap::mapInstance;
if (map) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import com.google.appinventor.client.editor.youngandroid.properties.YoungAndroidListViewLayoutChoicePropertyEditor;
import com.google.appinventor.client.editor.youngandroid.properties.YoungAndroidMapScaleUnitsPropertyEditor;
import com.google.appinventor.client.editor.youngandroid.properties.YoungAndroidMapTypePropertyEditor;
import com.google.appinventor.client.editor.youngandroid.properties.YoungAndroidMapCustomUrlPropertyEditor;
import com.google.appinventor.client.editor.youngandroid.properties.YoungAndroidNavigationMethodChoicePropertyEditor;
import com.google.appinventor.client.editor.youngandroid.properties.YoungAndroidRecyclerViewOrientationPropertyEditor;
import com.google.appinventor.client.editor.youngandroid.properties.YoungAndroidScreenAnimationChoicePropertyEditor;
Expand Down Expand Up @@ -266,6 +267,8 @@ public static PropertyEditor createPropertyEditor(String editorType, String defa
return new YoungAndroidFloatRangePropertyEditor(-180, 180);
} else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_MAP_TYPE)) {
return new YoungAndroidMapTypePropertyEditor();
} else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_MAP_CUSTOMURL)) {
return new YoungAndroidMapCustomUrlPropertyEditor();
} else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_MAP_UNIT_SYSTEM)) {
return new YoungAndroidMapScaleUnitsPropertyEditor();
} else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_MAP_ZOOM)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2024 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

package com.google.appinventor.client.editor.youngandroid.properties;

import com.google.appinventor.client.widgets.properties.TextPropertyEditor;
import static com.google.appinventor.client.Ode.MESSAGES;
import com.google.gwt.http.client.*;
import com.google.gwt.user.client.Window;

/**
* Property editor for Map custom URL matching a particular format.
*/
public class YoungAndroidMapCustomUrlPropertyEditor extends TextPropertyEditor {

public YoungAndroidMapCustomUrlPropertyEditor() {
}

@Override
protected void validate(String text) throws InvalidTextException {
// Check that the custom URL looks vaguely correct
if (!(text.startsWith("https://") || text.startsWith("http://"))
|| !text.contains("{x}")
|| !text.contains("{y}")
|| !text.contains("{z}")) {
throw new InvalidTextException(MESSAGES.customUrlNoPlaceholders(text, "{x}, {y} and {z}"));
}

// Try to request a single tile from the custom URL source as a final validation, only report errors
String urlString = text.replace("{x}", "0")
.replace("{y}", "0")
.replace("{z}", "0");
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, urlString);
try {
builder.sendRequest(null, new RequestCallback() {
@Override
public void onResponseReceived(Request request, Response response) {
handleResponseCode(urlString, response.getStatusCode());
}

@Override
public void onError(Request request, Throwable exception) {
handleRequestError(urlString, exception);
}
});
} catch (RequestException e) {
throw new InvalidTextException(MESSAGES.customUrlException(urlString, e.getMessage()));
}
}

// Window.alert is used here, rather than throw InvalidTextException, due to RequestBuilder Override signatures
private void handleResponseCode(String urlString, int responseCode) {
if (responseCode == 401 || responseCode == 403) {
Window.alert(MESSAGES.customUrlBadAuthentication(urlString, responseCode));
} else if (responseCode >= 400) {
Window.alert(MESSAGES.customUrlBadStatusCode(urlString, responseCode));
}
}

private void handleRequestError(String urlString, Throwable exception) {
Window.alert(MESSAGES.customUrlException(urlString, exception.getMessage()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public class YoungAndroidMapTypePropertyEditor extends ChoicePropertyEditor {
private static final Choice[] mapTypes = new Choice[] {
new Choice(MESSAGES.mapTypeRoads(), "1"),
new Choice(MESSAGES.mapTypeAerial(), "2"),
new Choice(MESSAGES.mapTypeTerrain(), "3")
new Choice(MESSAGES.mapTypeTerrain(), "3"),
new Choice(MESSAGES.mapTypeCustom(), "4")
};

public YoungAndroidMapTypePropertyEditor() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2064,6 +2064,10 @@ private static int upgradeMapProperties(Map<String, JSONValue> componentProperti
// Adds ScaleUnits and MapType dropdowns.
srcCompVersion = 6;
}
if (srcCompVersion < 7) {
// Adds CustomUrl (MapType 4).
srcCompVersion = 7;
}
return srcCompVersion;
}

Expand Down
10 changes: 7 additions & 3 deletions appinventor/blocklyeditor/src/versioning.js
Original file line number Diff line number Diff line change
Expand Up @@ -2506,9 +2506,13 @@ Blockly.Versioning.AllUpgradeMaps =
// AI2:
// - Adds Units and MapType dropdowns.
6: [Blockly.Versioning.makeSetterUseDropdown(
'Map', 'ScaleUnits', 'ScaleUnits'),
Blockly.Versioning.makeSetterUseDropdown(
'Map', 'MapType', 'MapType')]
'Map', 'ScaleUnits', 'ScaleUnits'),
Blockly.Versioning.makeSetterUseDropdown(
'Map', 'MapType', 'MapType')],

// AI2:
// - Adds CustomUrl (MapType 4).
7: "noUpgrade"

}, // End Map upgraders

Expand Down
2 changes: 1 addition & 1 deletion appinventor/components-ios/src/ErrorMessages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ import Foundation
case .ERROR_INVALID_ANCHOR_HORIZONTAL:
return "Invalid value %d given for AnchorHorizontal. Valid settings are 1, 2, or 3."
case .ERROR_INVALID_MAP_TYPE:
return "The MapType must be 1, 2, or 3"
return "The MapType must be 1, 2, 3, or 4"

// File Errors
case .ERROR_CANNOT_FIND_FILE:
Expand Down
45 changes: 42 additions & 3 deletions appinventor/components-ios/src/Map.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ enum AIMapType: Int32 {
case roads = 1
case aerial = 2
case terrain = 3
case custom = 4
}

typealias CLLocationDirection = Double
Expand Down Expand Up @@ -76,7 +77,8 @@ open class Map: ViewComponent, MKMapViewDelegate, UIGestureRecognizerDelegate, M
private var _featuresState = 0
private var _boundsChangeReady: Bool = false
private var _terrainOverlay: MKTileOverlay?

private var _customUrlOverlay: MKTileOverlay?
private var _customURL = ""
private var _activeOverlay: MapOverlayShape? = nil
private var _lastPoint: CLLocationCoordinate2D? = nil
private var _activeMarker: Marker? = nil
Expand Down Expand Up @@ -158,6 +160,7 @@ open class Map: ViewComponent, MKMapViewDelegate, UIGestureRecognizerDelegate, M
EnableZoom = true
EnablePan = true
MapType = 1
CustomUrl = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
Rotation = 0.0
ScaleUnits = 1
ShowZoom = false
Expand Down Expand Up @@ -347,7 +350,7 @@ open class Map: ViewComponent, MKMapViewDelegate, UIGestureRecognizerDelegate, M
return _mapType.rawValue
}
set(type) {
if !(1...3 ~= type) {
if !(1...4 ~= type) {
form?.dispatchErrorOccurredEvent(self, "MapType", ErrorMessage.ERROR_INVALID_MAP_TYPE.code,
ErrorMessage.ERROR_INVALID_MAP_TYPE.message)
return
Expand All @@ -357,17 +360,38 @@ open class Map: ViewComponent, MKMapViewDelegate, UIGestureRecognizerDelegate, M
switch _mapType {
case .roads:
removeTerrainTileRenderer()
removeCustomUrlTileRenderer()
mapView.mapType = .standard
case .aerial:
removeTerrainTileRenderer()
removeCustomUrlTileRenderer()
mapView.mapType = .satellite
case .terrain:
removeCustomUrlTileRenderer()
mapView.mapType = .standard // set that way zooming in too far displays a visible grid
setupTerrainTileRenderer()
case .custom:
removeTerrainTileRenderer()
mapView.mapType = .standard
setupCustomUrlTileRenderer()
}
}
}

@objc open var CustomUrl: String? {
get {
return _customURL
}
set(newUrl) {
guard let newUrl = newUrl, newUrl != CustomUrl else {
return
}
_customURL = newUrl
removeCustomUrlTileRenderer()
setupCustomUrlTileRenderer()
}
}

@objc open var ScaleUnits: Int32 {
get {
return _scaleUnits
Expand Down Expand Up @@ -887,9 +911,24 @@ open class Map: ViewComponent, MKMapViewDelegate, UIGestureRecognizerDelegate, M
}
}

/**
* Adds a custom tile overlay that matches the CustomUrl overlay on Android
*/
private func setupCustomUrlTileRenderer() {
_customUrlOverlay = MKTileOverlay(urlTemplate: CustomUrl)
_customUrlOverlay!.canReplaceMapContent = true
mapView.insertOverlay(_customUrlOverlay!, at: 0, level: .aboveLabels)
}

private func removeCustomUrlTileRenderer() {
if let overlay = _customUrlOverlay {
mapView.removeOverlay(overlay)
}
}

public func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let tileOverlay = overlay as? MKTileOverlay {
return _mapType == .terrain ? MKTileOverlayRenderer(tileOverlay: tileOverlay) : MKOverlayRenderer()
return (_mapType == .terrain || _mapType == .custom) ? MKTileOverlayRenderer(tileOverlay: tileOverlay) : MKOverlayRenderer()
} else if let shape = overlay as? MapCircleOverlay {
let renderer = MKCircleRenderer(circle: shape)
shape.renderer = renderer
Expand Down
1 change: 1 addition & 0 deletions appinventor/components-ios/src/MapFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,5 @@ enum MapType: Int32 {
case ROADS = 1
case AERIAL = 2
case TERRAIN = 3
case CUSTOM = 4
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
public enum MapType implements OptionList<Integer> {
Road(1),
Aerial(2),
Terrain(3);
Terrain(3),
Custom(4);

private final Integer value;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ private PropertyTypeConstants() {}
*/
public static final String PROPERTY_TYPE_MAP_TYPE = "map_type";

/**
* Map custom URL template required by the Map component.
* @see
* com.google.appinventor.client.editor.youngandroid.properties.YoungAndroidMapCustomUrlPropertyEditor
*/
public static final String PROPERTY_TYPE_MAP_CUSTOMURL = "map_customurl";

/**
* Integer values limited to the range of valid map zoom levels [1, 18].
* @see com.google.appinventor.client.editor.youngandroid.properties.YoungAndroidMapZoomPropertyEditor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,9 @@ private YaVersion() {
// - Added ScaleUnits property
// For MAP_COMPONENT_VERSION 6:
// - Adds ScaleUnits and MapType dropdowns.
public static final int MAP_COMPONENT_VERSION = 6;
// For MAP_COMPONENT_VERSION 7:
// - Adds CustomUrl (MapType 4).
public static final int MAP_COMPONENT_VERSION = 7;

// For MARKER_COMPONENT_VERSION 1:
// - Initial Marker implementation using OpenStreetMap
Expand Down
Loading