Skip to content

Commit

Permalink
feat: handle the token in query params
Browse files Browse the repository at this point in the history
  • Loading branch information
Youssef-Harby committed Apr 12, 2024
1 parent d40f717 commit 95be77c
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 43 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ This project provides a utility for converting Esri Vector Tile Styles to Mapbox

## Current Status

- ✅ The conversion from Esri to Mapbox style specifications is fully supported and operational.
- ✅ The conversion from Esri to Mapbox style specifications is partially supported and operational ( open GitHub issue if you find any issues).
- ❌ The conversion from Mapbox to Esri style specifications is not yet implemented.
- ArcGIS Vector Tile Server service endpoint is not yet supported (the mapbox works in ArcGIS Map SDK but not in ArcGIS Online or ArcGIS Pro so it needs to emulate the service endpoint).

## How to Use

Expand Down Expand Up @@ -54,4 +55,4 @@ Contributions are welcome, especially in implementing the conversion from Mapbox

## License

This project is licensed under the MIT License - see the LICENSE file for details.
This project is licensed under the MIT License - see the LICENSE file for details. - this project is for Education purpose only.
13 changes: 0 additions & 13 deletions example/src/App.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script lang="ts">
import {
mapState,
esriBasemapUrl,
maplibreBasemapUrl,
esriStyleJson,
Expand All @@ -13,8 +12,6 @@
resolveEsriRelativePaths,
constructMapboxStyleFromEsriAbsolute,
} from "@esri-style-ft-mapbox-style/esriToMapbox";
import { get } from "svelte/store";
import { onMount } from "svelte";
let esriUrlInput: string = "";
let maplibreUrlInput: string = "";
Expand Down Expand Up @@ -79,16 +76,6 @@
esriEditorContent = maplibreEditorContent;
esriStyleJson.set(esriEditorContent);
}
// onMount(() => {
// convertEsriToMaplibreStyle();
// convertMaplibreToEsriStyle();
// });
// $: {
// convertEsriToMaplibreStyle();
// convertMaplibreToEsriStyle();
// }
</script>

<!-- Navbar -->
Expand Down
83 changes: 55 additions & 28 deletions src/esriToMapbox.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
async function fetchEsriStyleJson(baseUrl: string): Promise<any> {
const styleUrl = `${baseUrl}/resources/styles/root.json`;
async function fetchEsriStyleJson(baseUrl: string, token: string | null): Promise<any> {
const styleUrl = `${baseUrl}/resources/styles/root.json${token ? `?token=${token}` : ''}`;
const response = await fetch(styleUrl);
if (!response.ok) {
throw new Error(`Failed to fetch Esri style JSON: ${response.statusText}`);
}
return response.json();
}


function resolveUrlPath(url: string): string {
const urlObj = new URL(url);
const pathSegments = urlObj.pathname.split('/').filter(segment => segment !== '');
Expand All @@ -27,14 +28,27 @@ function resolveUrlPath(url: string): string {
return decodeURI(urlObj.toString());
}

function appendTokenToUrl(url: string, token: string | null): string {
if (token) {
const urlObj = new URL(url);
urlObj.searchParams.append('token', token);
return urlObj.toString();
}
return url;
}

async function constructMapboxStyleFromEsri(baseUrl: string, esriStyleJson?: any): Promise<any> {
// Normalize the base URL to ensure it does not end with a slash
const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
// Extract the token from the base URL
const urlObj = new URL(baseUrl);
const token = urlObj.searchParams.get('token');

// Remove the token from the base URL
urlObj.searchParams.delete('token');
const normalizedBaseUrl = urlObj.toString().replace(/\/$/, '');

// Fetch the Esri style JSON if it's not provided
if (!esriStyleJson) {
esriStyleJson = await fetchEsriStyleJson(normalizedBaseUrl);
esriStyleJson = await fetchEsriStyleJson(normalizedBaseUrl, token);
}

// Clone the Esri style JSON to avoid mutating the original object
Expand All @@ -48,7 +62,7 @@ async function constructMapboxStyleFromEsri(baseUrl: string, esriStyleJson?: any
// Preserve all source properties
const updatedSource = { ...source };
// Update tiles URL, remove 'url' property
updatedSource.tiles = [`${normalizedBaseUrl}/tile/{z}/{y}/{x}.pbf`];
updatedSource.tiles = [`${normalizedBaseUrl}/tile/{z}/{y}/{x}.pbf${token ? `?token=${token}` : ''}`];
delete updatedSource.url;

// Update the source with the new properties
Expand All @@ -59,81 +73,94 @@ async function constructMapboxStyleFromEsri(baseUrl: string, esriStyleJson?: any

// Correct the sprite and glyphs paths
if (mapboxStyle.sprite) {
mapboxStyle.sprite = `${normalizedBaseUrl}/resources/${mapboxStyle.sprite.replace('../', '')}`;
mapboxStyle.sprite = appendTokenToUrl(`${normalizedBaseUrl}/resources/${mapboxStyle.sprite.replace('../', '')}`, token);
}
if (mapboxStyle.glyphs) {
mapboxStyle.glyphs = `${normalizedBaseUrl}/resources/${mapboxStyle.glyphs.replace('../', '')}`;
mapboxStyle.glyphs = appendTokenToUrl(`${normalizedBaseUrl}/resources/${mapboxStyle.glyphs.replace('../', '')}`, token);
}

return mapboxStyle;
}


async function resolveEsriRelativePaths(baseUrl: string, esriStyleJson?: any): Promise<any> {
// Normalize the base URL to ensure it does not end with a slash
const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
// Extract the token from the base URL
const urlObj = new URL(baseUrl);
const token = urlObj.searchParams.get('token');

// Remove the token from the base URL
urlObj.searchParams.delete('token');
const normalizedBaseUrl = urlObj.toString().replace(/\/$/, '');

// Fetch the Esri style JSON if it's not provided
if (!esriStyleJson) {
esriStyleJson = await fetchEsriStyleJson(normalizedBaseUrl);
esriStyleJson = await fetchEsriStyleJson(normalizedBaseUrl, token);
}

// Resolve the sprite path
if (esriStyleJson.sprite) {
esriStyleJson.sprite = `${normalizedBaseUrl}/resources/${esriStyleJson.sprite.replace('../', '')}`;
esriStyleJson.sprite = decodeURI(appendTokenToUrl(`${normalizedBaseUrl}/resources/${esriStyleJson.sprite.replace('../', '')}`, token));
}


// Resolve the glyphs path
if (esriStyleJson.glyphs) {
esriStyleJson.glyphs = `${normalizedBaseUrl}/resources/${esriStyleJson.glyphs.replace('../', '')}`;
esriStyleJson.glyphs = decodeURI(appendTokenToUrl(`${normalizedBaseUrl}/resources/${esriStyleJson.glyphs.replace('../', '')}`, token));
}

// Resolve the source URL
if (esriStyleJson.sources) {
for (const sourceKey in esriStyleJson.sources) {
const source = esriStyleJson.sources[sourceKey];
if (source.type === 'vector' && source.url) {
source.url = `${normalizedBaseUrl}/${source.url.replace('../../', '')}`;
source.url = decodeURI(appendTokenToUrl(`${normalizedBaseUrl}/${source.url.replace('../../', '')}`, token));
}
}
}

return esriStyleJson;
}


async function constructMapboxStyleFromEsriAbsolute(esriStyleJson?: any): Promise<any> {
// Normalize the base URL to ensure it does not end with a slash
// Fetch the Esri style JSON if it's not provided
if (!esriStyleJson) {
// error
throw new Error(`Failed to fetch Esri style JSON`);
}

// Extract the token from the sprite URL
const spriteUrlObj = new URL(esriStyleJson.sprite);
const token = spriteUrlObj.searchParams.get('token');

// Resolve paths in 'sprite' and 'glyphs'
if (esriStyleJson.sprite) {
esriStyleJson.sprite = resolveUrlPath(esriStyleJson.sprite);
esriStyleJson.sprite = decodeURI(resolveUrlPath(appendTokenToUrl(esriStyleJson.sprite, token)));
}
if (esriStyleJson.glyphs) {
esriStyleJson.glyphs = resolveUrlPath(esriStyleJson.glyphs);
esriStyleJson.glyphs = decodeURI(resolveUrlPath(appendTokenToUrl(esriStyleJson.glyphs, token)));
}

// Adjust the 'sources' to use the correct tile URL pattern while preserving other properties
if (esriStyleJson.sources) {
for (const sourceKey in esriStyleJson.sources) {
const source = esriStyleJson.sources[sourceKey];
if (source.type === 'vector' && source.url) {
// Resolve path in source URL
const resolvedSourceUrl = resolveUrlPath(source.url);
const resolvedSourceUrl = decodeURI(resolveUrlPath(source.url));
const baseTileUrl = resolvedSourceUrl.endsWith('/') ? resolvedSourceUrl : `${resolvedSourceUrl}/`;

// Preserve all source properties
const updatedSource = { ...source };
// Remove the token query parameter from the base tile URL
const baseTileUrlObj = new URL(baseTileUrl);
const token = baseTileUrlObj.searchParams.get('token');
baseTileUrlObj.searchParams.delete('token');
let baseUrlWithoutToken = baseTileUrlObj.toString();

// Ensure the resolved URL ends with a slash before appending 'tile/{z}/{y}/{x}.pbf'
const baseTileUrl = resolvedSourceUrl.endsWith('/') ? resolvedSourceUrl : `${resolvedSourceUrl}/`;
updatedSource.tiles = [`${baseTileUrl}tile/{z}/{y}/{x}.pbf`];
// Add a trailing slash if missing
if (!baseUrlWithoutToken.endsWith('/')) {
baseUrlWithoutToken += '/';
}

const updatedSource = { ...source };
updatedSource.tiles = [`${baseUrlWithoutToken}tile/{z}/{y}/{x}.pbf${token ? `?token=${token}` : ''}`];
delete updatedSource.url;

// Update the source with the new properties
esriStyleJson.sources[sourceKey] = updatedSource;
}
}
Expand Down

0 comments on commit 95be77c

Please sign in to comment.