Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Independent search service&reverse geocode #153

Merged
merged 3 commits into from
May 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/lib/search/search-bar/search-bar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Subscription } from 'rxjs/Subscription';
import { debounceTime } from 'rxjs/operators/debounceTime';
import { distinctUntilChanged } from 'rxjs/operators/distinctUntilChanged';

import { FeatureService, SourceFeatureType, FeatureType } from '../../feature';
import { FeatureService, SourceFeatureType, FeatureType, Feature } from '../../feature';
import { SearchService } from '../shared';

@Component({
Expand Down Expand Up @@ -139,9 +139,14 @@ export class SearchBarComponent implements OnInit, OnDestroy {
}

private handleTermChanged(term: string) {
if (term !== undefined) {
if (term !== undefined || term !== '') {
this.featureService.clear()
this.search.emit(term);
this.searchService.search(term);
const r = this.searchService.search(term)
if (r) {
r.map(res => res.subscribe(
(features) => (this.featureService.updateFeatures(features as Feature[], undefined))))
}
}
}
}
5 changes: 5 additions & 0 deletions src/lib/search/search-sources/datasource-search-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ export class DataSourceSearchSource extends SearchSource {
.map(res => this.extractData(res));
}

locate(coordinate?: [number, number]): Observable<Feature[] | Message[]> {
// It should be a good idea to locate layers by coordinates ?
return
}

private extractData(response): Feature[] {
return response.items.map(
(res) => this.formatResult(res)
Expand Down
72 changes: 57 additions & 15 deletions src/lib/search/search-sources/icherche-search-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,58 @@ import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

import { ConfigService, Message } from '../../core';
import { Feature, FeatureType, FeatureFormat, SourceFeatureType} from '../../feature';
import {
Feature,
FeatureType,
FeatureFormat,
SourceFeatureType
} from '../../feature';

import { SearchSource } from './search-source';
import { SearchSourceOptions } from './search-source.interface';


@Injectable()
export class IChercheSearchSource extends SearchSource {

get enabled(): boolean { return this.options.enabled !== false; }
set enabled(value: boolean) { this.options.enabled = value; }
get enabled(): boolean {
return this.options.enabled !== false;
}
set enabled(value: boolean) {
this.options.enabled = value;
}

static _name: string = 'ICherche Québec';

private searchUrl: string = 'https://geoegl.msp.gouv.qc.ca/icherche/geocode';
private locateUrl: string = 'https://geoegl.msp.gouv.qc.ca/icherche/xy';
private options: SearchSourceOptions;

constructor(private http: HttpClient,
private config: ConfigService) {
constructor(private http: HttpClient, private config: ConfigService) {
super();

this.options = this.config.getConfig('searchSources.icherche') || {};
this.searchUrl = this.options.url || this.searchUrl;
this.locateUrl = this.options.locateUrl || this.locateUrl;
}

getName(): string {
return IChercheSearchSource._name;
}

search(term?: string): Observable<Feature[] | Message[]> {
search(term?: string): Observable<Feature[] | Message[]> {
const searchParams = this.getSearchParams(term);

return this.http
.get(this.searchUrl, {params: searchParams})
.get(this.searchUrl, { params: searchParams })
.map(res => this.extractData(res));
}

locate(
coordinate: [number, number],
zoom: number
): Observable<Feature[] | Message[]> {
const locateParams = this.getLocateParams(coordinate, zoom);
return this.http
.get(this.locateUrl, { params: locateParams })
.map(res => this.extractData(res));
}

Expand All @@ -46,8 +64,9 @@ export class IChercheSearchSource extends SearchSource {

private getSearchParams(term: string): HttpParams {
const limit = this.options.limit === undefined ? 5 : this.options.limit;
const type = this.options.type ||
'adresse,code_postal,route,municipalite,mrc,region_administrative'
const type =
this.options.type ||
'adresse,code_postal,route,municipalite,mrc,region_administrative';

return new HttpParams({
fromObject: {
Expand All @@ -59,10 +78,34 @@ export class IChercheSearchSource extends SearchSource {
});
}

private getLocateParams(
coordinate: [number, number],
currentZoom: number
): HttpParams {
let distance = 100;
const type = this.options.type || 'adresse';
if (currentZoom >= 16) {
distance = 30;
} else if (currentZoom < 8) {
distance = 500;
}
return new HttpParams({
fromObject: {
loc: coordinate.join(','),
type: type,
distance: String(distance),
geometries: 'geom'
}
});
}

private formatResult(result: any): Feature {
const properties = Object.assign({
type: result.doc_type
}, result.properties);
const properties = Object.assign(
{
type: result.doc_type
},
result.properties
);
delete properties['@timestamp'];
delete properties['@version'];
delete properties.recherche;
Expand All @@ -85,5 +128,4 @@ export class IChercheSearchSource extends SearchSource {
extent: result.bbox
};
}

}
55 changes: 42 additions & 13 deletions src/lib/search/search-sources/nominatim-search-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,62 @@ import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

import { ConfigService, Message } from '../../core';
import { Feature, FeatureType, FeatureFormat, SourceFeatureType} from '../../feature';
import {
Feature,
FeatureType,
FeatureFormat,
SourceFeatureType
} from '../../feature';

import { SearchSource } from './search-source';
import { SearchSourceOptions } from './search-source.interface';


@Injectable()
export class NominatimSearchSource extends SearchSource {

get enabled(): boolean { return this.options.enabled !== false; }
set enabled(value: boolean) { this.options.enabled = value; }
get enabled(): boolean {
return this.options.enabled !== false;
}
set enabled(value: boolean) {
this.options.enabled = value;
}

static _name: string = 'Nominatim (OSM)';
static sortIndex: number = 10;

private searchUrl: string = 'https://nominatim.openstreetmap.org/search';
private locateUrl: string = 'https://nominatim.openstreetmap.org/reverse';
private options: SearchSourceOptions;

constructor(private http: HttpClient,
private config: ConfigService) {
constructor(private http: HttpClient, private config: ConfigService) {
super();

this.options = this.config.getConfig('searchSources.nominatim') || {};
this.searchUrl = this.options.url || this.searchUrl;
this.locateUrl = this.options.locateUrl || this.locateUrl;
}

getName(): string {
return NominatimSearchSource._name;
}

search(term?: string): Observable<Feature[] | Message[]> {
search(term?: string): Observable<Feature[] | Message[]> {
const searchParams = this.getSearchParams(term);

return this.http
.get(this.searchUrl, { params: searchParams })
.map(res => this.extractData(res));
}

locate(
coordinate: [number, number],
zoom: number
): Observable<Feature[] | Message[]> {
const locateParams = this.getLocateParams(coordinate, zoom);
return this.http
.get(this.locateUrl, { params: locateParams })
.map(res => this.extractData([res]));
}

private extractData(response): Feature[] {
return response.map(this.formatResult);
}
Expand All @@ -57,6 +75,21 @@ export class NominatimSearchSource extends SearchSource {
});
}

private getLocateParams(
coordinate: [number, number],
zoom: number
): HttpParams {
return new HttpParams({
fromObject: {
lat: String(coordinate[1]),
lon: String(coordinate[0]),
format: 'json',
zoom: String(18),
polygon_geojson: String(1)
}
});
}

private formatResult(result: any): Feature {
return {
id: result.place_id,
Expand All @@ -77,10 +110,7 @@ export class NominatimSearchSource extends SearchSource {
},
geometry: {
type: 'Point',
coordinates: [
parseFloat(result.lon),
parseFloat(result.lat)
]
coordinates: [parseFloat(result.lon), parseFloat(result.lat)]
},
extent: [
parseFloat(result.boundingbox[2]),
Expand All @@ -90,5 +120,4 @@ export class NominatimSearchSource extends SearchSource {
]
};
}

}
2 changes: 2 additions & 0 deletions src/lib/search/search-sources/search-source.interface.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
export interface SearchSourceOptions {
url?: string;
locateUrl?: string;
limit?: number;
enabled?: boolean;
type?: string;
distance?: number;
}

export interface SearchSourcesOptions {
Expand Down
2 changes: 2 additions & 0 deletions src/lib/search/search-sources/search-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ export abstract class SearchSource {

abstract search(term?: string): Observable<Feature[] | Message[]>

abstract locate(coordinate: [number, number], zoom?: number): Observable<Feature[] | Message[]>

}
45 changes: 20 additions & 25 deletions src/lib/search/shared/search.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { Observable } from 'rxjs/Observable';

import { MessageService } from '../../core';
import { Message } from '../../core';
import { Feature, FeatureService } from '../../feature';

import { SearchSourceService } from './search-source.service';
Expand All @@ -11,44 +11,39 @@ import { SearchSource } from '../search-sources/search-source';
@Injectable()
export class SearchService {

private subscriptions: Subscription[] = [];

constructor(private searchSourceService: SearchSourceService,
private featureService: FeatureService,
private messageService: MessageService) {
private featureService: FeatureService) {
}

search(term: string) {
locate(coordinates: [number, number], zoom: number = 18): Observable<Feature[] | Message[]>[] {
return this.searchSourceService.sources
.filter((source: SearchSource) => source.enabled)
.map((source: SearchSource) => this.locateSource(source, coordinates, zoom));
}

search(term: string): Observable<Feature[] | Message[]>[] {
if (!term || term === '') {
this.featureService.clear();
return;
}

this.unsubscribe();

this.subscriptions = this.searchSourceService.sources
return this.searchSourceService.sources
.filter((source: SearchSource) => source.enabled)
.map((source: SearchSource) => this.searchSource(source, term));
}

searchSource(source: SearchSource, term?: string) {
const request = source.search(term);

return request.subscribe(
(features: Feature[]) => this.handleFeatures(features, source),
(err) => {
err.error.title = source.getName();
this.messageService.showError(err);
}
);
locateSource(
source: SearchSource,
coordinates: [number, number],
zoom): Observable<Feature[] | Message[]> {
const request = source.locate(coordinates, zoom);
return request
}

private unsubscribe() {
this.subscriptions.forEach((sub: Subscription) => sub.unsubscribe());
searchSource(source: SearchSource, term?: string): Observable<Feature[] | Message[]> {
const request = source.search(term);
return request
}

private handleFeatures(features: Feature[], source: SearchSource) {
const sourcesToKeep = this.searchSourceService.sources.map(s => s.getName());
this.featureService.updateFeatures(features, source.getName(), sourcesToKeep);
}
}