@wq/map
======

[@wq/map]

**@wq/map** is a [@wq/app plugin] that adds mapping capabilities.  @wq/map can leverage the [wq configuration object] to generate [Leaflet] maps for pages rendered via @wq/app.  The generated maps can automatically download and display GeoJSON data rendered by [wq.db]'s [REST API].

<div data-interactive id='map-example'>
  <div id='doc-map-js-map' style='height:300px;background:#ccc;border:1px solid black'></div>
</div>


## Overview

As a plugin for [@wq/app], @wq/map utilizes similar concepts and conventions, with corresponding constraints on how it can be used.  In particular, each map generated by @wq/map always corresponds to a page defined in the [wq configuration object] with a `"map"` property defined.  For model-backed pages (`"list": true`), @wq/map' maps can distinguish between "list" views, "detail" views, and "edit" views.

@wq/map leverages three configuration objects:

 * Global *defaults*, such as basemaps and initial geographic bounds.  See Global Configuration below for options.
 * *Map configurations*, which each correspond to a page configuration.  See Map Configuration below for options.
 * *Layer configurations*, which describe individual overlay layers in a map configuration.  See Layer Configuration below for options.

A default map configuration can be assigned by setting `map: true` in the page configuration.  Each map will automatically get a single overlay configured with the GeoJSON equivalent of the page:

  * The default layer for a simple page (e.g `/simple`)  will be `/simple.geojson`
  * The default layer for a list page's list view (e.g. `/list/`) will be `/list.geojson`
  * The default layer for a list page's detail view (e.g. `/list/itemid`) will be `/list/itemid.geojson`
  * The default layer for a list page's edit view (e.g. `/list/itemid/edit`) will be `/list/itemid/edit.geojson`

> For example, the content you are reading is a `Doc` instance with an identifier of `map-js`, rendered in a "detail" view for the `docs` list page.  `docs` is registered in the <a href="https://wq.io/config.json" rel="external">wq configuration for this website</a> as `"map": true`.  Since @wq/map is loaded for this website, the map at the top of this document should have automatically loaded <a href="https://wq.io/docs/map-js.geojson" rel="external">/docs/map-js.geojson</a> when the page opened.

The default GeoJSON layers should work as long as your webserver is running [wq.db] or a service with a compatible [URL Structure].

## Configuration

The map plugin requires both a global configuration and a per-page configuration for pages that need maps.

```javascript
// myapp/main.js
define(['wq/app', 'wq/map', './config'],
function(app, map, config) {

// In myapp/config.js or in wq.db.rest registration:
// config.map = { ... }
// config.pages[page].map = { ... }

app.use(map);

app.init(config).then(function() {
    app.jqmInit();
    app.prefetchAll();
});

});
```

The map module provides the following configuration options.

### Global Configuration

`map.init()` is called automatically by `app.init()` with the contents of `config.map`.  The configuration object default settings for rendered maps including initial bounds.

name | default | purpose
-----|---------|---------
`bounds` | `[[-4,-4],[4,4]]` | Default extent for initially rendered map.  This is specified as a bounds rather than a center and zoom, to ensure the full intended extent is visible regardless of screen size.
`autoZoom` | Object | By default, rendered maps will automatically zoom (and pan) to the extent of their embedded GeoJSON feature layers using the following options.  To disable auto-zooming entirely, set `autoZoom` to `false`.
`autoZoom.animate` | `true` | Whether to animate the auto-zooming.  Incorporating animation is valuable as it gives the user a chance to visually orient the rendered features in relation to the original zoom level.
`autoZoom.wait` | `0.5` | How long to wait before triggering auto-zooming, in seconds.  Waiting gives the map a chance to settle and makes the animation more salient.
`autoZoom.sticky` | `true` | Whether to save the last zoom and center (from auto-zooming and/or regular panning) for use in the next map (`true`) or to always start out new maps from the default zoom and center (`false`).  Particularly useful in maintaining visual consistency for the user when they are quickly navigating between a series of list or detail pages in succession.
`autoZoom.maxZoom` | `13` | The maximum zoom level to use when auto-zooming.  (Useful to avoid zooming in too far when the only feature is a single point)
`icon` | Object | Default icon settings for use with `map.createIcon()`.  The "default" default icon settings correspond to the default icon created by Leaflet (`L.Icon.Default`).
`basemaps` | Array | Basemap configuration to use on every generated map.  The `name` attribute will be used as the basemap name in the layers control, while the `type` specifies the name of a layer creation function to use when creating the basemap.  All other options will be passed to the layer creation function.  One basemap type is preregistered: `tile` (which corresponds to `L.tileLayer()`).

#### Customizing the Basemap
The default basemap configuration uses the free [Stamen Terrain] layer:

```javascript
config.map.basemaps = [
    {
        'name': "Stamen Terrain",
        'type': 'tile',
        'url': '//stamen-tiles-{s}.a.ssl.fastly.net/{layer}/{z}/{x}/{y}.jpg',
        'layer': 'terrain',
        'attribution': 'Map tiles by Stamen Design ...'
    }
];
```

If you would like to change the basemap (for example, to incorporate aerial imagery), you will need to customize this configuration to incorporate map tiles from another source.  There are a number of services available (some free for small projects), but nearly all now require an API key.  For example, after obtaining an API key from [MapBox], you might do something like this:

```javascript
var attrib = 'Map data ...',
    token = '(insert API key)',
    cdn = 'https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}';

config.map = {
    'basemaps': [{
        'name': 'MapBox Streets',
        'type': 'tile',
        'url': cdn,
        'id': 'mapbox.streets',
        'accessToken': token,
        'attribution': attrib
    }, {
        'name': 'MapBox Satellite',
        'type': 'tile',
        'url': cdn,
        'id': 'mapbox.satellite',
        'accessToken': token,
        'attribution': attrib
    }]
};
```

If you want to keep your API token out of your version control, you can put it in a separate unversioned AMD module and/or include it in your `local_settings.py` and register it with your router (as in the [code for this website]).

If you have an ArcGIS license, you can integrate ESRI basemaps by loading `@wq/map/mapserv` rather than `@wq/map`.  `@wq/map/mapserv` is a variant of `@wq/map` with additional basemap types corresponding to [Esri Leaflet] layer creation functions.  For example, to register the ESRI Topograpic, Streets, and Imagery layers, you could do something like the following:

```javascript
config.map = {
    "basemaps": [
        {   
            "name": "ESRI Topographic",
            "type": "esri-basemap",
            "layer": "Topographic",
        },
        {   
            "name": "ESRI Streets",
            "type": "esri-basemap",
            "layer": "Streets",
        },
        {   
            "name": "ESRI Imagery",
            "type": "esri-basemap",
            "layer": "Imagery",
            "labels": true,
        }
    ]
}
```

Additional basemap types (e.g. from other Leaflet plugins) can be incorporated with `map.addBasemapType()` (see below).

#### Full Example

```javascript
// config.js
define(['data/config'], function(config) {

// (set template defaults, transitions, store)
// ...

// set map config defaults
config.map = {
    'bounds': [
        [44.78, -93.1],
        [45.18, -93.5]
    ],
    'basemaps': [
        // custom basemap definition
    ]
});

return config;
});
```

```javascript
// myapp.js

define(['wq/app', 'wq/map', './config'],
// to enable ESRI layers:
// define(['wq/app', 'wq/mapserv', './config'],

function(app, map, config) {
    app.use(map);
    app.init(config);
});
```

### Individual Map Configuration

After initialization, `map.config.maps` will be populated with map configurations for each page in the [wq configuration object] with a `"map"` property defined.  If a page's `map` property is defined an object, that object will be used as the map configuration.  If it is an array of objects, multiple map configurations will be defined (see below).  If the property is simply `true`, a default set of map configurations will be created with URL-based GeoJSON overlays (as described above).

#### Multiple Configurations

@wq/map supports defining separate map configurations for the "list", "detail", and "edit" modes of list pages.  As of version 1.0, @wq/map also supports custom page modes, as well as placing multiple maps in different locations on the same screen.

To make it easier to manage all of these possible variations, the `map` configuration is now defined as an array rather than as a deeply nested object.  Each object in the array can have the following attributes:

name | default | purpose
-----|---------|---------
`mode` | `defaults` | Template rendering mode for which this configuration applies to.  Typically one of `list`, `detail`, or `edit`.  If set to `all` or `defaults`, the defined configuration will be mixed together with any other applicable configuration when rendering mode-specific maps.  You can also use `all` or `defaults` to define maps for simple (non-list) pages, which do not use rendering modes.
`map` | `main` | Whether this configuration applies to the default (main) map or to a secondary map on the same screen.

#### Configuration Options

In addition to the mode and map attributes, each map configuration can have the following options:

name | default | purpose
-----|---------|---------
`layers` | See below | An array of layer configurations to use for this rendering mode.
`autoLayers` | `true` | If `true`, the maps created for the page will automatically include a default `"geojson"` layer as discussed above, as well as any explicitly defined layers.
`autoZoom` | global setting | Set to `false` to disable auto-zooming on a per-map basis.
`minBounds` | none | Minimum bounds to set when auto-zooming.  Should be a Leaflet [LatLngBounds] or compatible array.
`noLayerControl` | `false` | Set to true to disable the default layer control.
`onshow` | none | Function to call after map is created with `map.createMap()`.  The function will be passed the newly created `L.Map` object.
`div` | `[page]-map` | The id of the `<div>` tag to place the Leaflet map into.  The div should be present in the template in order for the automatic map creation to work.  By default the expected div id will be `[page]-map` for list views, `[page]-[itemid]-map` for detail views, and `[page]-[itemid]-edit-map` for edit views.  Like all Leaflet maps, the height of the div should be explicitly specified in an attribute or in CSS.

#### Example

```css
/* main.css */
.my-map-class {
  border: 1px solid black;
  height: 300px;
}
```

```xml
<!-- model_list.html -->
<div id="model-map" class="my-map-class"></div>
```

```xml
<!-- model_detail.html -->
<div id="model-{{id}}-map" class="my-map-class"></div>
```

### Layer Configuration

Each map configuration should have one or more layer configurations added to it.  The layer configurations are defined as `layers`, an array property on the map configuration.  Layers can also be added programmatically via `map.addLayerConf()`, which takes the name of an existing map and a layer configuration to add to it.  However, it is recommended to define all layers as part of the initial layer configuration.  With [wq.db.rest], this can be done by specifying a map property when registering the model:
```python
# myapp/rest.py
from .models import MyModel
app.router.register_model(
    MyModel,
    fields="__all__",
    map=[{
        'mode': 'all',
        ...
    }, {
        'mode': 'list',
        'layers': [...],
    }, {
        'mode': 'detail',
        'layers': [...]
    }]
)
```

A layer configuration consists of the following options:

name | purpose
-----|---------
`name` | The name of the layer to show in the layer list
`type` | The type of overlay to use.  The default is `"geojson"`, which is the only built-in type.  Other overlay types can be registered via `map.addOverlayType()` (see below).

The built-in `"geojson"` layer type recognizes the following options:

name | purpose
-----|---------
`url` | The path to a geojson file to download (including the `.geojson` extension).  This can be defined via a template syntax (as seen in the example below).
`icon` | The name of an icon to use, or a template that will compile to an icon name, or a function returning an icon name.  If a template or a function, it will be called with the `feature.properties` for each feature in the dataset.  Icon names should first be registered via `map.createIcon()` (see below).
`popup` | The name of a popup template to use when rendering marker popups.  See map.renderPopup() below
`cluster` | Boolean indicating whether or not to cluster markers.  The default for auto-generated layers is `true` as long as the Leaflet.markercluster plugin is present.  A copy of the plugin is included with wq.app but is not imported by default.
`draw` | Options to use for layer editing via `Leaflet.draw`.  See Map Editing below.
`geometryField` | The name of a hidden field to save edited geometry to.  See Map Editing below. 
`style` | A function to define styles based on the properties of each feature in the GeoJSON.  The function should take a feature and return a style object.  Available style options are listed in the documentation for [L.Path].  Equivalent to `L.GeoJSON`'s [style] option.
`oneach` | A function to call for each feature in the GeoJSON.  The function should take a Leaflet layer object and a GeoJSON feature.  `map.renderPopup()` can automatically create a compatible function that will attach a templated popup to each layer using the properties in the GeoJSON feature.  Equivalent to `L.GeoJSON`'s [onEachFeature] option.
`clusterIcon` | CSS class to use when creating the cluster icon `<div>`.  Can be a plain string, a template definition, or a function.  If a template or a function, a context of the form `{'count': count, [size]: true}` will be provided, where `[size]` is one of `large` (> 100), `medium` (> 10), or `small`.

#### Example

```javascript
config.pages[pagename].map = [
    {
        "mode": "list",
        // "autoLayers": true,
        "layers": [{
            'name': pagename,
            'type': 'geojson',
            'url': '{{{url}}}.geojson',
            'popup': pagename,
            'cluster': true
        }]
    }, {
        "mode": "detail":,
        // "autoLayers": true,
        "layers": [{
            'name': pagename,
            'type': 'geojson',
            'url': pageurl + '/{{{id}}}.geojson',
            'popup': page
        }]
    }, {
        "mode": "edit",
        // "autoLayers": true,
        "layers": [{
            'name': pagename,
            'type': 'geojson',
            'url': pageurl + '/{{{id}}}/edit.geojson',
            'draw': {
                'polygon': {},
                'polyline': {},
                'marker': {},
                'rectangle': {},
                'circle': false
            }
        }]
    }
];
```

## Map Editing
@wq/map supports editing layers via the [Leaflet.draw] plugin.  This functionality can be enabled by setting a `draw` attribute on the map's `edit` mode configuration.  The `draw` configuration will be passed on to the Leaflet draw control to enable different drawing types.

The draw functionality is meant to work in close integration with [wq.db.rest] - particularly to edit [LocatedModel]s and other models with `geometry` fields.  The basic workflow is like this:

 1. User navigates to /mymodel/1234/edit
 2. @wq/map loads /mymodel/1234/edit.geojson and displays it on a Leaflet.draw-enabled map
 3. User makes edits, which are serialized as a `FeatureCollection` to a hidden `geometry` or `locations` field in the form (this can be customized with the `geometryField` layer configuration option).
 4. User posts form to server, which parses and stores the geometry field, potentially splitting the geometries into multiple `Location` instances.

Note that there is an asymmetry between how the geographic data is initially loaded (edit.geojson) and how it is saved (form field).  This is primarily to avoid needing to store the entire geographic dataset in offline storage.  However, there are workarounds available if offline geographic data storage is needed.

## Advanced Usage

It is often necessary to define additional layer or icon types for use with the map configuration above.  These options need to be configured via JavaScript.

### `map.addBasemapType(name, function)`

`map.addBasemapType` can be used to add custom basemap types in addition to the built in `"tile"` type.  The provided `function` should accept a basemap configuration and return a Leaflet layer instance.  For example, the built-in `"tile"` layer type is registered as follows:

```javascript
map.addBasemapType('tile', function(layerConf) {
    return L.tileLayer(layerConf.url, layerConf);
});
```

The [@wq/map/mapserv] module provides additional examples of custom basemap types.

### `map.addOverlayType(name, function)`

`map.addOverlayType` can be used to add custom overlay types in addition to the built in `"geojson"` type.  The provided `function` should accept an overlay configuration and return a Leaflet layer instance.  For example, to create a WMS overlay with [leaflet.wms] you could do the following:

```javascript
map.addOverlayType('wms', function(layerConf) {
    var wmsSource = wms.source("/url/to/wms", {
        'format': 'image/png',
    });
    return wmsSource.getLayer(layerconf.layer);
});
```

The [@wq/map/mapserv] module provides additional examples of custom overlay types.

### `map.createIcon(name, options)`

`map.createIcon` defines and names an [L.Icon] for later use in layer configurations.  The function accepts a string name and an object containing options for the icon.  Options are the same as those for [L.Icon], but with a number of built-in defaults.  These defaults are optimized to make it trivial to define icons that have the same dimensions and shadow as Leaflet's default icon:

name | default
-----|---------
`iconSize` | `[25, 41]`
`iconAnchor` | `[12, 41]`
`popupAnchor` | `[1, -34]`
`shadowSize` | `[41, 41]`
`shadowUrl` | `L.Icon.Default.imagePath + '/marker-shadow.png'`

#### Example

```javascript
map.createIcon("green", {'iconUrl': "/images/green.png"});
```

### `map.createBasemaps()`

`map.createBasemaps()` gemerates the actual basemap instances for use in every map generated by @wq/map.  The function returns an object where the keys are layer names and the values are layer objects (usually `L.TileLayer`).  The basemaps will show up in the layer control generated for each map.  Generally, you shouldn't need to call or override this function directly - instead, customize the global `basemaps` configuration and/or register custom basemap types with `addBasemapType()`.

### `map.createLayerControl(basemaps, layers)`

`map.createLayerControl()` is a simple hook to allow customization of the default layer control added to every map generated by @wq/map.  The function takes two arguments; the basemaps from `map.createBasemaps()`, and the GeoJSON layers created by `map.createMap()` (using information from `map.getLayerConfs()`).  The default implementation simply passes the arguments on to, and returns, a [L.Control.Layers] instance.

### `map.renderPopup(page)`

`map.renderPopup(page)` generates a callback function suitable for use as the `oneach` argument to a layer configuration, or as the [onEachFeature] option in `L.GeoJSON`.  (Note that a configuration of `oneach: map.renderPopup(page)` can be replaced with `popup: page`.)  The function will render a popup using the template with the name `[page]_popup`.  The template should be included among the templates provided to [@wq/app]' `init()` function.  The template context will be the GeoJSON properties on each feature.  The example map at the top of the page uses the following template:

```xml
<!-- doc_popup.html -->
<h3>{{label}}</h3>
<table>
  <tr><th>Chapter:</th><td>{{chapter_label}}</td></tr>
  <tr><th>Last Updated:</th><td>{{updated_label}}</td></tr>
  <tr>
    <th>Interactive:</th>
    <td>
      {{#interactive}}&check;{{/interactive}}
      {{^interactive}}&cross;{{/interactive}}
    </td>
  </tr>
</table>
```

### `map.loadLayer(url, callback)`

`map.loadLayer()` is used retrieve the actual GeoJSON data for "geojson" overlay types.  The default implementation caches each GeoJSON object, so you can call `map.loadLayer()` to prefetch layers that you expect to appear in later maps.  The `url` argument is assumed to be relative to the webservice root used by [@wq/store].

[@wq/map]: https://github.com/wq/wq.app/blob/master/packages/map
[@wq/app plugin]: https://wq.io/docs/app-plugins
[wq configuration object]: https://wq.io/docs/config
[Leaflet]: http://leafletjs.com
[wq.db]: https://wq.io/wq.db
[REST API]: https://wq.io/docs/about-rest
[@wq/app]: https://wq.io/docs/app-js
[AMD]: https://wq.io/docs/amd
[router.addRoute()]: https://wq.io/docs/router-js
[@wq/app config object]: https://wq.io/docs/app-js
[URL Structure]: https://wq.io/docs/url-structure
[LatLngBounds]: http://leafletjs.com/reference.html#latlngbounds
[L.Path]: http://leafletjs.com/reference.html#path
[style]: http://leafletjs.com/reference.html#geojson-style
[onEachFeature]: http://leafletjs.com/reference.html#geojson-oneachfeature
[iconCreateFunction]: https://github.com/Leaflet/Leaflet.markercluster#customising-the-clustered-markers
[L.Icon]: http://leafletjs.com/reference.html#icon
[MapQuest-OSM and MapQuest Open Aerial]: http://developer.mapquest.com/web/products/open/map
[L.Control.Layers]: http://leafletjs.com/reference.html#control-layers
[@wq/store]: https://wq.io/docs/store-js
[wq.db.rest]: https://wq.io/docs/about-rest
[leaflet.wms]: https://github.com/heigeo/leaflet.wms
[Leaflet.draw]: https://github.com/leaflet/leaflet.draw
[LocatedModel]: https://wq.io/docs/locate
[Stamen Terrain]: http://maps.stamen.com/terrain/
[MapBox]: https://mapbox.com/
[code for this website]: https://github.com/powered-by-wq/wq.io/blob/ad35bffe1514644e0bd978b5e79275ac2c312ea1/db/content/rest.py#L63
[Esri Leaflet]: https://esri.github.io/esri-leaflet/
[@wq/map/mapserv]: https://github.com/wq/wq.app/blob/master/packages/map/src/mapserv.js

@wq/map/locate
=========

[@wq/map/locate]

**@wq/map/locate** is a [@wq/app plugin] providing utilities for requesting the user's latitude and longitude, a common use case in many VGI, citizen science, and crowdsourcing applications.  @wq/map/locate is designed to be used together with [@wq/map].

## API

Once registered, the locate plugin populates form `<input>`s from a Leaflet map to facilitate multiple ways of providing location information (e.g. GPS or a map click).

```javascript
// myapp/main.js
define(['wq/app', 'wq/map', 'wq/locate', './config'],
function(app, map, locate, config) {

// In myapp/config.js or in wq.db.rest registration:
// config.locate = { ... };
// config.pages[page].map = { ... };
// config.pages[page].locate = true;

app.use(map);
app.use(locate); // Should be registered after map

app.init(config).then(function() {
    app.jqmInit();
    app.prefetchAll();
});

});
```

## Usage

The Locator widget provides three modes for entering location information:

 * `gps`: Request position using device GPS or network location, if available.  Also returns GPS "accuracy" in meters.  To get more accurate measurements, the `gps` mode of the Locator widget continually polls location until the form is saved or a different mode is selected.
 * `interactive`: Allow user to click the map directly.  Accuracy is computed based on zoom level.
 * `manual`: Allow user to manually enter their latitude and longitude.

The entered coordinates are automatically displayed on a Leaflet map.  "Accuracy" is represented as a circle with the radius of the accuracy.

> **Tip: Save Accuracy In Your Database!**
>
> The accuracy number is a critical part of the location information - so don't discard it and only save the latitude and longitude.  Remember that accuracy is fundamentally different than precision: just because a GPS device returns latitude and longitude specified out to 9 decimal places, does not mean the user is actually at that exact location.  In fact, the initial measurement returned by many consumer GPS devices will often be "precise" but inaccurate, perhaps by several kilometers.  The accuracy measurement is thus an imperfect, but useful metric for evaluating the location information.  Keeping the GPS on for as long as possible is one way to get more accurate measurements, but it is still important to save the information in the database for future reference.
>
> Similarly, having a user tap the map to specify their location is practically guaranteed to provide inaccurate results unless they zoom in first.  For this reason, the `interactive` mode approximates an accuracy measurement based on zoom level (accuracy = 2 pixels of screen space converted to meters based on the zoom level).  Accuracy can serve as a reminder to the user to zoom in, or even to enforce a minimum level of accuracy in your form processing logic.

The Locator widget searches for the following fields in the form.  All fields are technically optional, though you will probably want at least `latitude` and `longitude` and/or `geometry` be present.

field name | purpose
-----------|------------------------
`latitude` | A text input that will receive (or provide) the latitude
`longitude` | A text input that will receive (or provide) the longitude
`geometry` |  A hidden input that will receive booth coordinates as a simple GeoJSON point
`accuracy` | A text input that will receive the computed accuracy
`toggle` | A set of radio buttons (or a select menu) that will change the widget mode.  The values for each option should be one or more of the modes listed above.
`mode` | A hidden input that will receive the selected mode (in the case where toggle field is not used or is not saved)
 `source` | A text input that will recieve information about the source of the GPS coordinate, if known.  (For use with `"gps"` mode in PhoneGap/Cordova applications with [cordova-plugin-bluetooth-geolocation] installed).

If any of these fields are named differently in your application, define `config.locate.fieldNames` as follows:

```javascript
config.locate = {
    "fieldNames": {
        "toggle": "toggle-btn",
        "latitude": "lat",
        "longitude": "lng",
        "accuracy": "accuracy"
    }
};
```

`config.locate` can also be used to register up to three callback functions that will be executed at various points in the process:

 * `onSetMode(mode)`: Called whenever the locator mode changes, e.g. in response to the user clicking the `toggle` button.
 * `onUpdate(location, accuracy)`: Called when ever a new location is determined.
 * `onError(event)`: Called when GPS lookup fails.

### Example

<div data-interactive id='locator-example'>
  <fieldset data-role="controlgroup" data-type="horizontal">
    <input type='radio' value='gps' id='loc-gps' name='mode'>
    <label for='loc-gps'>GPS</label>
    <input type='radio' value='interactive' id='loc-interactive' name='mode'>
    <label for='loc-interactive'>Interactive</label>
    <input type='radio' value='manual' id='loc-manual' name='mode'>
    <label for='loc-manual'>Manual</label>
  </fieldset>
  <div id='loc-map' style='height:300px;background:#ccc;border:1px solid black'></div>
  <div class='ui-grid-b'>
    <div class='ui-block-a ui-content'>
      Latitude
      <input id="loc-lat" type="number" step="0.0001">
    </div>
    <div class='ui-block-b ui-content'>
      Longitude
      <input id="loc-long" type="number" step="0.0001">
    </div>
    <div class='ui-block-c'>
      Accuracy (m)
      <input id="loc-acc" type="number" step="0.0001">
    </div>
  </div>
  <p id="loc-message"></p>
</div>

#### JS

```javascript
// myapp/main.js
define(['wq/app', 'wq/map', 'wq/locate', './config'],
function(app, map, locate, config) {

app.use(map);
app.use(locate);

config.locate = {
    // Custom handler for location updates
    'onUpdate': function(loc, accuracy) {
        if (accuracy > 1000) {
            $('#message').html(
                "Note: your location accuracy appears to be off by more than 1km."
            );
        } else {
            $('#message').html("");
        }
    }
}

app.init(config).then(function() {
    app.jqmInit();
    app.prefetchAll();
});

});
```

#### HTML

```xml
<fieldset data-role="controlgroup" data-type="horizontal">
  <input type='radio' value='gps' id='loc-gps' name='toggle'>
  <label for='loc-gps'>GPS</label>
  <input type='radio' value='interactive' id='loc-interactive' name='toggle'>
  <label for='loc-interactive'>Interactive</label>
  <input type='radio' value='manual' id='loc-manual' name='toggle'>
  <label for='loc-manual'>Manual</label>
</fieldset>
<div id='map-div-id'></div>
<div class='ui-grid-b'>
  <div class='ui-block-a ui-content'>
    Latitude
    <input name="latitude" type="number" step="0.0001">
  </div>
  <div class='ui-block-b ui-content'>
    Longitude
    <input name="longitude" type="number" step="0.0001">
  </div>
  <div class='ui-block-c'>
    Accuracy (m)
    <input name="accuracy" type="number" step="0.0001">
  </div>
</div>
```

> **Tip: Keep GPS running for better results!**
>
> When requesting the user's location in a web app, it's generally better to use `Geolocation.watchPosition()` than `Geolocation.getCurrentPosition()`, even when you only need a single point and not a GPS trace.  The reason for this is that the first result returned by the GPS may be inaccurate, and the longer the GPS is on, the more time it has to lock on to the satellites.  For this reason, @wq/map/locate continues requesting the GPS location until the user saves the form and/or navigates to another page.  This is accomplished by setting `watch: true` in the underlying call to [L.Map.locate()].

[@wq/map/locate]: https://github.com/wq/wq.app/blob/master/packages/map/src/locate.js
[@wq/app plugin]: https://wq.io/docs/app-plugins
[@wq/map]: https://wq.io/docs/map-js
[AMD]: https://wq.io/docs/amd
[L.Map.locate()]: http://leafletjs.com/reference.html#map-locate-options
[cordova-plugin-bluetooth-geolocation]: https://github.com/heigeo/cordova-plugin-bluetooth-geolocation
