Skip to content

Commit

Permalink
Merge pull request #698 from geonetwork/DH-fix-map-centering
Browse files Browse the repository at this point in the history
Fix(DH): Center bbox extent with different coordinate reference system
  • Loading branch information
Angi-Kinas authored Dec 7, 2023
2 parents 0dd0958 + 320c8b0 commit 6d4aec4
Show file tree
Hide file tree
Showing 16 changed files with 208 additions and 178 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@ import {
ViewChild,
} from '@angular/core'
import { ThemeService } from '@geonetwork-ui/util/shared'
import type { Feature } from 'geojson'
import type { Feature as FeatureType } from 'geojson'
import { asArray, asString } from 'ol/color'
import { isEmpty } from 'ol/extent'
import GeoJSON from 'ol/format/GeoJSON'
import { Geometry } from 'ol/geom'
import TileLayer from 'ol/layer/Tile'
import VectorLayer from 'ol/layer/Vector'
import Map from 'ol/Map'
import Feature from 'ol/Feature'
import OSM from 'ol/source/OSM'
import VectorSource from 'ol/source/Vector'
import { Fill, RegularShape, Stroke, Style } from 'ol/style'
import { StyleLike } from 'ol/style/Style'
import View from 'ol/View'
import { FeatureLike } from 'ol/Feature'

const DEFAULT_PRIMARY_COLOR = '#9a9a9a'
const PADDING = 50
Expand All @@ -41,7 +43,7 @@ export class DataImportValidationMapPanelComponent
@Input() headerLabel = ''
@Input() footerLabel = ''
@Input() footerList = []
@Input() geoJson?: Feature
@Input() geoJson?: FeatureType
@Input() footerValue = ''
@Input() padding = []

Expand All @@ -50,8 +52,8 @@ export class DataImportValidationMapPanelComponent
selectedValue: any

private map: Map
private source: VectorSource<Geometry>
private vectorLayer: VectorLayer<VectorSource<Geometry>>
private source: VectorSource<FeatureLike>
private vectorLayer: VectorLayer<VectorSource<Feature<Geometry>>>
private format: any = new GeoJSON({})

ngOnInit(): void {
Expand Down Expand Up @@ -158,8 +160,8 @@ export class DataImportValidationMapPanelComponent
]
}

private buildVectorLayer(): VectorLayer<VectorSource<Geometry>> {
this.source = new VectorSource({})
private buildVectorLayer(): VectorLayer<VectorSource<Feature<Geometry>>> {
this.source = new VectorSource<Feature<Geometry>>({})
if (this.geoJson) {
this.source.addFeatures(
this.format.readFeatures(this.geoJson, {
Expand All @@ -168,7 +170,7 @@ export class DataImportValidationMapPanelComponent
)
}
return new VectorLayer({
source: this.source,
source: this.source as VectorSource<Feature<Geometry>>,
style: this.getDefaultStyle(),
})
}
Expand Down
11 changes: 7 additions & 4 deletions apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,15 +373,18 @@ describe('dataset pages', () => {
})
})
it('downloads a file on click', () => {
cy.intercept(
'GET',
'https://www.geo2france.fr/geoserver/insee/ows?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=insee%3Arectangles_200m_menage_erbm&OUTPUTFORMAT=csv'
).as('downloadRequest')

cy.get('datahub-record-downloads')
.find('gn-ui-download-item')
.first()
.click()
cy.exec('ls cypress/downloads').then((result) => {
const fileList = result.stdout.split('\n')

const isFileDownloaded = fileList[0]
expect(/\S/.test(isFileDownloaded)).to.be.true
cy.wait('@downloadRequest').then((interception) => {
expect(interception.response.statusCode).to.equal(200)
})
})
it('displays the full list after clicking two times on one filter', () => {
Expand Down
2 changes: 1 addition & 1 deletion jest.preset.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module.exports = {
...nxPreset,
coverageReporters: ['text'],
setupFiles: ['jest-canvas-mock'],
transformIgnorePatterns: ['node_modules/(?!(ol|@mapbox|.*.mjs$))'],
transformIgnorePatterns: ['node_modules/(?!(color-*|ol|@mapbox|.*.mjs$))'],
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
MapManagerService,
} from '@geonetwork-ui/feature/map'
import { FEATURE_COLLECTION_POINT_FIXTURE_4326 } from '@geonetwork-ui/common/fixtures'
import { Map } from 'ol'
import Map from 'ol/Map'
import Feature from 'ol/Feature'
import GeoJSON from 'ol/format/GeoJSON'
import TileLayer from 'ol/layer/Tile'
import VectorLayer from 'ol/layer/Vector'
Expand All @@ -21,6 +22,7 @@ import XYZ from 'ol/source/XYZ'
import { Subject } from 'rxjs'

import { GeoTableViewComponent } from './geo-table-view.component'
import { Geometry } from 'ol/geom'

const vectorLayer = new VectorLayer({
source: new VectorSource({
Expand All @@ -31,7 +33,7 @@ const vectorLayer = new VectorLayer({
dataProjection: 'EPSG:4326',
}
),
}),
}) as VectorSource<Feature<Geometry>>,
})

const mapMock = new Map({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ export class GeoTableViewComponent implements OnInit, AfterViewInit, OnDestroy {

private map: Map
private view: View
private vectorLayer: VectorLayer<VectorSource<Geometry>>
private vectorSource: VectorSource<Geometry>
private vectorLayer: VectorLayer<VectorSource<Feature<Geometry>>>
private vectorSource: VectorSource<Feature<Geometry>>
private features: Feature<Geometry>[]

tableData: TableItemModel[]
Expand Down Expand Up @@ -76,7 +76,7 @@ export class GeoTableViewComponent implements OnInit, AfterViewInit, OnDestroy {
const map = (this.map = this.manager.map)
this.view = map.getView()
this.vectorLayer = this.manager.map.getLayers().item(1) as VectorLayer<
VectorSource<Geometry>
VectorSource<Feature<Geometry>>
>
this.vectorLayer.setStyle(this.styleFn.bind(this))
this.vectorSource = this.vectorLayer.getSource()
Expand Down
7 changes: 7 additions & 0 deletions libs/feature/dataviz/src/test-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ getTestBed().initTestEnvironment(
platformBrowserDynamicTesting(),
{ teardown: { destroyAfterEach: false } }
)

class ResizeObserverMock {
observe = jest.fn()
unobserve = jest.fn()
}

;(window as any).ResizeObserver = ResizeObserverMock
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
DEFAULT_VIEW,
MapContextService,
} from './map-context.service'
import Feature from 'ol/Feature'

const mapStyleServiceMock = {
createDefaultStyle: jest.fn(() => new Style()),
Expand Down Expand Up @@ -319,7 +320,9 @@ describe('MapContextService', () => {
})
it('add one WFS layer from config on top of baselayer', () => {
const layerWFSSource = (
map.getLayers().item(2) as VectorLayer<VectorSource<Geometry>>
map.getLayers().item(2) as VectorLayer<
VectorSource<Feature<Geometry>>
>
).getSource()
expect(layerWFSSource).toBeInstanceOf(VectorSource)
})
Expand Down
4 changes: 3 additions & 1 deletion libs/feature/map/src/lib/map-context/map-context.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import { FeatureCollection } from 'geojson'
import { fromLonLat } from 'ol/proj'
import WMTS from 'ol/source/WMTS'
import { removeSearchParams } from '@geonetwork-ui/util/shared'
import { Geometry } from 'ol/geom'
import Feature from 'ol/Feature'

export const DEFAULT_BASELAYER_CONTEXT: MapContextLayerXyzModel = {
type: MapContextLayerTypeEnum.XYZ,
Expand Down Expand Up @@ -136,7 +138,7 @@ export class MapContextService {
}
const features = this.mapUtils.readFeatureCollection(
geojson as FeatureCollection
)
) as Feature<Geometry>[]
return new VectorLayer({
source: new VectorSource({
features,
Expand Down
34 changes: 27 additions & 7 deletions libs/feature/map/src/lib/utils/map-utils-wms.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { TestBed } from '@angular/core/testing'
import { readFirst } from '@nx/angular/testing'

import { MapUtilsWMSService } from './map-utils-wms.service'
import { fromEPSGCode } from 'ol/proj/proj4'

jest.mock('@camptocamp/ogc-client', () => ({
WmsEndpoint: class {
Expand All @@ -22,6 +23,24 @@ jest.mock('@camptocamp/ogc-client', () => ({
},
}))

jest.mock('ol/proj/proj4', () => {
const fromEPSGCodeMock = jest.fn()
const registerMock = jest.fn()
return {
fromEPSGCode: fromEPSGCodeMock,
register: registerMock,
}
})

jest.mock('ol/proj', () => {
const extent = [1, 2, 3, 4]
const transformExtentMock = jest.fn().mockReturnValue(extent)

return {
transformExtent: transformExtentMock,
}
})

describe('MapUtilsWMSService', () => {
let service: MapUtilsWMSService

Expand Down Expand Up @@ -80,8 +99,8 @@ describe('MapUtilsWMSService', () => {
}
})
it('returns CRS:84 bbox', async () => {
const extent = service.getLonLatBBox(wmsLayerFull)
expect(extent).toEqual(['2.3', '50.6', '2.8', '50.9'])
const extent = await service.getLonLatBBox(wmsLayerFull)
expect(extent).toStrictEqual(['2.3', '50.6', '2.8', '50.9'])
})
})
describe('bbox in EPSG:4326', () => {
Expand All @@ -94,8 +113,8 @@ describe('MapUtilsWMSService', () => {
}
})
it('returns EPSG:4326 bbox', async () => {
const extent = service.getLonLatBBox(wmsLayerFull)
expect(extent).toEqual(['1', '2.6', '3.3', '4.2'])
const extent = await service.getLonLatBBox(wmsLayerFull)
expect(extent).toStrictEqual(['1', '2.6', '3.3', '4.2'])
})
})
describe('no lon lat bbox', () => {
Expand All @@ -106,9 +125,10 @@ describe('MapUtilsWMSService', () => {
},
}
})
it('returns EPSG:4326 bbox', async () => {
const extent = service.getLonLatBBox(wmsLayerFull)
expect(extent).toBeUndefined()
it('transforms to EPSG:4326 bbox', async () => {
const extent = await service.getLonLatBBox(wmsLayerFull)
expect(fromEPSGCode).toHaveBeenCalled()
expect(extent).toStrictEqual([1, 2, 3, 4])
})
})
})
Expand Down
24 changes: 21 additions & 3 deletions libs/feature/map/src/lib/utils/map-utils-wms.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ import { WmsEndpoint, WmsLayerFull } from '@camptocamp/ogc-client'
import { MapContextLayerWmsModel } from '../map-context/map-context.model'
import { ProxyService } from '@geonetwork-ui/util/shared'
import { from, Observable } from 'rxjs'
import { map } from 'rxjs/operators'
import { map, switchMap } from 'rxjs/operators'
import { LONLAT_CRS_CODES } from './projections'
import { fromEPSGCode, register } from 'ol/proj/proj4'
import { Extent } from 'ol/extent'
import proj4 from 'proj4/dist/proj4'
import { transformExtent } from 'ol/proj'

@Injectable({
providedIn: 'root',
Expand All @@ -24,17 +28,31 @@ export class MapUtilsWMSService {

getLayerLonLatBBox(layer: MapContextLayerWmsModel) {
return this.getLayerFull(layer).pipe(
map((wmsLayerFull) => this.getLonLatBBox(wmsLayerFull))
switchMap((wmsLayerFull) => from(this.getLonLatBBox(wmsLayerFull)))
)
}

getLonLatBBox(wmsLayerFull: WmsLayerFull) {
async getLonLatBBox(wmsLayerFull: WmsLayerFull): Promise<Extent> {
const { boundingBoxes } = wmsLayerFull
const lonLatCRS = Object.keys(boundingBoxes)?.find((crs) =>
LONLAT_CRS_CODES.includes(crs)
)
if (lonLatCRS) {
return boundingBoxes[lonLatCRS]
} else {
const availableEPSGCode = Object.keys(boundingBoxes)[0]
register(proj4)
const proj = await fromEPSGCode(availableEPSGCode)
proj4.defs(availableEPSGCode, proj)

const bboxWithFiniteNumbers = [
parseFloat(boundingBoxes[availableEPSGCode][0]),
parseFloat(boundingBoxes[availableEPSGCode][1]),
parseFloat(boundingBoxes[availableEPSGCode][2]),
parseFloat(boundingBoxes[availableEPSGCode][3]),
]
const extent = transformExtent(bboxWithFiniteNumbers, proj, 'EPSG:4326')
return extent
}
}
}
11 changes: 10 additions & 1 deletion libs/feature/map/src/lib/utils/map-utils.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ import {
import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/model/record'
import MapBrowserEvent from 'ol/MapBrowserEvent'

jest.mock('ol/proj/proj4', () => {
const fromEPSGCodeMock = jest.fn()
const registerMock = jest.fn()
return {
fromEPSGCode: fromEPSGCodeMock,
register: registerMock,
}
})

const wmsUtilsMock = {
getLayerLonLatBBox: jest.fn(() => of([1.33, 48.81, 4.3, 51.1])),
}
Expand Down Expand Up @@ -146,7 +155,7 @@ describe('MapUtilsService', () => {
})
it('returns true', () => {
expect(url).toEqual(
'url?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetFeatureInfo&FORMAT=image%2Fpng&TRANSPARENT=true&QUERY_LAYERS=layerName&LAYERS=layerName&INFO_FORMAT=application%2Fjson&I=50&J=50&CRS=EPSG%3A3857&STYLES=&WIDTH=101&HEIGHT=101&BBOX=-1697932.4932933417%2C4610319.813853541%2C1332067.5067066583%2C7640319.813853541'
'url?QUERY_LAYERS=layerName&INFO_FORMAT=application%2Fjson&REQUEST=GetFeatureInfo&SERVICE=WMS&VERSION=1.3.0&FORMAT=image%2Fpng&STYLES=&TRANSPARENT=true&LAYERS=layerName&I=50&J=50&WIDTH=101&HEIGHT=101&CRS=EPSG%3A3857&BBOX=-1697932.4932933417%2C4610319.813853541%2C1332067.5067066583%2C7640319.813853541'
)
})
})
Expand Down
4 changes: 2 additions & 2 deletions libs/feature/map/src/lib/utils/map-utils.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core'
import type { FeatureCollection } from 'geojson'
import { extend, Extent, isEmpty } from 'ol/extent'
import OlFeature from 'ol/Feature'
import OlFeature, { FeatureLike } from 'ol/Feature'
import GeoJSON from 'ol/format/GeoJSON'
import { Geometry } from 'ol/geom'
import Layer from 'ol/layer/Layer'
Expand Down Expand Up @@ -50,7 +50,7 @@ export class MapUtilsService {
featureCollection: FeatureCollection,
featureProjection = FEATURE_PROJECTION,
dataProjection = DATA_PROJECTION
): OlFeature<Geometry>[] => {
): FeatureLike[] => {
const olFeatures = new GeoJSON().readFeatures(featureCollection, {
featureProjection,
dataProjection,
Expand Down
7 changes: 7 additions & 0 deletions libs/feature/map/src/test-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ getTestBed().initTestEnvironment(
platformBrowserDynamicTesting(),
{ teardown: { destroyAfterEach: false } }
)

class ResizeObserverMock {
observe = jest.fn()
unobserve = jest.fn()
}

;(window as any).ResizeObserver = ResizeObserverMock
Loading

0 comments on commit 6d4aec4

Please sign in to comment.