Skip to content

Commit

Permalink
Merge pull request #843 from geoadmin/fix-489-text-offset
Browse files Browse the repository at this point in the history
PB-489: Fix text offset
  • Loading branch information
ismailsunni authored May 21, 2024
2 parents 31b2594 + 88606e0 commit ffd0344
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 15 deletions.
1 change: 1 addition & 0 deletions cypress.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default defineConfig({
defaultCommandTimeout: 5000,
requestTimeout: 5000,
numTestsKeptInMemory: 2,
watchForFileChanges: false, // Prevent auto run on file changes

retries: {
runMode: 3,
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/vue-fontawesome": "^3.0.6",
"@geoblocks/cesium-compass": "^0.5.0",
"@geoblocks/mapfishprint": "^0.2.13",
"@geoblocks/mapfishprint": "^0.2.14",
"@geoblocks/ol-maplibre-layer": "^0.1.3",
"@ivanv/vue-collapse-transition": "^1.0.2",
"@mapbox/togeojson": "^0.16.2",
Expand Down
41 changes: 36 additions & 5 deletions src/api/print.api.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
requestReport,
} from '@geoblocks/mapfishprint'
import axios from 'axios'
import { Circle } from 'ol/style'

import { API_BASE_URL, API_SERVICES_BASE_URL, WMS_BASE_URL } from '@/config'
import i18n from '@/modules/i18n'
Expand Down Expand Up @@ -88,6 +89,16 @@ class GeoAdminCustomizer extends BaseCustomizer {
symbolizer.pointRadius = adjustWidth(symbolizer.pointRadius, this.printResolution)
symbolizer.strokeWidth = adjustWidth(symbolizer.strokeWidth, this.printResolution)
symbolizer.haloRadius = adjustWidth(symbolizer.haloRadius, this.printResolution)
// Ideally this should be done in the geoblocks/mapfishprint
// but it's quite complex to handle all the cases
try {
const fontFamily = symbolizer.fontFamily.split(' ')
symbolizer.fontWeight = fontFamily[0]
symbolizer.fontSize = parseInt(fontFamily[1])
symbolizer.fontFamily = fontFamily[2].toUpperCase()
} catch (error) {
// Keep the font family as it is
}
}

/**
Expand All @@ -100,12 +111,32 @@ class GeoAdminCustomizer extends BaseCustomizer {
*/
// eslint-disable-next-line no-unused-vars
point(layerState, symbolizer, image) {
symbolizer.graphicWidth = adjustWidth(symbolizer.graphicWidth, this.printResolution)
symbolizer.graphicXOffset = adjustWidth(symbolizer.graphicXOffset, this.printResolution)
symbolizer.graphicYOffset = adjustWidth(symbolizer.graphicYOffset, this.printResolution)
// Handling the case where we need to print a circle in the end of measurement lines
// It's not rendered in the OpenLayers (opacity == 0.0) but it's needed to be rendered in the print
const scale = image.getScaleArray()[0]
let size = null
let anchor = null

// We need to resize the image to match the old geoadmin
if (symbolizer.externalGraphic) {
size = image.getSize()
anchor = image.getAnchor()
} else if (image instanceof Circle) {
const radius = image.getRadius()
const width = adjustWidth(2 * radius, this.printResolution)
size = [width, width]
anchor = [width / 2, width / 2]
}

if (anchor) {
symbolizer.graphicXOffset = adjustWidth(-anchor[0] * scale, this.printResolution)
symbolizer.graphicYOffset = adjustWidth(-anchor[1] * scale, this.printResolution)
}
if (size) {
symbolizer.graphicWidth = adjustWidth(size[0] * scale, this.printResolution)
}

if (symbolizer.fillOpacity === 0.0 && symbolizer.fillColor === '#ff0000') {
// Handling the case where we need to print a circle in the end of measurement lines
// It's not rendered in the OpenLayers (opacity == 0.0) but it's needed to be rendered in the print
symbolizer.fillOpacity = 1
}
}
Expand Down
8 changes: 3 additions & 5 deletions src/utils/featureStyleUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,10 @@ export function calculateTextOffset(textScale, iconScale, anchor, iconSize) {
const iconOffset = 0.5 * iconScale * anchorScale * iconSize[1]
const textOffset = 0.5 * fontSize * textScale
const defaultOffset = 5
log.debug('title offset of feature is calculated to be : ', [
0,
defaultOffset + iconOffset + textOffset,
])
const offset = [0, -(defaultOffset + iconOffset + textOffset)]
log.debug('title offset of feature is calculated to be : ', offset)

return [0, -(defaultOffset + iconOffset + textOffset)]
return offset
}

/**
Expand Down
1 change: 1 addition & 0 deletions tests/cypress/fixtures/print/label.kml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/kml/2.2 https://developers.google.com/kml/schema/kml22gx.xsd"><Document><name>Drawing</name><Placemark id="drawing_feature_1715908968084"><name>Sample Label</name><description></description><Style><IconStyle><scale>1.125</scale><Icon><href>https://sys-map.dev.bgdi.ch/api/icons/sets/default/icons/001-marker@1x-255,0,0.png</href><gx:w>48</gx:w><gx:h>48</gx:h></Icon><hotSpot x="24" y="6" xunits="pixels" yunits="pixels"/></IconStyle><LabelStyle><color>ff0000ff</color><scale>1.5</scale></LabelStyle></Style><ExtendedData><Data name="textOffset"><value>0,-44.75</value></Data><Data name="type"><value>marker</value></Data></ExtendedData><Point><coordinates>7.629431833399272,47.0431253777804</coordinates></Point></Placemark></Document></kml>
1 change: 1 addition & 0 deletions tests/cypress/fixtures/print/old-geoadmin-label.kml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/kml/2.2 https://developers.google.com/kml/schema/kml22gx.xsd"><Document><name>Drawing</name><Placemark id="marker_1715912961995"><ExtendedData><Data name="type"><value>marker</value></Data></ExtendedData><name>Old Label</name><description></description><Style><IconStyle><Icon><href>https://api3.geo.admin.ch/color/255,0,0/[email protected]</href><gx:w>48</gx:w><gx:h>48</gx:h></Icon><hotSpot x="24" y="4.799999999999997" xunits="pixels" yunits="pixels"/></IconStyle><LabelStyle><color>ff0000ff</color></LabelStyle></Style><Point><tessellate>1</tessellate><altitudeMode>clampToGround</altitudeMode><coordinates>8.161491779847474,46.97804233732786</coordinates></Point></Placemark></Document></kml>
238 changes: 238 additions & 0 deletions tests/cypress/tests-e2e/print.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -443,5 +443,243 @@ describe('Testing print', () => {
).to.lessThan(1.5) // thinner than the drawn in the OL map.
})
})
it('should send a print request correctly to mapfishprint (icon and label)', () => {
interceptPrintRequest()
interceptPrintStatus()
interceptDownloadReport()

cy.goToMapView({}, true)
cy.readStoreValue('state.layers.activeLayers').should('be.empty')
cy.openMenuIfMobile()
cy.get('[data-cy="menu-tray-tool-section"]:visible').click()
cy.get('[data-cy="menu-advanced-tools-import-file"]:visible').click()

cy.get('[data-cy="import-file-content"]').should('be.visible')
cy.get('[data-cy="import-file-online-content"]').should('be.visible')

const localKmlFile = 'print/label.kml'

// Test local import
cy.log('Switch to local import')
cy.get('[data-cy="import-file-local-btn"]:visible').click()
cy.get('[data-cy="import-file-local-content"]').should('be.visible')

// Attach a local KML file
cy.log('Test add a local KML file')
cy.fixture(localKmlFile, null).as('kmlFixture')
cy.get('[data-cy="file-input"]').selectFile('@kmlFixture', {
force: true,
})
cy.get('[data-cy="import-file-load-button"]:visible').click()

// Assertions for successful import
cy.get('[data-cy="file-input-text"]')
.should('have.class', 'is-valid')
.should('not.have.class', 'is-invalid')
cy.get('[data-cy="file-input-valid-feedback"]')
.should('be.visible')
.contains('File successfully imported')
cy.get('[data-cy="import-file-load-button"]').should('be.visible').contains('Import')
cy.get('[data-cy="import-file-online-content"]').should('not.be.visible')
cy.readStoreValue('state.layers.activeLayers').should('have.length', 1)

// Close the import tool
cy.get('[data-cy="import-file-close-button"]:visible').click()
cy.get('[data-cy="import-file-content"]').should('not.exist')

// Print
cy.get('[data-cy="menu-print-section"]').should('be.visible').click()
cy.get('[data-cy="menu-print-form"]').should('be.visible')

cy.get('[data-cy="print-map-button"]').should('be.visible').click()
cy.get('[data-cy="abort-print-button"]').should('be.visible')

cy.wait('@printRequest').then((interception) => {
expect(interception.request.body).to.haveOwnProperty('layout')
expect(interception.request.body['layout']).to.equal('1. A4 landscape')
expect(interception.request.body).to.haveOwnProperty('format')
expect(interception.request.body['format']).to.equal('pdf')

const attributes = interception.request.body.attributes
expect(attributes).to.haveOwnProperty('printLegend')
expect(attributes['printLegend']).to.equals(0)
expect(attributes).to.haveOwnProperty('qrimage')
expect(attributes['qrimage']).to.contains(
encodeURIComponent('https://s.geo.admin.ch/0000000')
)

const mapAttributes = attributes.map
expect(mapAttributes['scale']).to.equals(10000)
expect(mapAttributes['dpi']).to.equals(254)
expect(mapAttributes['projection']).to.equals('EPSG:2056')

const layers = mapAttributes.layers
expect(layers).to.be.an('array')
expect(layers).to.have.length(2)
expect(layers[0]['type']).to.equals('geojson')
expect(layers[0]['geoJson']['features']).to.have.length(1)
expect(layers[0]['geoJson']['features'][0]['properties']).to.haveOwnProperty(
'_mfp_style'
)
expect(layers[0]['geoJson']['features'][0]['properties']['_mfp_style']).to.equal(
'1'
)
expect(layers[0]['style']).to.haveOwnProperty("[_mfp_style = '1']")

const symbolizers = layers[0]['style']["[_mfp_style = '1']"]['symbolizers']
const pointSymbol = symbolizers[0]
expect(pointSymbol).to.haveOwnProperty('type')
const pointSymbolAttributes = {
type: 'point',
externalGraphic: '001-marker@1x-255,0,0.png', // suffix only
graphicWidth: 19.133858267716537,
}

for (const attribute in pointSymbolAttributes) {
expect(pointSymbol).to.haveOwnProperty(attribute)
}
expect(
pointSymbol['externalGraphic'].endsWith(
pointSymbolAttributes['externalGraphic']
)
).to.be.true
expect(
pointSymbol['graphicWidth'] - pointSymbolAttributes['graphicWidth']
).to.lessThan(0.1)

const textSymbol = symbolizers[1]
const textSymbolAttributes = {
type: 'text',
label: 'Sample Label',
fontFamily: 'HELVETICA',
fontSize: 16,
fontWeight: 'normal',
labelYOffset: 44.75,
}
for (const attribute in textSymbolAttributes) {
expect(textSymbol).to.haveOwnProperty(attribute)
expect(textSymbol[attribute]).to.equal(textSymbolAttributes[attribute])
}
})
})
it('should send a print request correctly to mapfishprint (KML from old geoadmin)', () => {
interceptPrintRequest()
interceptPrintStatus()
interceptDownloadReport()

cy.goToMapView({}, true)
cy.readStoreValue('state.layers.activeLayers').should('be.empty')
cy.openMenuIfMobile()
cy.get('[data-cy="menu-tray-tool-section"]:visible').click()
cy.get('[data-cy="menu-advanced-tools-import-file"]:visible').click()

cy.get('[data-cy="import-file-content"]').should('be.visible')
cy.get('[data-cy="import-file-online-content"]').should('be.visible')

const localKmlFile = 'print/old-geoadmin-label.kml'

// Test local import
cy.log('Switch to local import')
cy.get('[data-cy="import-file-local-btn"]:visible').click()
cy.get('[data-cy="import-file-local-content"]').should('be.visible')

// Attach a local KML file
cy.log('Test add a local KML file')
cy.fixture(localKmlFile, null).as('kmlFixture')
cy.get('[data-cy="file-input"]').selectFile('@kmlFixture', {
force: true,
})
cy.get('[data-cy="import-file-load-button"]:visible').click()

// Assertions for successful import
cy.get('[data-cy="file-input-text"]')
.should('have.class', 'is-valid')
.should('not.have.class', 'is-invalid')
cy.get('[data-cy="file-input-valid-feedback"]')
.should('be.visible')
.contains('File successfully imported')
cy.get('[data-cy="import-file-load-button"]').should('be.visible').contains('Import')
cy.get('[data-cy="import-file-online-content"]').should('not.be.visible')
cy.readStoreValue('state.layers.activeLayers').should('have.length', 1)

// Close the import tool
cy.get('[data-cy="import-file-close-button"]:visible').click()
cy.get('[data-cy="import-file-content"]').should('not.exist')

// Print
cy.get('[data-cy="menu-print-section"]').should('be.visible').click()
cy.get('[data-cy="menu-print-form"]').should('be.visible')

cy.get('[data-cy="print-map-button"]').should('be.visible').click()
cy.get('[data-cy="abort-print-button"]').should('be.visible')

cy.wait('@printRequest').then((interception) => {
expect(interception.request.body).to.haveOwnProperty('layout')
expect(interception.request.body['layout']).to.equal('1. A4 landscape')
expect(interception.request.body).to.haveOwnProperty('format')
expect(interception.request.body['format']).to.equal('pdf')

const attributes = interception.request.body.attributes
expect(attributes).to.haveOwnProperty('printLegend')
expect(attributes['printLegend']).to.equals(0)
expect(attributes).to.haveOwnProperty('qrimage')
expect(attributes['qrimage']).to.contains(
encodeURIComponent('https://s.geo.admin.ch/0000000')
)

const mapAttributes = attributes.map
expect(mapAttributes['scale']).to.equals(10000)
expect(mapAttributes['dpi']).to.equals(254)
expect(mapAttributes['projection']).to.equals('EPSG:2056')

const layers = mapAttributes.layers
expect(layers).to.be.an('array')
expect(layers).to.have.length(2)
expect(layers[0]['type']).to.equals('geojson')
expect(layers[0]['geoJson']['features']).to.have.length(1)
expect(layers[0]['geoJson']['features'][0]['properties']).to.haveOwnProperty(
'_mfp_style'
)
expect(layers[0]['geoJson']['features'][0]['properties']['_mfp_style']).to.equal(
'1'
)
expect(layers[0]['style']).to.haveOwnProperty("[_mfp_style = '1']")

const symbolizers = layers[0]['style']["[_mfp_style = '1']"]['symbolizers']
const pointSymbol = symbolizers[0]
expect(pointSymbol).to.haveOwnProperty('type')
const pointSymbolAttributes = {
type: 'point',
externalGraphic: '001-marker@1x-255,0,0.png', // suffix only
graphicWidth: 17.007874015748033,
graphicXOffset: -8.503937007874017,
graphicYOffset: -8.503937007874017,
}

for (const attribute in pointSymbolAttributes) {
expect(pointSymbol).to.haveOwnProperty(attribute)
if (attribute === 'externalGraphic') {
expect(pointSymbol[attribute].endsWith(pointSymbolAttributes[attribute])).to
.be.true
} else {
expect(pointSymbol[attribute]).to.equal(pointSymbolAttributes[attribute])
}
}

const textSymbol = symbolizers[1]
const textSymbolAttributes = {
type: 'text',
label: 'Old Label',
fontFamily: 'HELVETICA',
fontSize: 16,
fontWeight: 'normal',
labelYOffset: 0,
}
for (const attribute in textSymbolAttributes) {
expect(textSymbol).to.haveOwnProperty(attribute)
expect(textSymbol[attribute]).to.equal(textSymbolAttributes[attribute])
}
})
})
})
})

0 comments on commit ffd0344

Please sign in to comment.