Skip to content

Commit

Permalink
feat: first PoC of interactive map support
Browse files Browse the repository at this point in the history
refs: #31 #30
  • Loading branch information
aaronczichon committed Oct 28, 2024
1 parent c64e09e commit 92bbd57
Show file tree
Hide file tree
Showing 4 changed files with 797 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import mapboxgl from 'mapbox-gl';
import { Plugin } from 'obsidian';
import { changeApiTokenCommand } from './commands/api-token.command';
import { changeDefaultMarkerUrlCommand } from './commands/custom-marker.command';
Expand All @@ -9,6 +10,7 @@ import { addNewLocationFromClipboard } from './commands/new-from-clipboard.comma
import { toggleReverseCoordinates } from './commands/toggle-reverse.command';
import { checkVersionUpdate } from './functions/version-hint.func';
import { processLocationCodeBlock } from './processors/process-code.func';
import { processInteractiveLocationCodeBlock } from './processors/process-interactive-code.func';
import { processMultiLocationCodeBlock } from './processors/process-multi-locations.func';
import { LocationSettingTab } from './settings/plugin-settings.tab';
import { DEFAULT_SETTINGS, LocationPluginSettings } from './settings/plugin-settings.types';
Expand All @@ -31,6 +33,8 @@ export default class MapboxPlugin extends Plugin {
changeDefaultMarkerUrlCommand(this);
toggleReverseCoordinates(this);

mapboxgl.accessToken = this.settings.mapboxToken;

// Register the processors for the given code blocks.
// Code blocks are used in this plugin to render the maps.
this.registerMarkdownCodeBlockProcessor('location', (source: string, el: HTMLElement) =>
Expand All @@ -39,6 +43,11 @@ export default class MapboxPlugin extends Plugin {
this.registerMarkdownCodeBlockProcessor('multi-location', (source: string, el: HTMLElement) =>
processMultiLocationCodeBlock(source, el, this.settings),
);
this.registerMarkdownCodeBlockProcessor(
'interactive-location',
(source: string, el: HTMLElement) =>
processInteractiveLocationCodeBlock(source, el, this.settings),
);

// Adding the UI settings tab to the Obsidian preferences.
this.addSettingTab(new LocationSettingTab(this.app, this));
Expand Down
115 changes: 115 additions & 0 deletions src/processors/process-interactive-code.func.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import mapboxgl from 'mapbox-gl';
import { Notice } from 'obsidian';
import {
findLatitude,
findLatitudeAndLongitude,
findLongitude,
findMapStyle,
findMapZoom,
findMarkerIcon,
findMarkerUrl,
findSearchQuery,
} from '../exctractors/row-extractor.func';
import { hasMapboxToken } from '../functions/map.func';
import { processLocationSearch } from '../functions/process-location-search.func';
import {
LocationBlockConfiguration,
LocationPluginSettings,
} from '../settings/plugin-settings.types';

export const processInteractiveLocationCodeBlock = async (
source: string,
el: HTMLElement,
settings: LocationPluginSettings,
) => {
try {
const extractedData = processCodeBlock(source);

// If no Mapbox token is set, we don't need to process the code block.
if (!hasMapboxToken(settings)) return;

if (!extractedData.searchQuery && (!extractedData.latitude || !extractedData.longitude)) {
new Notice(
'Error processing location code block. Either Longitude and Latitude are required together, or a search term.',
5000,
);
return;
}

// check if being run as a search then retrieve and render the image
let address = '';
if (extractedData.searchQuery) {
const [latitude, longitude, fullAddress] = await processLocationSearch(
extractedData.searchQuery,
settings.mapboxToken,
);
extractedData.latitude = latitude.toString();
extractedData.longitude = longitude.toString();
address = fullAddress;
}
// if we need to flip the order of the coordinates
// then we need to do it before rendering the image
if (settings.reverseOrder) {
const temp = extractedData.latitude;
extractedData.latitude = extractedData.longitude;
extractedData.longitude = temp;
}

// create new element where we append the map
const divElement = document.createElement('div');
divElement.classList.add('mapbox-image');
divElement.classList.add('mapbox-interactive-map');
const id = `map-${Math.random().toString(36).substr(2, 9)}`;
divElement.id = id;
el.appendChild(divElement);
setTimeout(() => {
const lng = parseFloat(extractedData.longitude!);
const lat = parseFloat(extractedData.latitude!);
if (isNaN(lng) || isNaN(lat)) return;
const map = new mapboxgl.Map({
container: id, // container ID
style: `mapbox://styles/mapbox/${extractedData.style || settings.mapStyle}`, // style URL
center: [lng, lat], // starting position [lng, lat]
zoom: parseInt(extractedData.zoom || settings.mapZoom), // starting zoom
});
map.on('load', () => {
new mapboxgl.Marker({
color: `#${settings.markerColor}`,
})
.setLngLat([lng, lat])
.addTo(map);
});
}, 1500);
} catch (e) {
new Notice(
'Error processing location code block. Please check syntax or missing settings.',
5000,
);
}
};

const processCodeBlock = (source: string) => {
const rows = source.split('\n');

let latitude = findLatitude(rows);
let longitude = findLongitude(rows);
const markerUrl = findMarkerUrl(rows);
const makiIcon = findMarkerIcon(rows);
const mapStyle = findMapStyle(rows);
const mapZoom = findMapZoom(rows);
const searchQuery = findSearchQuery(rows);
const latLong = findLatitudeAndLongitude(rows);
latitude = latLong ? latLong[0] : latitude;
longitude = latLong ? latLong[1] : longitude;

const config: LocationBlockConfiguration = {
latitude,
longitude,
markerUrl,
style: mapStyle,
zoom: mapZoom,
searchQuery,
makiIcon,
};
return config;
};
Loading

0 comments on commit 92bbd57

Please sign in to comment.