This application implements a map client for the openrouteservice API as Single Page Application (SPA). It is a base application that can be used for multiple purposes, customized via configurarions and extended via plug-ins.
The base application is built using VueJS, Vuetify and a set of custom components, directives and services. The structure uses a feature-by-folder design, allowing view, code and translation elements to be contained in a folder.
This app uses single file components and others non-native javascript code that are transpiled to native javascript during the build process. That is way the app needs to be compiled before running it either in dev or production mode. The VueJS components allow a better code organization, weak and clear coupling between components and an easier code understanding.
- Set up and run locally
- App folders
- App flow overview
- Feature-by-folder design
- Reserved methods and accessors
- pages
- Configuration, theming, customization and extension
- Add language
- Menu
- Debug
- Build and deploy
- Contribute
Run the app locally in three steps: set the environment up, get the code and define a configuration file.
- To manage dependencies and pack the app it is necessary to have Node version 12. If you already have it, skip this step. If you don't, please install it by running:
curl -sL https://deb.nodesource.com/setup_12.x | bash - && \
apt-get update && \
apt-get install -y nodejs && \
npm install -g npm && \
npm update -g
- Clone the repository of the ORS Map Client, go to the root folder and install the dependencies:
git clone https://github.com/GIScience/ors-map-client.git
# Go to your local repository root folder
cd ors-map-client
# The installation of dependencies is required before running the app:
npm install
- Copy fo the files in the
src/config-example
tosrc/config
, without-example
in their names. The files are:
- app-config-
example
.js => app-config.js - ors-map-filters
-example
.js => ors-map-filters.js - layer-zoom-mapping
-example
.js => layer-zoom-mapping.js - hooks
-example
.js => hooks.js - theme
-example
.js => theme.js - default-map-settings
-example
.js => default-map-settings.js - settings-options
-example
.js => settings-options.js
- Set the app-config.js values for:
orsApiKey
- ORS API key to be used when ot running the app from localhost or ors valid domainsbitlyApiKey
- the bitly key that will be used to shorten the share URLbitlyLogin
- the bitly login that will be used to shorten the share URL
- The ORS menu is loaded/used by default. If you want to use a custom menu, have a look in the hooks-example.js
The map client filters, the theme and the hooks can be customized, if you need.
At this point the app is ready to run in dev
mode. Do it by executing the following command in the app root folder:
npm run dev
# This will start a standalone http node server and the host and port to access it will be displayed
App folder structure under src
:
assets
- where the images and icon sare storedcommon
- available mixins, http client, main menu, Vue with Vuetify and themedirectives
- custom directivesfilters
- custom filtersfragments
- all app fragments used by pagesi18n
- internationalization resourcesmodels
- models used to deal transport ors response data and deals with app state and transitionspages
- app pages. currently only themaps
page is availableresoures
- where support files like the loader lib, definitions and options used to process requests, responses are placedrouter
- router component based on vue-routershared-services
- shared services that can be used by various componentsstore
- app store definitionssupport
- support utilities, like geo-utils, ors-api-runner, routes-resolver and app modes
This is a Single Page Application (SPA). This means that the client app is loaded on the browser and defines which components and pages are processed based on the browser URL. The app watches every change in the browser URL and updates its state based on that. These URL changes don't trigger a request to the back-end directly, but the components loaded/updated will decide which RESTfull requests must be run to load the required data. In other words, it means that the front-end (this app) is decoupled from the back-ends (ORS API and ORS website)
The app load cycle follows these steps:
- Execute the
main.js
file and add global extensions, mixins components and external libs. - The registered hooks are loaded and the
appLoaded
hook is run. - The
main.js
also includes the main router script, the main vuex store and the main i18n file, that will internally, each one, load all the additional.router.js
files,.store.js
files and.i18n.js
files. main.js
file will create a VueJS app instance and load theApp.vue
. At this pointAppHooks
is set up and attached to the main VueJS instance and the hookappLoaded
is run.App.vue
includes all basic navigation components, like menu, sidebar, footer and etc.- As soon as all the routes are loaded, including the ones in the
pages
sub folder, the page with the/
route will also be rendered in the<router-view></router-view>
inApp.vue
component.
Data flow, state and requests to services, in a simplified view, happens as follows:
- The app is loaded
- the API data are fetched from ORS website service and if
appConfig.appMenu.useORSMenu
is true, the menu items are loaded insrc/main.js
usingsrc/app-loader.js
. - the app
mode
is defined based on the matching URL in themaps.route.js
- the
maps
page, uses the app mode utility to define the app state using the currentmode
. This utility will also populate the values of theors-map-filters
based on the URL and build theAppRouteData
(in src/models/app-route-data.js). - based on the app mode/state certain components are activated/displayed
- Every component, once activated, may use the data in
src/config/ors-map-filters
to render its elements and may run requests to the ORS api using thesrc/support/ors-api-runner
. Once the request succeed, the response data will be used to fill theMapViewData
object. - Once an input is changed the app goes to a new URL and this makes the flow restart at the step 2.
- If a component changes the
MapViewData
model, it emits an event to themaps
page, that passes the currentMapViewData
object to theMapView
component. - Interactions via
MapView
may result in events sent back tomaps
page, that may notify other child components, that in their turn may change the URL and trigger the step 2 again. - Several app hooks are called during the app flow and and it is possible to listen to these hooks and run custom code to modify some of the app behavior. The available hooks are listed in
src/config/hook-example.js
and must be coded insrc/config/hooks.js
.
- the API data are fetched from ORS website service and if
This app uses feature by folder components and predefined folders where the business code should be placed in. Example of this design usage:
Page:
- my-page-name (folder)
- MyPageName.vue (main VueJS component file)
- my-page-name.css (styles for the page, included by the MyPageName.vue component)
- my-page-name
.store.js
(Vuex store module for the page, included by the store/store.js loader) - my-page-name
.route.js
(route to reach this page, included by the router/index loader) - i18n (folder)
- my-page-name
.i18n.en.js
(in this example containing the EN resources for the page)
- my-page-name
Component:
-
my-fragment-name (folder under
src/fragments/
)- MyFragmentName.vue (main VueJS component file)
- my-fragment-name.css (styles for the page, included by the MyFragmentName.vue component)
- my-fragment-name
.store.js
(Vuex store module for the fragment, included by the store/store.js loader) - i18n (folder)
- my-fragment-name
.i18n.en.js
(in this example containing the EN resources for the component)
- my-fragment-name
The app will automatically load:
- all the locale resources in i18n folder ending with
.i18n.*.js
where*
is the locale - the defined routes in files ending with
.routes.js
- the store definitions in files ending with
.store.js
All the VueJS components created (including the fragments) will have, by default, the following methods/accessors define din the main vue instance app:
-
showMessage (msg, theme, options)
- shows a message using the toaster with specified theme and options -
showError (msg, options)
- shows an error message using the toaster with the specified options -
showWarning (msg, options)
- shows a warning message using the toaster with the specified options -
showInfo (msg, options)
- shows an info message using the toaster with the specified options -
showSuccess (msg, options)
- shows a success message using the toaster with the specified options -
confirmDialog (title, text, options)
- shows a confirm dialog with the specified title, text and options and returns a promise. If the user clicksyes
, the promise will be resolved, if s/he clicks onno
, the promise will be rejected. -
eventBus
- accessor to global event bus object, that allows to broadcast and get events in all components -
$store
- accessor to app store that used vuex
maps
- the page where the user can search places, routes and create isochrones.
The map client app can be configured, customized and extended. Several aspects can be defined/changed in order to disable features, customize visual identity and change/extend its features/behaviors. It is also possible to add plug-ins to the app and subscribe to hooks, listen and emit evens that will make the app respond to specific needs. The items of the menu can also be customized via hooks.
It is possible to define your custom settings and plug-ins and keep getting updates from the ORS repository because the src/plugins
and src/config
folders are not vesioned. To keep the original ors-map-client as a secondary repository, do the following:
# Rename the original remote
git remote rename origin ors-map-client-origin
# Add your remote as the origin one
git remote add origin <git-repo-url>
# Set your new origin as the default upstream
git branch --set-upstream-to=origin/master
After doing this we recommend you to remove from .gitignore
the lines that tell git to ignore the folders /src/config
, src/plugins
and eventually /static
. Then run the initial push to the just defined new origin repository, with the following command:
git push -u origin master
The ways to change/extend the app are:
- Define custom settings (see files in
src/config
) that will change the standard way that the app works. - Add hook listeners in
src/config/hooks.js
and run custom code inside those hooks - Create a plug-in that will have its methods linked to hooks called during the app flow (see
src/plugins/example-plugin/
)
It is possible to configure/disable some app features and behaviors by changing the values
of the src/config/app-config.js
. It is also possible to change the app theme/colors by changing the values of src/config/theme.js
. The app logo can also be changed in the src/config/app-config
file. The available filters/options to be used in the services are defined in the src/config/ors-map-filters.js
. They can be adjusted according the needs. Other files can be used to adjust app configurations are the layer-zoom-mapping.js
, settings-options.js
and the default-map-settings.js
.
It is possible to add plug-ins to the application in order to change its behavior or extend it.
Please check the src/plugins
folder, the plug-ins readme and the plugin example for more details.
Follow the steps below to add a language.
-
Create a copy of the /src/i18n/translations/
en-us
folder giving it the identification of the target language. For example: if you are adding the French from France, then the folder should be calledfr-fr
. -
Edit the builder.js file inside the just created folder in order to replace the language pattern to the one you are creating. For example, where is /.i18n.
en-us\
.js$, put /.i18n.fr-fr
.js$ -
Translate the language strings for each key in the
global.js
file -
Search for each file inside the
/src
folder that ends withi18n.en-en.js
and create a copy of it and each one so that each new created file now ends withi18n.fr-fr.js
-
Translate the language strings for each key in all the files created in the previous step.
-
Edit the /src/config/
settings-options.js
and add the new locale object to theappLocales
array. For this example, you would add { text: 'Français FR', value: 'fr-fr' } -
Open the src/i18n/
i18n-builder.js
file and apply the following changes:-
Import the object from the new language builder that you just created. For this example it would be
import frFRTranslations from './translations/fr-fr/builder
' -
Inside the
build
method, add the new language placeholder object to the messages object. For this example you would add, 'fr-fr': {}
-
Still inside the
build
method, add the result of the new language building to the previously created message object. In this example you would add:i18n.messages['fr-fr'] = frFRTranslations.build()
-
Save all the files changed and rebuild the application.
-
The menu displayed in the header and in the sidebar (low resolution and mobile devices) is loaded from the ORS website back-end and adjusted to be shown according the resolution.
The menu items are fetched on the app load (src/app-loader.js
). It dispatches the store fetchMainMenu
and the menu is retrieved by @/common/main-menu.js
that internally uses @/support/menu-manager.js
and @/support/model-service.js
.
Once the items from the back-end are loaded, MenuManager
is used to add/remove custom items and define sidebar item icons. The items displayed on the menu can be changed by running custom code on the loadMenuItems
and/or the modifyMenu
hooks. Check the /src/config-example/hooks-example.js
to see more details.
If you are using WebStorm you should set the
webpack config (settings -> Languages & Frameworks -> JavaScript -> Webpack) to {path to ors-map-client}/build/webpack.base.conf.js
so the file paths are resolved correctly.
To debug the application you must run it in dev
mode.
For better debugging in your browser install the VueJS devtools extension.
After doing that, open the application in the browser and press F12 and select the tab Console
, Vue
or Sources
(and then expand e.g.: webpack://src
).
The app must be built before it is deployed. To do so, run:
cd <project-root-folder>/
npm run build
Important: to run the built application you must to set up a web server and put this repository (after the build) there. The index.html
at the root of this repository is expected to load the app.
For a detailed explanation on how webpack works, check out the guide and docs for vue-loader.
The develop
branch is used as the working branch. Anything new goes to develop and then it is tested, committed and finally merged into master
. So, develop has always the latest version (latest but not necessarily the production one) while master
has the production version. Considering this, any merge request must be tone targeting develop
. If you want to work on a feature we recommend you to create a feature branch then when it is finished send a merge request to develop
.
Like almost every team, we have limited workforce and we have to define priorities.
Bugs
:
If you have identified any bug and think that you can help fixing it, please create an issue first, instead of directly submitting a push request. So the people involved will have the opportunity to discuss it.
New festures
:
If you want to contribute by adding a new feature or improve an existing one, please also create an issue. We do want contributions and the community effort is very important to us, but features may add complexity and future maintenance effort. Because of this, we have also to analyze the trade off of such contributions. We just have to decide about them together before the hands on. This approach is intended to create cohesion and keep the project sustainable.
As you may notice, this project is an on going project and thus, there is a lot of room for improvement. We already have in mind some of these possible improvements and some of them are listed below:
Tests
(unit, e2e) - we need it, but we were not able to implement it yet. It is one of the priorities of our list. So, if you can contribute with it, please let us know.Rendering performance
- we are continuously looking for performance improvement. If you think you can suggest a better way to deal with the rendering phase in a way that it improves the speed, please tell us. We are relying on Vue2leaflet for this.Accessibility
of the app for people with special needsUser test
to check that the app works in multiple browsers, resolutions and devices (versions of Mac, Windows, Linux, Android and Iphone) and their multiple possible browsers