diff --git a/src/geosearch.ts b/src/geosearch.ts index 393063f..ac746c9 100644 --- a/src/geosearch.ts +++ b/src/geosearch.ts @@ -1,12 +1,12 @@ -import { request, App } from 'obsidian'; +import { App } from 'obsidian'; import * as geosearch from 'leaflet-geosearch'; import * as leaflet from 'leaflet'; -import * as querystring from 'query-string'; import { PluginSettings } from 'src/settings'; import { UrlConvertor } from 'src/urlConvertor'; import { FileMarker } from 'src/markers'; import * as consts from 'src/consts'; +import { GooglePlacesAPI } from './geosearchGoogleApi'; /** * A generic result of a geosearch @@ -19,10 +19,18 @@ export class GeoSearchResult { existingMarker?: FileMarker; } +type searchProviderParms = { + query: string; + location?: string; +}; + +export type GeoSearcherProvider = + | geosearch.OpenStreetMapProvider + | geosearch.GoogleProvider + | GooglePlacesAPI; + export class GeoSearcher { - private searchProvider: - | geosearch.OpenStreetMapProvider - | geosearch.GoogleProvider = null; + public searchProvider: GeoSearcherProvider; private settings: PluginSettings; private urlConvertor: UrlConvertor; @@ -31,7 +39,9 @@ export class GeoSearcher { this.urlConvertor = new UrlConvertor(app, settings); if (settings.searchProvider == 'osm') this.searchProvider = new geosearch.OpenStreetMapProvider(); - else if (settings.searchProvider == 'google') { + else if (this.usingGooglePlacesSearch) { + this.searchProvider = new GooglePlacesAPI(settings); + } else if (settings.searchProvider == 'google') { this.searchProvider = new geosearch.GoogleProvider({ params: { key: settings.geocodingApiKey }, }); @@ -59,97 +69,42 @@ export class GeoSearcher { }); } - // Google Place results - if ( - this.settings.searchProvider == 'google' && - this.settings.useGooglePlaces && - this.settings.geocodingApiKey - ) { - try { - const placesResults = await googlePlacesSearch( - query, - this.settings, - searchArea?.getCenter() - ); - for (const result of placesResults) - results.push({ - name: result.name, - location: result.location, - resultType: 'searchResult', - }); - } catch (e) { - console.log( - 'Map View: Google Places search failed: ', - e.message - ); - } - } else { - const areaSW = searchArea?.getSouthWest() || null; - const areaNE = searchArea?.getNorthEast() || null; - let searchResults = await this.searchProvider.search({ - query: query, - }); + let params: searchProviderParms = { + query: query, + }; + + if (this.usingGooglePlacesSearch() && searchArea) { + const centerOfSearch = searchArea?.getCenter(); + params.location = `${centerOfSearch.lat},${centerOfSearch.lng}`; + } + let searchResults = await this.searchProvider.search(params); + + if (!this.usingGooglePlacesSearch()) { searchResults = searchResults.slice( 0, consts.MAX_EXTERNAL_SEARCH_SUGGESTIONS ); - results = results.concat( - searchResults.map( - (result) => - ({ - name: result.label, - location: new leaflet.LatLng(result.y, result.x), - resultType: 'searchResult', - } as GeoSearchResult) - ) - ); } + results = results.concat( + searchResults.map( + (result) => + ({ + name: result.label, + location: new leaflet.LatLng(result.y, result.x), + resultType: 'searchResult', + } as GeoSearchResult) + ) + ); + return results; } -} -export async function googlePlacesSearch( - query: string, - settings: PluginSettings, - centerOfSearch: leaflet.LatLng | null -): Promise { - if (settings.searchProvider != 'google' || !settings.useGooglePlaces) - return []; - const googleApiKey = settings.geocodingApiKey; - const params = { - query: query, - key: googleApiKey, - }; - if (centerOfSearch) - (params as any)[ - 'location' - ] = `${centerOfSearch.lat},${centerOfSearch.lng}`; - const googleUrl = - 'https://maps.googleapis.com/maps/api/place/textsearch/json?' + - querystring.stringify(params); - const googleContent = await request({ url: googleUrl }); - const jsonContent = JSON.parse(googleContent) as any; - let results: GeoSearchResult[] = []; - if ( - jsonContent && - 'results' in jsonContent && - jsonContent?.results.length > 0 - ) { - for (const result of jsonContent.results) { - const location = result.geometry?.location; - if (location && location.lat && location.lng) { - const geolocation = new leaflet.LatLng( - location.lat, - location.lng - ); - results.push({ - name: `${result?.name} (${result?.formatted_address})`, - location: geolocation, - resultType: 'searchResult', - } as GeoSearchResult); - } - } + usingGooglePlacesSearch(): boolean { + return ( + this.settings.searchProvider == 'google' && + this.settings.useGooglePlaces && + this.settings.geocodingApiKey !== null + ); } - return results; } diff --git a/src/geosearchGoogleApi.ts b/src/geosearchGoogleApi.ts new file mode 100644 index 0000000..c91ea72 --- /dev/null +++ b/src/geosearchGoogleApi.ts @@ -0,0 +1,62 @@ +import { PluginSettings } from './settings'; +import * as leaflet from 'leaflet'; +import { request } from 'obsidian'; +import * as querystring from 'query-string'; +import { GeoSearchResult } from './geosearch'; +import { SearchResult } from 'leaflet-geosearch/dist/providers/provider'; + +export type GooglePlacesAPIQuery = { [key: string]: string }; + +export class GooglePlacesAPI { + private googleApiKey: string; + + constructor(settings: PluginSettings) { + this.googleApiKey = settings.geocodingApiKey; + } + + async googlePlacesRequest( + query: GooglePlacesAPIQuery, + scope: string + ): Promise<{}> { + query.key = this.googleApiKey; + const googleUrl = `https://maps.googleapis.com/maps/api/place/${scope}/json?${querystring.stringify( + query + )}`; + const googleContent = await request({ url: googleUrl }); + return JSON.parse(googleContent) as any; + } + + async search(query: GooglePlacesAPIQuery): Promise { + let jsonContent: any = await this.googlePlacesSearch(query); + + let results: SearchResult[] = []; + if ( + jsonContent && + 'results' in jsonContent && + jsonContent?.results.length > 0 + ) { + for (const result of jsonContent.results) { + const location = result.geometry?.location; + if (location && location.lat && location.lng) { + results.push({ + label: `${result?.name} (${result?.formatted_address})`, + y: location.lat, + x: location.lng, + } as any); + } + } + } + return results; + } + + async googlePlacesSearch(query: GooglePlacesAPIQuery): Promise<{}> { + return await this.googlePlacesRequest(query, 'textsearch'); + } + + async googlePlacesDetailsSearch(placeId: string): Promise<{}> { + const params = { + place_id: placeId, + }; + return this.googlePlacesRequest(params, 'details'); + } +} diff --git a/src/main.ts b/src/main.ts index 379d003..c25fe1e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -40,11 +40,13 @@ import { SettingsTab } from 'src/settingsTab'; import { LocationSearchDialog } from 'src/locationSearchDialog'; import { TagSuggest } from 'src/tagSuggest'; import * as utils from 'src/utils'; +import { MapViewAPI } from './mapViewAPI'; export default class MapViewPlugin extends Plugin { settings: PluginSettings; public highestVersionSeen: number = 0; public iconCache: IconCache; + public mapViewAPI: MapViewAPI; private suggestor: LocationSuggest; private tagSuggestor: TagSuggest; private urlConvertor: UrlConvertor; @@ -138,6 +140,7 @@ export default class MapViewPlugin extends Plugin { ); this.suggestor = new LocationSuggest(this.app, this.settings); + this.mapViewAPI = new MapViewAPI(this.app, this.settings); this.tagSuggestor = new TagSuggest(this.app, this.settings); this.urlConvertor = new UrlConvertor(this.app, this.settings); diff --git a/src/mapviewAPI.ts b/src/mapviewAPI.ts new file mode 100644 index 0000000..6512133 --- /dev/null +++ b/src/mapviewAPI.ts @@ -0,0 +1,13 @@ +import { App } from 'obsidian'; +import { GeoSearcher, GeoSearcherProvider } from './geosearch'; +import { PluginSettings } from 'src/settings'; + +export class MapViewAPI { + public searchProvider: GeoSearcherProvider; + public searcher: GeoSearcher; + + constructor(app: App, settings: PluginSettings) { + this.searcher = new GeoSearcher(app, settings); + this.searchProvider = this.searcher.searchProvider; + } +}