Skip to content

Commit

Permalink
From Quicklook straight to the map when closing in activateAR mode (#8
Browse files Browse the repository at this point in the history
)

* FIX auto quit QuickLook now skips the 3D desktop view

* Version bump

* ENH better timing management for auto activating the AR

* FIX min zoom and improved relative base thickness computation

* ADD option for high res texture

* Added to changelog + formatting

* FIX example
  • Loading branch information
jonathanlurie authored Mar 8, 2024
1 parent bc51e0b commit 1b87f66
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
root: true,
ignorePatterns: ["**/*.js"],
ignorePatterns: ["**/*.js", "src/platform-constants.ts"],

overrides: [
{
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# MapTiler AR Control Changelog

## v1.2.0
### New Features
- Possibility to add a marketting image on top of the 3D view
- Possibility to increase the resolution of the texture for non-iOS devices
- if AR is automatically ran, the intermediate 3D view no longer shows on iOS (at both begining and end of AR)
- The elevation padding of the 3D models now depends on zoom level and minimal elevation
- If possible and desired to go straight to AR mode, the control icon now features a box logo with brackets

### Bug Fixes
- Very small extents at high zoom level are no longer possible as they were generating bad looking meshes. Min zoom: 16

## v1.1.0
### New Features
- Possibility to launch the AR mode automatically (when device allows) with the opt-in option `activateAR` set to `true`
Expand Down
2 changes: 1 addition & 1 deletion demos/auto-activate.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
width: 100vw;
height: 100vh;
background-color: #3e4c55;
z-index: 3;
z-index: 4;
display: none;
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@maptiler/ar-control",
"version": "1.1.0",
"version": "1.2.0",
"description": "AR Control for MapTiler SDK",
"type": "module",
"homepage": "https://docs.maptiler.com/sdk-js/modules/ar/",
Expand Down
3 changes: 2 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ There are two events:

The AR control performs some temporary changes to the map view, so these events are handy to hide those transformations behind a curtain or displaying a message.

In the [example](./examples/index.html), we show a fullscreen overlay with a waiting message at `computeStart` and hide it at `computeEnd`, just by dynamically updating the `.style.display` property of the overlay.
In the [example](./examples/index.html), we show a fullscreen overlay with a waiting message at `computeStart` and hides it at `computeEnd`, just by dynamically updating the `.style.display` property of the overlay. Keep in mind that the `z-index` CSS property of this overlay must be higher than the 3D model view, so greater than `3`.
When using React, you may want to replace this logic by a change of state.

# Options
Expand All @@ -110,6 +110,7 @@ The constructor `MaptilerARControl` accepts an option object to customize the lo
- `logoHeight`(number): the height of the logo in pixels (if any). Default: `60`
- `logoClass` (string): CSS class to add to the class list of the `<img>` element holding the logo (if any). If used, the `.logoHeight` as well as the default styling will no longer be applied.
- `activateAR` (boolean): When the platform allows, setting this to `true` automatically activates the AR mode as soon as the data is ready. Quick Look on iOS is likely to allow this, while WebXR on Android is not likeley to. Default: `false`
- `highRes` (boolean): increases the resolution of the texture. Will most likely have no effect on iOS due to some format limitation. Default: `false`.

![](images/screenshot2.jpg)

Expand Down
100 changes: 85 additions & 15 deletions src/MaptilerARControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { Map, LngLatBounds, LngLat, IControl } from "@maptiler/sdk";
import { ModelViewerElement } from "@google/model-viewer";
import * as platformConstants from "./platform-constants.ts";

import EventEmitter from "events";
import * as THREE from "three";
Expand Down Expand Up @@ -34,6 +35,7 @@ function removeDomNode(node: HTMLElement) {

const MIN_TERRAIN_ZOOM = 12;
const TERRAIN_TILE_SIZE = 512;
const MAX_ZOOM = 16;

function latLon2Tile(
zoom: number,
Expand Down Expand Up @@ -261,6 +263,12 @@ export type MaptilerARControlOptions = {
* Default: `false`
*/
activateAR?: boolean;

/**
* Generate a mesh with a higher resolution texture.
* Default: `false`
*/
highRes?: false;
};

const defaultOptionValues: MaptilerARControlOptions = {
Expand Down Expand Up @@ -288,6 +296,7 @@ const defaultOptionValues: MaptilerARControlOptions = {
edgeColor: "#7b8487",
logo: "",
activateAR: false,
highRes: false,
};

const defaultArButtonStyle = {
Expand Down Expand Up @@ -389,12 +398,37 @@ export class MaptilerARControl extends EventEmitter implements IControl {
iconSpan.className = "maplibregl-ctrl-icon";
iconSpan.setAttribute("aria-hidden", "true");
this.controlButton.appendChild(iconSpan);
iconSpan.innerHTML = `
<svg width="100%" height="100%" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
const boxIcon = `
<svg width="100%" height="100%" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="M22.701,35.306L14.997,30.784C14.621,30.554 14.33,30.251 14.121,29.873C13.913,29.496 13.81,29.091 13.81,28.659L13.81,19.652C13.81,19.22 13.913,18.815 14.121,18.438C14.33,18.061 14.621,17.757 14.997,17.527L22.737,12.925C23.117,12.706 23.537,12.595 23.996,12.595C24.454,12.595 24.874,12.706 25.255,12.925L32.994,17.527C33.37,17.757 33.662,18.061 33.87,18.438C34.078,18.815 34.183,19.22 34.183,19.652L34.183,28.659C34.183,29.091 34.075,29.496 33.861,29.873C33.647,30.251 33.346,30.554 32.959,30.784L25.147,35.306C24.759,35.532 24.348,35.644 23.918,35.644C23.487,35.644 23.081,35.532 22.701,35.306ZM25.073,32.57L31.735,28.582L31.735,20.945L25.073,24.754L25.073,32.57ZM22.918,32.57L22.918,24.754L16.292,20.945L16.292,28.582L22.918,32.57ZM23.996,15.159L17.361,18.97L23.996,22.809L30.631,18.97L23.996,15.159Z" style="fill:rgb(68,73,82);"/>
</svg>
`;

const boxIconWithBrackets = `
<svg width="100%" height="100%" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="M22.701,35.306L14.997,30.784C14.621,30.554 14.33,30.251 14.121,29.873C13.913,29.496 13.81,29.091 13.81,28.659L13.81,19.652C13.81,19.22 13.913,18.815 14.121,18.438C14.33,18.061 14.621,17.757 14.997,17.527L22.737,12.925C23.117,12.706 23.537,12.595 23.996,12.595C24.454,12.595 24.874,12.706 25.255,12.925L32.994,17.527C33.37,17.757 33.662,18.061 33.87,18.438C34.078,18.815 34.183,19.22 34.183,19.652L34.183,28.659C34.183,29.091 34.075,29.496 33.861,29.873C33.647,30.251 33.346,30.554 32.959,30.784L25.147,35.306C24.759,35.532 24.348,35.644 23.918,35.644C23.487,35.644 23.081,35.532 22.701,35.306ZM25.073,32.57L31.735,28.582L31.735,20.945L25.073,24.754L25.073,32.57ZM22.918,32.57L22.918,24.754L16.292,20.945L16.292,28.582L22.918,32.57ZM23.996,15.159L17.361,18.97L23.996,22.809L30.631,18.97L23.996,15.159Z" style="fill:rgb(68,73,82);"/>
<g transform="matrix(0.712957,0,0,0.712957,6.88903,6.88903)">
<path d="M13.45,44L7,44C6.175,44 5.469,43.706 4.881,43.119C4.294,42.531 4,41.825 4,41L4,34.55L7,34.55L7,41L13.45,41L13.45,44Z" style="fill:rgb(68,73,82);"/>
</g>
<g transform="matrix(0.712957,0,0,0.712957,6.88903,6.88903)">
<path d="M34.55,44L34.55,41L41,41L41,34.55L44,34.55L44,41C44,41.825 43.706,42.531 43.119,43.119C42.531,43.706 41.825,44 41,44L34.55,44Z" style="fill:rgb(68,73,82);"/>
</g>
<g transform="matrix(0.712957,0,0,0.712957,6.88903,6.88903)">
<path d="M4,13.45L4,7C4,6.175 4.294,5.469 4.881,4.881C5.469,4.294 6.175,4 7,4L13.45,4L13.45,7L7,7L7,13.45L4,13.45Z" style="fill:rgb(68,73,82);"/>
</g>
<g transform="matrix(0.712957,0,0,0.712957,6.88903,6.88903)">
<path d="M41,13.45L41,7L34.55,7L34.55,4L41,4C41.825,4 42.531,4.294 43.119,4.881C43.706,5.469 44,6.175 44,7L44,13.45L41,13.45Z" style="fill:rgb(68,73,82);"/>
</g>
</svg>
`;

iconSpan.innerHTML =
this.options.activateAR &&
(platformConstants.IS_AR_QUICKLOOK_CANDIDATE ||
platformConstants.IS_WEBXR_AR_CANDIDATE)
? boxIconWithBrackets
: boxIcon;

this.controlButton.type = "button";

this.controlButton.addEventListener("click", async () => {
Expand Down Expand Up @@ -426,9 +460,8 @@ export class MaptilerARControl extends EventEmitter implements IControl {

await this.buildMapModel();

this.displayModal();
// this.emit("computeEnd");
// this.downloadUSDZ()
await this.displayModal();

this.lock = false;
}

Expand Down Expand Up @@ -502,7 +535,7 @@ export class MaptilerARControl extends EventEmitter implements IControl {

const topViewCameraSettings = {
center: this.cameraSettings.center,
zoom: this.cameraSettings.zoom,
zoom: Math.min(this.cameraSettings.zoom, MAX_ZOOM),
pitch: 0,
bearing: 0,
};
Expand Down Expand Up @@ -729,6 +762,12 @@ export class MaptilerARControl extends EventEmitter implements IControl {
}

private async computeTextures() {
const originalPixelRatio = this.map.getPixelRatio();

if (this.options.highRes) {
this.map.setPixelRatio(4);
}

// Set the view from top and axis-aligned
this.enableTopView();
await this.computeColorData();
Expand All @@ -744,6 +783,10 @@ export class MaptilerARControl extends EventEmitter implements IControl {
const distance = middleEast.distanceTo(middleWest);
this.meterPerPixelCenter = distance / this.colorData?.width;

if (this.options.highRes) {
this.map.setPixelRatio(originalPixelRatio);
}

// Set the camera back to normal
this.restoreMapSettings();

Expand Down Expand Up @@ -881,8 +924,9 @@ export class MaptilerARControl extends EventEmitter implements IControl {
}
}

// Flooring the minimum elevation to the lower hundred meter
minEle = Math.max(0, ~~(minEle / 100) * 100 - 100);
const almostMaxZoom = MAX_ZOOM - 1;
const z = Math.min(this.map.getZoom(), almostMaxZoom);
minEle = minEle - (50 * (z - almostMaxZoom) ** 2 + 30);

for (let i = 0; i < positionBuf.length / 3; i += 1) {
const r = this.terrainData.pixelData[i * 4];
Expand Down Expand Up @@ -980,7 +1024,7 @@ export class MaptilerARControl extends EventEmitter implements IControl {

// error
(err) => {
console.log("error:", err);
console.warn("error:", err);
},

// options
Expand Down Expand Up @@ -1048,7 +1092,6 @@ export class MaptilerARControl extends EventEmitter implements IControl {
const container = this.map.getContainer();
const modelBlobGLB = await this.getModelBlobGLB();
const modelObjectURLGLB = URL.createObjectURL(modelBlobGLB);
this.emit("computeEnd");

this.modelViewer = new ModelViewerElement();
this.modelViewer.src = modelObjectURLGLB;
Expand Down Expand Up @@ -1108,8 +1151,6 @@ export class MaptilerARControl extends EventEmitter implements IControl {

this.modelViewer.appendChild(this.closeButton);

// this.modelViewer.activateAR();

this.closeButton.addEventListener("click", async () => {
this.close();
});
Expand Down Expand Up @@ -1138,22 +1179,51 @@ export class MaptilerARControl extends EventEmitter implements IControl {
}

// Automatically run the AR
let successfullyEnabledAR = false;
if (this.options.activateAR) {
// Wait for Model Viewer to be ready
this.modelViewer.addEventListener("load", () => {
this.modelViewer.addEventListener("load", async () => {
if (this.modelViewer.canActivateAR) {
this.modelViewer.activateAR();
try {
await this.modelViewer.activateAR();
successfullyEnabledAR = true;
// Waiting a sec before fireing event because Quicklook takes some time to start
setTimeout(() => this.emit("computeEnd"), 1000);
} catch (e) {
console.warn("AR to be automatically activated but failed.");
this.emit("computeEnd");
}
} else {
console.warn("AR cannot be automatically activated.");
this.emit("computeEnd");
}
});
} else {
this.emit("computeEnd");
}

this.modelViewer.addEventListener(
"camera-change",
(e: CustomEvent<CameraChangeDetails>) => {
// If AR was successfully enabled, then such event is fired when coming back to
// Mode Viewer 3D view, and auto activate AR also mean going back straight to SDK view
// without showing MV in between
if (successfullyEnabledAR && e.detail.source === "automatic") {
this.close();
}
}
);
}

close() {
this.dispose();
removeDomNode(this.arButton);
removeDomNode(this.modelViewer);
removeDomNode(this.closeButton);
removeDomNode(this.logoImgElement);

if (this.logoImgElement) {
removeDomNode(this.logoImgElement);
}
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/maptiler-ar-control.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./MaptilerARControl";
export * from "./platform-constants";
Loading

0 comments on commit 1b87f66

Please sign in to comment.