diff --git a/docs/cli.md b/docs/cli.md index 410d9a1..f5aa7f3 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -103,6 +103,37 @@ Descent: 8 m Returns routed distance, estimated travel time, and elevation change. Does not return the path geometry. +### `mv-query` + +Return all map markers that match a Map View query expression. Supports the full Map View query language — the same syntax used in the map's filter bar. + +```bash +obsidian mv-query query="tag:#hiking" +``` + +The `query` parameter is optional. Omitting it returns every marker in the vault. + +Example output: + +``` +1. Sentier des Crêtes [48.01230, 7.07845] (Hikes/Vosges.md) +2. Lac Blanc [47.99501, 7.05312] (Hikes/Vosges.md) +3. Cascade du Tennbach [48.00812, 7.06631] (Hikes/Alsace.md) +``` + +Each line contains the marker's display name, coordinates, and the path of the source note. + +**Query syntax examples:** + +| Query | Meaning | +| ------------------------------ | ----------------------------------------------- | +| `tag:#hiking` | Markers tagged `#hiking` | +| `path:France` | Markers from notes whose path contains "France" | +| `tag:#cafe AND path:Paris` | Cafés in Paris notes | +| `name:Louvre` | Markers whose display name contains "Louvre" | +| `tag:#restaurant OR tag:#cafe` | Restaurants or cafés | +| _(empty)_ | All markers | + ### `mv-focus-note` Focus a note in Map View, filtering the map to show only its locations. diff --git a/skills/map-view/SKILL.md b/skills/map-view/SKILL.md index 07cf99e..27aa360 100644 --- a/skills/map-view/SKILL.md +++ b/skills/map-view/SKILL.md @@ -1,6 +1,6 @@ --- name: map-view -description: Look up and record geolocations in Obsidian notes using the Map View plugin. Use as part of helping the user to plan a trip, research places to visit, record a location in a note, or whenever the content being added to a note includes named real-world places. Every time the user asks you add to a note something that is a location, consider using this skill so it will be logged as a geolocation. Another usage is to measure distances, either aerial or by routing (foot/driving/cycling etc), e.g. for researching for places that are nearby a location, within a walking distance from it, etc. +description: Look up and record geolocations in Obsidian notes using the Map View plugin. Use as part of helping the user to plan a trip, research places to visit, record a location in a note, or whenever the content being added to a note includes named real-world places. Every time the user asks you add to a note something that is a location, consider using this skill so it will be logged as a geolocation. Also use when the user refers to a known place ("my home", "my office", "my hotel") — query their vault to find its coordinates, then use those for distance or routing calculations. Another usage is to measure distances, either aerial or by routing (foot/driving/cycling etc), e.g. for researching for places that are nearby a location, within a walking distance from it, within a driving distance, etc. --- # Map View @@ -40,6 +40,21 @@ Opens Map View filtered to show only the locations in that note. Use after addin Consider actively asking the user if he wants to see the results after adding geolocations. +```bash +obsidian mv-query query="" +``` + +Returns all map markers that match a Map View query. The `query` parameter is optional — omitting it returns every marker in the vault. Supports the full Map View query language: `tag:`, `path:`, `name:`, `linkedto:`, `linkedfrom:`, `AND`, `OR`, `NOT`, and more. Each result line contains: display name, coordinates, and source note path. + +Example output: + +``` +1. Eiffel Tower [48.85837, 2.29450] (Places/Paris.md) +2. Louvre Museum [48.86013, 2.33552] (Places/Paris.md) +``` + +Use this command to query the user's vault for known locations. When the user refers to a place they have recorded ("my home", "my office", "the hotel from last week"), search the vault with `mv-query` to retrieve its coordinates — then feed those into `mv-calc-distance` or `mv-calc-route`. Also useful to find existing locations before adding new ones, or to check what's already in a note. The command initializes the layer cache automatically — no map needs to be open. + ```bash obsidian mv-calc-distance from="lat,lng" to="lat,lng" ``` @@ -88,6 +103,24 @@ User asks: _"Is the hotel within 20 minutes' drive of the conference center?"_ obsidian mv-calc-route from="48.8584,2.2945" to="48.8738,2.2950" profile="car" ``` +**Example: distance from existing vault markers** + +User asks: _"Which restaurants in my vault are within a 15-minute walk of my hotel?"_ + +1. Find the hotel's coordinates using `mv-query`: + +```bash +obsidian mv-query query="name:Grand Hotel" +``` + +2. Get all restaurant markers: + +```bash +obsidian mv-query query="tag:#restaurant" +``` + +3. Check walking time for each candidate with `mv-calc-route` (profile `foot`). Only keep results ≤ 15 min. + **Example: quick aerial check before routing** If you need to filter a long list of candidates, first use `mv-calc-distance` to discard obviously far-away results (e.g. > 5 km aerial for a 10-min walk), then run `mv-calc-route` only for the remaining candidates to avoid unnecessary API calls. diff --git a/src/cli.ts b/src/cli.ts index b3e65f5..8e06afe 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -2,9 +2,16 @@ import { App, Plugin, TFile } from 'obsidian'; import * as leaflet from 'leaflet'; import { GeoSearcher } from 'src/geosearch'; import { calcRoute } from 'src/routing'; +import { Query } from 'src/query'; +import { FileMarker } from 'src/fileMarker'; +import type { LayerCache } from 'src/layerCache'; import type { PluginSettings } from 'src/settings'; -type MapViewPlugin = Plugin & { focusNoteInMap: (file: TFile) => void }; +type MapViewPlugin = Plugin & { + focusNoteInMap: (file: TFile) => void; + waitForInitialization: () => Promise; + layerCache: LayerCache | null; +}; export function registerCliHandlers( plugin: MapViewPlugin, @@ -150,6 +157,44 @@ export function registerCliHandlers( }, ); + plugin.registerCliHandler( + 'mv-query', + 'Return all map markers matching a Map View query (full query language supported). An empty query returns all markers. Waits up to 10 seconds for the layer cache to initialize if needed.', + { + query: { + value: '', + description: + 'Map View query expression, e.g. tag:#hiking, path:trips, tag:#cafe AND path:Paris. Leave empty to return all markers.', + required: false, + }, + }, + async (params: { query?: string }) => { + // Support positional arg: `mv-query tag:#foo` arrives as {"tag:#foo": "true"} + const queryStr = + params.query ?? + Object.keys(params).find((k) => k !== 'query') ?? + ''; + await plugin.waitForInitialization(); + const query = new Query(app, queryStr); + const results: string[] = []; + let index = 1; + for (const layer of plugin.layerCache.layers) { + if (!(layer instanceof FileMarker)) continue; + if (!query.testLayer(layer)) continue; + const coords = `[${layer.location.lat.toFixed(5)}, ${layer.location.lng.toFixed(5)}]`; + results.push( + `${index}. ${layer.name} ${coords} (${layer.file.path})`, + ); + index++; + } + if (!results.length) + return queryStr + ? `No markers matched: ${queryStr}` + : 'No markers found in the vault.'; + return results.join('\n'); + }, + ); + function parseLatLng(str: string): leaflet.LatLng | null { const cleaned = str.replace(/[\[\]\s]/g, ''); const m = cleaned.match(/^(-?\d+\.?\d*),(-?\d+\.?\d*)$/); diff --git a/src/query.ts b/src/query.ts index 0dde016..cda4c7a 100644 --- a/src/query.ts +++ b/src/query.ts @@ -185,7 +185,7 @@ export class Query { ); } else throw new Error( - `Unsupported query format for Map View display rule: "${value}"`, + `Unsupported query format for Map View: "${value}"`, ); } }