Skip to content

Commit

Permalink
Merge pull request #327 from geoadmin/develop
Browse files Browse the repository at this point in the history
New Release v0.4.0 - #minor
  • Loading branch information
procrastinatio authored Dec 20, 2022
2 parents 360aab9 + 5be5818 commit 5af3ebe
Show file tree
Hide file tree
Showing 48 changed files with 1,652 additions and 389 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"private": true,
"scripts": {
"start": "npm run dev",
"dev": "vite --port 8080",
"dev": "vite --port 8080 --host --cors",
"preview": "npm run build:prod && vite preview --port 8080 --outDir dist/production",
"preview:dev": "npm run build:dev && vite preview --port 8080 --outDir dist/development",
"preview:int": "npm run build:int && vite preview --port 8080 --outDir dist/integration",
Expand Down
2 changes: 1 addition & 1 deletion src/api/__tests__/features.api.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe('Validate features api', () => {
expect(reconstructed.description).to.be.equal(testObject.description)
expect(reconstructed.icon).to.be.equal(testObject.icon)

/* If theses classes are extended to save more data than what is serialized, these tests
/* If these classes are extended to save more data than what is serialized, these tests
will need to be changed*/
expect(reconstructed.textColor).to.deep.equal(testObject.textColor)
expect(reconstructed.textSize).to.deep.equal(testObject.textSize)
Expand Down
79 changes: 50 additions & 29 deletions src/api/features.api.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Icon } from '@/api/icon.api'
import { DrawingIcon } from '@/api/icon.api'
import { API_BASE_URL } from '@/config'
import { CoordinateSystems } from '@/utils/coordinateUtils'
import EventEmitter from '@/utils/EventEmitter.class'
Expand All @@ -10,23 +10,25 @@ import {
MEDIUM,
RED,
} from '@/utils/featureStyleUtils'
import { getEditableFeatureFromLegacyKmlFeature } from '@/utils/legacyKmlUtils'
import log from '@/utils/logging'
import axios from 'axios'
import { Icon as openlayersIcon } from 'ol/style'
import Feature from 'ol/Feature'
import { featureStyleFunction } from '@/modules/drawing/lib/style'
import { GeodesicGeometries } from '@/utils/geodesicManager'
/**
* Representation of a feature that can be selected by the user on the map. This feature can be
* edited if the corresponding flag says so (it will then fires "change" events any time one
* property of the instance has changed)
* edited if the corresponding flag says so (it will then fire "change" events any time one property
* of the instance has changed)
*
* This will then be specialized in (at least) two flavor of features, layer feature (coming from
* our backend, with extra information attached) and drawing feature (that can be modified by the
* user)
*
* @abstract
*/
export class Feature extends EventEmitter {
export class SelectableFeature extends EventEmitter {
/**
* @param {String | Number} id Unique identifier for this feature (unique in the context it
* comes from, not for the whole app)
Expand All @@ -42,9 +44,9 @@ export class Feature extends EventEmitter {
super()
this._id = id
// using the setter for coordinate (see below)
this._coordinates = coordinates
this._title = title
this._description = description
this.coordinates = coordinates
this.title = title
this.description = description
this._isEditable = !!isEditable
this._isDragged = false
}
Expand Down Expand Up @@ -95,6 +97,10 @@ export class Feature extends EventEmitter {
this._coordinates = newCoordinates
}
this.emitChangeEvent('coordinates')
} else if (newCoordinates === null) {
this._coordinates = newCoordinates
} else {
log.error(`feature.api new coordinates is not an array`, newCoordinates)
}
}
get lastCoordinate() {
Expand Down Expand Up @@ -132,20 +138,20 @@ export const EditableFeatureTypes = {
}

/** Describe a feature that can be edited by the user, such as feature from the current drawing */
export class EditableFeature extends Feature {
export class EditableFeature extends SelectableFeature {
/**
* @param {String | Number} id Unique identifier for this feature (unique in the context it
* comes from, not for the whole app)
* @param {Number[][]} coordinates [[x,y],[x2.y2],...] coordinates of this feature in EPSG:3857
* (metric mercator)
* @param {Number[][]} coordinates [[x,y],[x2.y2],...] or [x,y] if point geometry coordinates of
* this feature in EPSG:3857 (metric mercator)
* @param {String} title Title of this feature
* @param {String} description A description of this feature, can not be HTML content (only
* text)
* @param {EditableFeatureTypes} featureType Type of this editable feature
* @param {FeatureStyleColor} textColor Color for the text of this feature
* @param {FeatureStyleSize} textSize Size of the text for this feature
* @param {FeatureStyleColor} fillColor Color of the icon (if defined)
* @param {Icon} icon Icon that will be covering this feature, can be null
* @param {DrawingIcon} icon Icon that will be covering this feature, can be null
* @param {FeatureStyleSize} iconSize Size of the icon (if defined) that will be covering this
* feature
*/
Expand Down Expand Up @@ -196,7 +202,7 @@ export class EditableFeature extends Feature {
/**
* This function returns a stripped down version of this object ready to be serialized.
*
* @returns The version of the object that can be serialized
* @returns {Object} The version of the object that can be serialized
*/
getStrippedObject() {
/* Warning: Changing this method will break the compability of KML files */
Expand All @@ -217,7 +223,7 @@ export class EditableFeature extends Feature {
/**
* Regenerates the full version of an editable feature given a stripped version.
*
* @param {stripped EditableFeature} o A stripped down version of the editable Feature
* @param {Object} o A stripped down version of the editable Feature
* @returns The full version of the editable Feature
*/
static recreateObject(o) {
Expand All @@ -230,28 +236,43 @@ export class EditableFeature extends Feature {
FeatureStyleColor.recreateObject(o.textColor),
FeatureStyleSize.recreateObject(o.textSize),
FeatureStyleColor.recreateObject(o.fillColor),
o.icon ? Icon.recreateObject(o.icon) : null,
o.icon ? DrawingIcon.recreateObject(o.icon) : null,
FeatureStyleSize.recreateObject(o.iconSize)
)
}

/**
* This method deserializes an editable feature that is stored in the extra properties of an
* openlayers feature. It then recreates a fully functional olFeature with the correct styling.
* openlayers feature. If there is no editable feature to deserialize (e.g. in the case of a kml
* that was generated with mf-geoadmin3), the editable feature is instead reconstructed with the
* styling information stored in the official '<style>' tag of the kml. It then recreates a
* fully functional olFeature with the correct styling.
*
* @param {openlayersFeature} olFeature
* @param {Feature} olFeature An olFeature that was just deserialized with
* @param {DrawingIconSet[]} availableIconSets {@link ol/format/KML}.
*/
static deserialize(olFeature) {
const editableFeature = this.recreateObject(JSON.parse(olFeature.get('editableFeature')))
olFeature.set('editableFeature', editableFeature)
olFeature.setStyle(featureStyleFunction)
if (editableFeature.isLineOrMeasure()) {
/* The featureStyleFunction uses the geometries calculated in the geodesic object
if present. The lines connecting the vertices of the geometry will appear
geodesic (follow the shortest path) in this case instead of linear (be straight on
the screen) */
olFeature.geodesic = new GeodesicGeometries(olFeature)
static deserialize(olFeature, availableIconSets) {
const serializedEditableFeature = olFeature.get('editableFeature')
// in case we are deserializing a legacy KML (one made with mf-geoadmin3) the editableFeature object
// will not be present, and we will have to rebuild one from the styles tags in the KML
let editableFeature = null
if (!serializedEditableFeature) {
editableFeature = getEditableFeatureFromLegacyKmlFeature(olFeature, availableIconSets)
} else {
editableFeature = this.recreateObject(JSON.parse(serializedEditableFeature))
}
if (editableFeature) {
olFeature.set('editableFeature', editableFeature)
olFeature.setStyle(featureStyleFunction)
if (editableFeature.isLineOrMeasure()) {
/* The featureStyleFunction uses the geometries calculated in the geodesic object
if present. The lines connecting the vertices of the geometry will appear
geodesic (follow the shortest path) in this case instead of linear (be straight on
the screen) */
olFeature.geodesic = new GeodesicGeometries(olFeature)
}
}
return editableFeature
}

isLineOrMeasure() {
Expand Down Expand Up @@ -308,11 +329,11 @@ export class EditableFeature extends Feature {
return this._textSize?.font
}

/** @returns {Icon | null} */
/** @returns {DrawingIcon | null} */
get icon() {
return this._icon
}
/** @param newIcon {Icon} */
/** @param newIcon {DrawingIcon} */
set icon(newIcon) {
this._icon = newIcon
this.emitStylingChangeEvent('icon')
Expand Down Expand Up @@ -389,7 +410,7 @@ export class EditableFeature extends Feature {
* Describe a feature from the backend, so a feature linked to a backend layer (see
* {@link getFeature}) below
*/
export class LayerFeature extends Feature {
export class LayerFeature extends SelectableFeature {
/**
* @param {GeoAdminLayer} layer The layer in which this feature belongs
* @param {Number | String} id The unique feature ID in the layer it is part of
Expand Down
26 changes: 13 additions & 13 deletions src/api/icon.api.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import axios from 'axios'
*
* Some sets are colorable, where other aren't
*/
export class IconSet {
export class DrawingIconSet {
/**
* @param {String} name Name of this set in the backend, lower cased
* @param {Boolean} isColorable Tells if this set's icons can be colored differently (if the
Expand Down Expand Up @@ -52,14 +52,14 @@ export class IconSet {
return this._templateURL
}

/** @returns {Icon[]} List of all icons from this icon set */
/** @returns {DrawingIcon[]} List of all icons from this icon set */
get icons() {
return [...this._icons]
}
/**
* Overwrite all icons of this set with the new ones
*
* @param {Icon[]} newIcons
* @param {DrawingIcon[]} newIcons
*/
set icons(newIcons) {
this._icons = [...newIcons]
Expand All @@ -71,12 +71,12 @@ export class IconSet {
*
* Each icon has a specific anchor (an offset from the coordinate it is linked to)
*/
export class Icon {
export class DrawingIcon {
/**
* @param {String} name Name of this icon in the backend (lower cased)
* @param {String} imageURL URL to the image of this icon itself (with default size and color)
* @param {String} imageTemplateURL URL template where size and color can be defined (by
* replacing {icon_scale} and {{r},{g},{b}} respectively, see {@link Icon.generateURL})
* replacing {icon_scale} and {{r},{g},{b}} respectively, see {@link DrawingIcon.generateURL})
* @param {String} iconSetName Name of the icon set in which belongs this icon (an icon can only
* belong to one icon set)
* @param {Number[]} anchor Offset to apply to this icon when placed on a coordinate ([x,y]
Expand All @@ -102,7 +102,7 @@ export class Icon {
}

static recreateObject(o) {
return new Icon(o.name, o.imageURL, o.imageTemplateURL, o.iconSetName, o.anchor)
return new DrawingIcon(o.name, o.imageURL, o.imageTemplateURL, o.iconSetName, o.anchor)
}

/** @returns {String} Name of this icon in the backend (lower cased) */
Expand All @@ -117,7 +117,7 @@ export class Icon {

/**
* @returns {String} URL template where size and color can be defined (by replacing {icon_scale}
* and {{r},{g},{b}} respectively, see {@link Icon.generateURL})
* and {{r},{g},{b}} respectively, see {@link DrawingIcon.generateURL})
*/
get imageTemplateURL() {
return this._imageTemplateURL
Expand All @@ -129,8 +129,8 @@ export class Icon {
}

/**
* @returns {String} Name of the {@link IconSet} in which belongs this icon (an icon can only
* belong to one icon set)
* @returns {String} Name of the {@link DrawingIconSet} in which belongs this icon (an icon can
* only belong to one icon set)
*/
get iconSetName() {
return this._iconSetName
Expand Down Expand Up @@ -171,14 +171,14 @@ export class Icon {
* Also retrieve all available icons for those icon sets (so no need to do any additional request to
* the backend after that)
*
* @returns {Promise<IconSet[]>}
* @returns {Promise<DrawingIconSet[]>}
*/
export async function loadAllIconSetsFromBackend() {
const sets = []
try {
const rawSets = (await axios.get(`${API_SERVICES_BASE_URL}icons/sets`)).data.items
for (const rawSet of rawSets) {
const iconSet = new IconSet(
const iconSet = new DrawingIconSet(
rawSet.name,
rawSet.colorable,
rawSet.icons_url,
Expand All @@ -198,7 +198,7 @@ export async function loadAllIconSetsFromBackend() {
/**
* Loads all icons from an icon set and attach them to the icon set
*
* @param {IconSet} iconSet
* @param {DrawingIconSet} iconSet
* @returns {Promise} Promise resolving when all icons have been attached to the icon set
*/
function loadIconsForIconSet(iconSet) {
Expand All @@ -207,7 +207,7 @@ function loadIconsForIconSet(iconSet) {
.then((rawIcons) => {
iconSet.icons = rawIcons.data.items.map(
(rawIcon) =>
new Icon(
new DrawingIcon(
rawIcon.name,
rawIcon.url,
rawIcon.template_url,
Expand Down
22 changes: 16 additions & 6 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@ export const ENVIRONMENT = VITE_ENVIRONMENT
*/
export const IS_TESTING_WITH_CYPRESS = !!window.Cypress

/**
* Flag that tells if users should be warned that it is a development site. Also used to hide
* development specific features in production (like the app version)
*/
export const DEV_SITE_WARNING = !IS_TESTING_WITH_CYPRESS && ENVIRONMENT !== 'production'

/**
* Current app version (from package.json)
*
Expand Down Expand Up @@ -253,3 +247,19 @@ export const DRAWING_HIT_TOLERANCE = 6
export const VECTOR_TILES_STYLE_ID = 'ch.swisstopo.leichte-basiskarte_world.vt'

export const VECTOR_TILES_STYLE_URL = `https://vectortiles.geo.admin.ch/styles/${VECTOR_TILES_STYLE_ID}/style.json`

/**
* Display a big developpment banner on all but these hosts.
*
* @type {String[]}
*/
export const NO_WARNING_BANNER_HOSTNAMES = ['test.map.geo.admin.ch', 'map.geo.admin.ch']

/**
* Display a warning ribbon ('TEST') on the top-left (mobile) or bottom-left (desktop)
* corner on all these hosts.
*
* @type {String[]}
*/

export const WARNING_RIBBON_HOSTNAMES = ['test.map.geo.admin.ch']
2 changes: 1 addition & 1 deletion src/modules/drawing/DrawingModule.vue
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ export default {
featureProjection: layer.projection,
})
features.forEach((olFeature) => {
EditableFeature.deserialize(olFeature)
EditableFeature.deserialize(olFeature, this.availableIconSets)
})
this.drawingLayer.getSource().addFeatures(features)
},
Expand Down
10 changes: 3 additions & 7 deletions src/modules/drawing/components/DrawingToolbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<teleport v-if="readyForTeleport" to=".drawing-toolbox-in-menu">
<DrawingHeader v-if="isDesktopMode" @close="emitCloseEvent" />
<div :class="[{ 'drawing-toolbox-closed': !drawMenuOpen }, 'drawing-toolbox']">
<div class="card text-center drawing-toolbox-content">
<div class="card text-center drawing-toolbox-content rounded-0">
<div class="card-body position-relative">
<ButtonWithIcon
class="drawing-toolbox-close-button"
Expand Down Expand Up @@ -77,17 +77,13 @@
<ModalWithBackdrop
v-if="showClearConfirmationModal"
show-confirmation-buttons
fluid
data-cy="drawing-toolbox-delete-confirmation-modal"
@close="onCloseClearConfirmation"
>
{{ $t('confirm_remove_all_features') }}
</ModalWithBackdrop>
<ModalWithBackdrop
v-if="showShareModal"
:title="$t('share')"
data-cy="drawing-toolbox-share-modal"
@close="onCloseShare"
>
<ModalWithBackdrop v-if="showShareModal" fluid :title="$t('share')" @close="onCloseShare">
<ShareForm :kml-ids="kmlIds" />
</ModalWithBackdrop>
</teleport>
Expand Down
4 changes: 1 addition & 3 deletions src/modules/drawing/components/DrawingTooltip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,7 @@ export default {
const hasFeatureSelected = this.selectedFeatures.length > 0
// we only keep track of the first feature's info (the one on top of the stack)
const selectedFeatureId = hasFeatureSelected
? this.selectedFeatures[0]?.id.replace('drawing_feature_', '')
: null
const selectedFeatureId = hasFeatureSelected ? this.selectedFeatures[0]?.id : null
let featureUnderCursor
map.forEachFeatureAtPixel(
Expand Down
Loading

0 comments on commit 5af3ebe

Please sign in to comment.