sMap-responsive is a software framework for web maps built with Leaflet and Bootstrap. The purpose of the framework is to facilitate creation of maps which support a range of different browsers and devices (specified in the wiki).
Quick install with npm:
npm install --save smap-responsive
The framework has been developed by Johan Lahti together with valuable contributors.
First. This framework is based around Leaflet controls (plugins). The controls are reusable in any other Leaflet-based framework. You can pick Leaflet controls from the Leaflet plugin site or vice versa – pick controls from here and use in your own Leaflet based framework (with few or no modifications needed).
Second. This framework is built to be dynamic and serve multiple objectives in various contexts. Smap-responsive is used in many different types of external applications as well as for stand-alone maps. Although every map is unique, most functionality can be re-used for every map.
Third. In order to minimise the need of programmers' hands-on – we've put a lot of effort into making this framework as easy to use as possible for the administrator. New maps – from the most advanced to the simplest of simple – can be created by just copying and modifying one configuration file. This gives the administrator a lot of power and flexibility – without doing any programming.
Basic chapters:
Advanced chapters:
First, make sure you have the following applications installed.
The easy way: Install npm and execute this command:
npm install --save smap-responsive
inside a directory of your choice (locate the folder node_modules/smap-responsive)
If you are using a Mac or Linux computer, you may need to use sudo
before some of the commands.
Clone the project using Git or Subversion (you can also use a GUI version of the mentioned):
git clone https://github.com/getsmap/smap-responsive
or
svn co https://github.com/getsmap/smap-responsive/trunk smap-responsive
Install dependencies
cd smap-responsive
npm install
bower install
Build the application. Run this from the root directory of your smap clone.
gulp full
If the build went fine, point your browser to http://localhost/smap-responsive/dev.html – or wherever your clone is located.
If you want to use a minimised version of the application, just point your browser to http://localhost/smap-responsive/dist/index.html instead.
The dist
folder contains the whole application but everything is minimised and compressed. While debugging, it is better to use dev.html.
The default language is Swedish (language code "sv"). If you want to change this to English, then edit the file
core/js/mainConfig.js
and change the variable todefaultLanguage: "en"
.
Now you are finally ready to create some maps!
myconfig.js
.The configuration file informs the application of:
Inside the example configuration file (the one you just copied) is described how each and every parameter affects the application.
Next. We need to specify which configuration file to use. This is accomplished with the URL parameter config
:
// Using our newly created configuration file myconfig.js
http://localhost/smap-responsive/dev.html?config=examples/configs/myconfig.js
// Or, using compressed code:
http://localhost/smap-responsive/dist/index.html?config=../examples/configs/myconfig.js
Note that the path to the config file is relative to the html file.
Next step is to change the application's behaviour using URL parameters.
The application can be called with various URL parameters. For instance, http://localhost/smap?config=another_config.js&zoom=12 will load the config file another_config.js
and set the zoom to 12 (higher means more zoomed in).
Parameter key | Parameter value(s) | Decides | Example value |
---|---|---|---|
config | {String} | Config to use | my_config.js or somefolder/my_config.js or http://someserver.com/folder/my_config.js |
zoom | {Integer} range: 0-18 | Starting zoom level | zoom=12 (higher=more zoomed in) |
center | {Integer},{Integer} | Starting center | 13.1,55.6 |
bl | {String} | Starting baselayer | bl=citymap - the baselayer with given layerId will be active from start |
ol | {String}, … | Starting overlay(s) | ol=some_points,some_data (layerId for two layers) |
xy | {Integer},{Integer},{Optional String},{Optional String} | Adds a marker | 13.1,55.6 or 13.1,55.6,Text%20in%20popup or xy=117541,6163401,Projected coords,EPSG:3008 |
lang | {String} | Sets language | lang=en (for English) |
Parameter key | Parameter value(s) | Decides | Example value | Default value |
---|---|---|---|---|
geojson | Takes 3 parameters (see below) | This allows you to render vector data on the fly (just be aware of CORS). Being a hash parameter it won't reload the map if the value changes. | - | - |
(param 1) | {String} | Encoded URL pointing to a GeoJSON source | "http://some.geosjon/source.json?" (but should be encoded using encodeURIComponent() | null (required) |
(param 2) | {Boolean} | Cluster features | true | false |
(param 3) | {Boolean} | Zoom to extent of features | true | false |
Examples
This will show some features residing in a geojson file (the path is encoded):
?config=myconfig.js#geojson=my%2Fgeojson.json%3F,false,false
This will make the map zoom to the features' extent, after the features have loaded:
?config=myconfig.js#geojson=my%2Fgeojson.json%3F,false,true
This will cluster the features:
?config=myconfig.js#geojson=my%2Fgeojson.json%3F,true,false
Parameter key | Parameter value(s) | Decides | Example value | Plugin |
---|---|---|---|---|
poi | {String},{Optional Integer} | Triggers geolocate for given address | poi=Storgatan%201,1 (open popup) poi=Storgatan%201 (no popup) | L.Control.Search |
lsw | {Integer} | Open switcher from start | lsw=1 opens switcher from start (only small screens) | L.Control.LayerSwitcher |
md | {String} | Features to draw | - (created internally) | L.Control.MeasureDraw |
As seen in the example configuration file plugins, any Leaflet control can be included in the map simply by adding its constructor and its options to the plugins array. All options will be transferred to the plugin when it is instanstiated (the pre-assumption is that all controls follow the same syntax pattern, taking only one parameter which is options
).
However, first you need to incorporate the plugin into the code. Follow these steps:
MyPlugin.js
and MyPlugin.css
inside a folder named MyPlugin
. This folder should be placed inside the plugins-folder of your smap clone.gulp full
Developing a plugin for smap-responsive is no different than developing an ordinary Leaflet control, which you can learn better from other sources. However, smap-responsive has some special additions which can be useful when you want to interact with core funcionality. For instance:
Use the plugin template when developing a new plugin. The only addition to an ordinary Leaflet plugin is the _lang
object which allows you to adapt labels and stuff depending on language. If you don't need it, just ignore it.
Note! While developing a plugin you can execute gulp without any parameter:
gulp
. It will then automatically compile .styl and .sass files into CSS whenever you save something. Check thegulpfile.js
and learn more about how to modify it for your needs.
This section describes:
Event name | Triggered… | Example |
---|---|---|
smap.core.createparams | …when URL params are created. Useful if your plugin needs to add something to the URL. | smap.event.on("smap.core.createparams", function(e, paramsObject) { paramsObject.new_param = 3; }); |
smap.core.aftercreateparams | …after createparams have been triggered. Useful e.g. if you have a param dependency for your own param(s). | smap.event.on("smap.core.createparams", function(e, paramsObject) { paramsObject.new_param = 3; }); |
smap.core.beforeapplyparams | …before URL params are applied in core | smap.event.on("smap.core.beforeapplyparams", function(e, paramsObj) { alert(paramsObj.MY_PARAM); }); |
smap.core.applyparams | …after URL params have been applied in core | – " " – |
smap.core.pluginsadded | …when all plugins have been added. Useful if you need another plugin to be added before you do something. | smap.event.on("smap.core.pluginsadded", function() {}); |
selected | …when a feature is selected (through getfeatureinfo or vector select) | smap.map.on("selected", function(e) { /* e.layer, e.feature, e.latLng, e.selectedFeatures */}); |
unselected | …when a feature is unselected | smap.map.on("unselected", function(e) {}); |
Note! Check the file where the method exists for a more detailed description of the parameters below.
Method | Params | Description | Example |
---|---|---|---|
smap.cmd.createParams | addRoot {Boolean} | Create URL params that recreates the map | var s = smap.cmd.createParams(true); |
smap.cmd.createParamsAsObject | - | Same as previous but params as object | var obj = smap.cmd.createParamsAsObject(); |
smap.cmd.getControl | controlName {String} | Get control with given name OR full class name | var search = smap.cmd.getControl("Search"); |
smap.cmd.getControls | controlName {String} | Same as above but to be used if there are many instances | var arr = smap.cmd.getControls("RedirectClick"); |
smap.cmd.notify | text {String}, msgType {String}, options {Object} | Alert the user about something | smap.cmd.notify("An error", "error"); |
smap.cmd.addLayerWithConfig | layerConfig {Object} | Same method used by core when adding layers from config file | – |
smap.cmd.getLayer | layerId {String} | Fetch an already added layer | var layer = smap.cmd.getLayer("some_layer_id"); |
smap.cmd.getLayerConfig | layerId {String} | Fetch configuration for a specific layer from config file | var t = smap.cmd.getLayerConfig("some_layer_id"); |
smap.cmd.getLayerConfigBy | key {String}, val {String}, options {Object} | Same as previous, but based on different key and val | smap.cmd.getLayerConfigBy("options.displayName", "Districts of Malmö"); |
smap.cmd.getLang | - | Get the currently set language | var lang = smap.cmd.getLang(); // e.g. "en" |
smap.cmd.reloadCore | options {Object} | Reload the map without reloading the browser | smap.cmd.reloadCore({}); |
smap.cmd.loading | show {Boolean}, options {Object} | Show/hide the loading indicator (set show to false to hide) |
smap.cmd.loading(true, {text: "Fetching data"}); |
Method | Params | Description | Example |
---|---|---|---|
utils.rmPx | text {String} | Converts a CSS "size" given as text into an integer | utils.rmPx("10px") // -> 10 |
utils.capitalize | theString {String} | Converts string into upper-case | utils.capitalize("Hey there") // -> "HEY THERE" |
utils.getBrowser | - | Get current browser (so far only IE versions) | utils.getBrowser().ie8; // {Boolean} |
utils.urlAppend | baseUrl {String}, params {String}, separator {String} | Merges a URL with params | utils.urlAppend("http://myurl/folder/, "param=1&hey=2, "?"); |
utils.isInIframe | - | Check whether this app runs inside an iframe | utils.isInIframe() // {Boolean} |
utils.makeUniqueArr | arr {String} | Removes all duplicates from array | utils.makeUniqueArr([1,1,2,2,2]); // -> [1,2] |
utils.objectToUpperCase | o {Object} | Converts all keys in the object into upper-case | utils.objectToUpperCase({test: "hey"}); // -> {"TEST": "hey"} |
utils.drawDialog | title {String}, bodyContent {String or jQuery Object}, footerContent{String or jQuery Object}, options {Object} | Returns a Bootstrap modal | utils.drawDialog("Title", " Body ", '').modal("show"); |
utils.getLayerFromFeature | feature {unknown}, layer {unknown} | Return the parent layer of a feature | utils.getLayerFromFeature(f, layer); |
utils.round | val {Number}, nbrOfDecimals {Integer} | Rounds to number of decimals | utils.round(4.87123, 1); // -> 4.9 |
utils.makeUnselectable | tag {jQuery tag} | Make HTML elements in (old) IE unselectable | utils.makeUnselectable($("body")); |
utils.extractToHtml | html {String}, props {Object} | Replaces all ${key} with the corresponding value found in props | utils.extractToHtml(" ${some_key} ", {"some_key": "Hey"}); // ->Hey |
utils.createLabel | center {L.LatLng}, html {String}, className {String} | Returns a map label | utils.createLabel([13,55.6], "I am a label", "label-nbr-1"); |
utils.getLength | arrLatLng {Array({L.LatLng},{…})} | Returns total true (curved) distance for the line | utils.getLength([L.latLng(13, 55), L.latLng(14, 67)]) |
utils.paramsStringToObject | pString {String}, keysToUpperCase {Boolean} | Converts a parameter string into an object | utils.paramsStringToObject("a=2&b=3", true); // -> {"A":2, "B":3} |
utils.projectPoint | east {Number}, north {Number}, srsSrc {String}, srsDest {String} | Convert coords from one projection to another | utils.projectPoint(13,56,"EPSG:4326","EPSG:3008"); // -> [118803, 6208675] |
utils.projectLatLng | latLng, srsSrc, srsDest, reverseAxisSrc, reverseAxisDest | Similar as previous but for L.LatLng | utils.projectLatLng(L.latLng([13,56]), "EPSG:4326", "EPSG:3008", false, true); |
utils.projectFeature | feature {GeoJSON Object}, inputCrs {String}, options {Object} | Convert entire GeoJSON object into another projection (used by WFS layer class) | See L.GeoJSON.WFS class for example usage |
sMap as a product consists of source code developed by the contributors of this repository. This code is released under the AGPL-v3 license.
The product uses a number of third-party libraries, such as Leaflet and Bootstrap etc. These are not included in the sMap product. The copyright of these libraries belongs to their respective authors and are protected by their own license.
The sMap package (i.e. the contents of this repository) consists of the sMap product on one hand, and the required third-party libraries on the other.
Data sources – whether belonging to the contributors' organisation, or to anyone else – are not included, neither in the product, nor in the package. You need to seek permission from the publisher and/or from the copyright owner to use these data. This also applies if the data is linked from any other code.
If you are using sMap-responsive to make your own map, or if you change it, we would more than happy if you would like to share it. Although not actively developed, feel free to raise issues or contribute.
We are happy for any type of feedback.
For questions, please contact us.