Skip to content

Commit

Permalink
feat: add sincronization between map center/zoom and app url
Browse files Browse the repository at this point in the history
  • Loading branch information
amoncaldas committed Dec 9, 2021
1 parent c3f5e4b commit 02949e1
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 83 deletions.
1 change: 1 addition & 0 deletions src/fragments/map-view/MapView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
:url="tileProvider.url"
:attribution="tileProvider.attribution"
:token="tileProvider.token"
:options="{maxZoom: tileProvider.maxZoom}"
layer-type="base"/>
<l-tile-layer
v-for="layer in overlayerTileProviders"
Expand Down
40 changes: 24 additions & 16 deletions src/fragments/map-view/map-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export default {
}
},
computed: {

showMyLocationControl () {
return this.supportsMyLocationBtn && !this.isAltitudeModalOpen && this.showControls
},
Expand Down Expand Up @@ -675,8 +675,12 @@ export default {
* store the current location on localStorage
*/
center: {
handler: function () {
this.centerChanged()
handler: function (newVal, oldVal) {
// Center might contain a new object, but with the same values
// We make this check before triggering centerChanged
if (oldVal && newVal && newVal.toString() !== oldVal.toString()) {
this.centerChanged()
}
},
deep: true
}
Expand Down Expand Up @@ -868,16 +872,18 @@ export default {
* @emits zoomChanged
*/
zoomed (event) {
this.zoomLevel = event.sourceTarget._zoom
if (!this.featuresJustFitted && !this.hasOnlyOneMarker) {
this.storeMapBoundsAndSetMapAsReady()
if (this.zoomLevel !== event.sourceTarget._zoom) {
this.zoomLevel = event.sourceTarget._zoom
if (!this.featuresJustFitted && !this.hasOnlyOneMarker) {
this.storeMapBoundsAndSetMapAsReady()
} else {
// If the zoom was changed programmatically
// reset the flag to false, as it has already
// been accomplished its goal for one zoom event cycle
this.featuresJustFitted = false
}
this.$emit('zoomChanged', {zoom: event.sourceTarget._zoom, map: this.map, context: this})
} else {
// If the zoom was changed programmatically
// reset the flag to false, as it has already
// been accomplished its goal for one zoom event cycle
this.featuresJustFitted = false
}
}
},

/**
Expand Down Expand Up @@ -1088,7 +1094,7 @@ export default {
if (this.localMapViewData.hasPlaces()) {
this.defineActiveRouteIndex()
this.updateMarkersLabel()
if (this.hasOnlyOneMarker) {
if (this.hasOnlyOneMarker && this.fitBounds) {
this.setFocusedPlace(this.localMapViewData.places[0])
}
if (this.mode === constants.modes.place && this.hasOnlyOneMarker && appConfig.showAdminAreaPolygon) {
Expand Down Expand Up @@ -1118,8 +1124,10 @@ export default {
let adminAreaLoader = new AdminAreaLoader()
let context = this
adminAreaLoader.getAdminAreaPolygon(place).then(polygons => {
context.localMapViewData.polygons = context.localMapViewData.polygons.concat(polygons)
context.fitFeaturesBounds(true)
if (Array.isArray(polygons) && polygons.length > 0) {
context.localMapViewData.polygons = context.localMapViewData.polygons.concat(polygons)
context.fitFeaturesBounds()
}
}).catch(err => {
console.log(err)
})
Expand Down Expand Up @@ -2029,7 +2037,6 @@ export default {

// Once the map component is mounted, load the map data
this.loadMapData()
this.setProviders()
this.setDrawingTool()
this.storeMapBoundsAndSetMapAsReady()
},
Expand All @@ -2043,6 +2050,7 @@ export default {
this.showClickPopups = this.showPopups
this.localAvoidPolygons = this.avoidPolygons
this.loadAvoidPolygons()
this.setProviders()
this.setMapCenter()
window.addEventListener('keyup', this.disablePickPlaceMode)
}
Expand Down
47 changes: 42 additions & 5 deletions src/pages/maps/maps.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import RouteUtils from '@/support/route-utils'
import appConfig from '@/config/app-config'
import constants from '@/resources/constants'
import { ResizeObserver } from 'vue-resize'
import Place from '@/models/place'
import lodash from 'lodash'

export default {
Expand Down Expand Up @@ -164,9 +165,11 @@ export default {
*
*/
fitMapBounds () {
if (this.mapViewData.timestamp && this.previousMapViewDataTimeStamp !== this.mapViewData.timestamp && this.$store.getters.mapSettings.alwaysFitBounds) {
if (this.mapViewData.timestamp
&& this.previousMapViewDataTimeStamp !== this.mapViewData.timestamp
&& this.$store.getters.mapSettings.alwaysFitBounds) {
return true
} else if (this.firstLoad) {
} else if (this.firstLoad && (this.$store.getters.mode !== constants.modes.place || !this.$store.getters.appRouteData.options.zoom)) {
return true
}
return false
Expand Down Expand Up @@ -240,7 +243,17 @@ export default {
zoomChanged (data) {
this.$root.appHooks.run('zoomChanged', data)
this.storeZoomValue(data.zoom)
this.searchBtnAvailable = true
// Set searchBtnAvailable as true if in search mode
this.searchBtnAvailable = this.$store.getters.mode === constants.modes.search

if (this.$store.getters.mode === constants.modes.place) {
const appMode = new AppMode(this.$store.getters.mode)
let places = this.getCurrentMapPlaces()
const route = appMode.getRoute(places, {zoom: data.zoom})
if (route.params && Object.keys(route.params).length > 0) { // params contains data and placeName? props
this.$router.push(route)
}
}
},
/**
* Set the refreshing search flag as true,
Expand All @@ -262,12 +275,33 @@ export default {
this.searchBtnAvailable = false
this.eventBus.$emit('refreshSearch')
},
/**
* Get current map place center
* @returns {Array} okf Places
*/
getCurrentMapPlaces () {
let places = this.$store.getters.appRouteData.places || []
if (places.length === 0) {
let centerPlace = new Place(this.$store.getters.mapCenter.lng, this.$store.getters.mapCenter.lat, 'null')
places.push(centerPlace)
}
return places
},
/**
* Defines if the searchBtnAvailable based on
* how much the map has been moved
* @param {Object} data
*/
mapCenterMoved (data) {
if (this.$store.getters.appRouteData.places.length === 0) {
const appMode = new AppMode(this.$store.getters.mode)
let places = this.getCurrentMapPlaces()
const route = appMode.getRoute(places, {zoom: data.zoom})
if (route.params && Object.keys(route.params).length > 0) { // params contains data and placeName? props
this.$router.push(route)
}
}

// Only enables the refresh search btn
// if the map has been moved more than 500 meters
if (data.distance > 500) {
Expand Down Expand Up @@ -424,9 +458,12 @@ export default {
if (appRouteData === false) { // if there is no app route data, load default state
this.$router.push({name: 'Maps'})
} else {
//TOCONTINUE here. When the zoom changes, the map moves a bit!
this.$store.commit('appRouteData', appRouteData)
this.storeZoomValue()
this.eventBus.$emit('appRouteDataChanged', appRouteData)
if (this.$route.name !== 'MapLocation') {
this.storeZoomValue()
this.eventBus.$emit('appRouteDataChanged', appRouteData)
}
}
}
},
Expand Down
15 changes: 13 additions & 2 deletions src/pages/maps/maps.route.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ const mapRoutes = [
// Go to the child route
next(currentPath)
} else {
// Otherwise load the root route
next()
// Otherwise load the map in the default or previous location
let zoom = store.getters.appRouteData.options.zoom || appConfig.initialZoomLevel
let mapLocation = `${placePath}@${store.getters.mapCenter.lng},${store.getters.mapCenter.lat},${zoom}`
next(mapLocation)
}
}
},
Expand All @@ -51,6 +53,15 @@ const mapRoutes = [
next()
}
},
{
path: `${placePath}@:coordinates`,
name: 'MapLocation',
component: Maps,
beforeEnter: (to, from, next) => {
store.commit('mode', constants.modes.place)
next()
}
},
{
// The maximum number of places/points of a route is 15. so, we can have 15 places more the data parameter that
// contains the coordinates for all the places in que query encoded in base64 format
Expand Down
18 changes: 11 additions & 7 deletions src/support/app-modes/app-mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
import OrsParamsParser from '@/support/map-data-services/ors-params-parser'
import OrsMapFilters from '@/config/ors-map-filters'
import AppLoader from '@/app-loader'
import utils from '@/support/utils'
import store from '@/store/store'
import lodash from 'lodash'

// Modes
import directionsMode from './strategies/directions-mode'
Expand All @@ -29,14 +31,14 @@ class AppMode {

/**
* Get a route object based on the places given and the app mode
* @param {*} places
* @param {Array} places
* @returns {Object} - {name: ..., params: ...}
*/
getRoute (places) {
const newAppRouteData = this.getAppRouteData(places)
getRoute (places = null, options = {}) {
places = places || store.getters.appRouteData.places
const newAppRouteData = this.getAppRouteData(places, this.getRouteOptions(options))
store.commit('appRouteData', newAppRouteData)
const options = this.getRouteOptions(newAppRouteData.options)
var route = this.targetMode.getRoute(newAppRouteData, options)
var route = this.targetMode.getRoute(newAppRouteData)
AppLoader.getInstance().appHooks.run('appModeRouteReady', route)
return route
}
Expand All @@ -46,13 +48,14 @@ class AppMode {
* @param {*} places
* @returns {AppRouteData} newAppRouteData
*/
getAppRouteData (places) {
getAppRouteData (places, options = {}) {
// We are about to build a new appRouteData using the passed places
// and the values stored in the @see ors-map-filter object in memory.
// We have identified that in this case we should not use the previous
// options in the old appRouteData and pass it as a second parameter
// for the method below
const newAppRouteData = this.targetMode.buildAppRouteData(places)
const newAppRouteData = this.targetMode.buildAppRouteData(places, options)
newAppRouteData.options = lodash(newAppRouteData.options).omitBy(lodash.isUndefined).omitBy(lodash.isNull).value()
return newAppRouteData
}

Expand All @@ -75,6 +78,7 @@ class AppMode {
* @returns {*} options
*/
getRouteOptions = (options) => {
options = utils.merge(store.getters.appRouteData.options, options)
AppLoader.getInstance().appHooks.run('afterGetRouteOptions', options)
return options
}
Expand Down
3 changes: 2 additions & 1 deletion src/support/app-modes/strategies/directions-mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ class DirectionsMode {
* @param {*} appRouteData
* @returns {Object} route like {name: 'MapDirections', params: {...} }
*/
getRoute = (appRouteData, options) => {
getRoute = (appRouteData, options = null) => {
options = options || appRouteData.options
// Get only the valid params for directions
const validOptions = {}
const validParams = ['profile', 'preference']
Expand Down
3 changes: 2 additions & 1 deletion src/support/app-modes/strategies/isochrones-mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class IsochronesMode {
* @param {*} appRouteData
* @returns {Object} route like {name: 'MapDirections', params: {...} }
*/
getRoute = (appRouteData, options) => {
getRoute = (appRouteData, options = null) => {
options = options || appRouteData.options
const params = RouteUtils.buildRouteParams(appRouteData, options)
// Build the route object
const route = { name: 'MapIsochrones', params: params }
Expand Down
54 changes: 36 additions & 18 deletions src/support/app-modes/strategies/place-mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import store from '@/store/store'
*/
class PlaceMode {
// eslint-disable-next-line no-unused-vars
buildAppRouteData (places, options = {}) {
buildAppRouteData (places) {
const appRouteData = store.getters.appRouteData || new AppRouteData()
appRouteData.places = places

Expand All @@ -24,7 +24,9 @@ class PlaceMode {
appRouteData.options.country = appRouteData.places[0].properties.country || appRouteData.options.country

// Update the zoom according to the place type/layer
appRouteData.options.zoom = GeoUtils.zoomLevelByLayer(appRouteData.options.layer)
if (!appRouteData.options.zoom) {
appRouteData.options.zoom = GeoUtils.zoomLevelByLayer(appRouteData.options.layer)
}
}
return appRouteData
}
Expand All @@ -35,21 +37,30 @@ class PlaceMode {
* @param {*} options
* @returns {Object} route like {name: 'MapDirections', params: {...} }
*/
getRoute = (appRouteData, options) => {
getRoute = (appRouteData) => {
if (appRouteData.places.length > 0) {
const place = appRouteData.places[0]
const name = place.placeName ? place.placeName.replace(/, /g, ',') : ''
const name = place.placeName ? place.placeName.replace(/, /g, ',') : ''

// Transform the coordinates into a comma separated value (easier to put in the url)
const lngLatStr = place.isEmpty() ? '' : `${place.lng},${place.lat}`

options = JSON.stringify(options)

const data = store.getters.mapSettings.compressDataUrlSegment ? utils.compressTxt(options) : options
var lngLatStr = place.isEmpty() ? '' : `${place.lng},${place.lat}`
if (appRouteData.options.zoom) {
lngLatStr = `${lngLatStr},${appRouteData.options.zoom}`
}



// Create the route object
const params = { placeName: name, coordinates: lngLatStr, data: data }
const route = { name: 'MapPlace', params: params }
const params = { coordinates: lngLatStr}
let route = { name: 'MapLocation' }

if (name && name !== 'null' && Object.keys(appRouteData.options).length > 1) {
params.placeName= name
route.name = 'MapPlace'
let options = JSON.stringify(appRouteData.options)
params.data = store.getters.mapSettings.compressDataUrlSegment ? utils.compressTxt(options) : options
}
route.params = params
return route
} else {
store.commit('cleanMap', true)
Expand All @@ -71,15 +82,22 @@ class PlaceMode {
}

if (currentRoute.params.coordinates) {
const lnglat = currentRoute.params.coordinates.split(',')
const coordinates = currentRoute.params.coordinates.split(',')
appRouteData.options.center = GeoUtils.buildLatLong(coordinates[1], coordinates[0])
if (coordinates.length > 2) {
appRouteData.options.zoom = Number(coordinates[2])
}

// Get and format the place name
const placeName = currentRoute.params.placeName.replace(/, /g, ',').replace(',', ', ')
// Recreate the place object
const place = new Place(lnglat[0], lnglat[1], placeName, { properties: data })

// Add the single place to the route data
appRouteData.places.push(place)
if (currentRoute.params.placeName) {
const placeName = currentRoute.params.placeName.replace(/, /g, ',').replace(',', ', ')
if (placeName && placeName !== 'null') {
// Recreate the place object
const place = new Place(coordinates[0], coordinates[1], placeName, { properties: data })
// Add the single place to the route data
appRouteData.places.push(place)
}
}
}
// Return the object
AppLoader.getInstance().appHooks.run('afterPlacePathDecoded', appRouteData)
Expand Down
3 changes: 2 additions & 1 deletion src/support/app-modes/strategies/roundtrip-mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class RoundTripMode {
* @param {*} appRouteData
* @returns {Object} route like {name: 'MapDirections', params: {...} }
*/
getRoute = (appRouteData, options) => {
getRoute = (appRouteData, options = null) => {
options = options || appRouteData.options
const params = RouteUtils.buildRouteParams(appRouteData, options)
// Build and return the route object
const route = { name: 'MapDirections', params: params }
Expand Down

0 comments on commit 02949e1

Please sign in to comment.