Store Locator using GeoJSON File as Data Source with Custom Controls
This guide shows you how to build a store locator from scratch using a GeoJSON file containing the store locations.
The finished example will look like this:
When to use
- The entire store location data set is relatively small and can be easily downloaded.
- The data set is not changing frequently.
What is needed
- Magic Lane API key token
- Web server (an example is provided)
- SVG file of the store logo (or use our sample SVG)
- GeoJSON file containing the store locations (or use our sample GeoJSON)
Setup
Get your Magic Lane API key token: if you do not have a token, see the Getting Started guide.
This project needs a web server. If you do not have access to a web server, you can easily install a local web server, see the Installing a Local Web Server guide. In this project we use a local web server.
(1) Interactive Map
The first step is to render an interactive map.
- JavaScript
- HTML
// Start by setting your token from https://developer.magiclane.com/api/projects
if (gem.core.App.token === undefined)
gem.core.App.token = "";
var defaultAppScreen = gem.core.App.initAppScreen({
container: "map-canvas",
style: "./Mono-Day.style" // map style downloaded from Studio
});
The first step in JavaScript is to set your API key token gem.core.App.token
.
The app is initialized inside the map-canvas
container with a custom map style downloaded from Studio: gem.core.App.initAppScreen();
.
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8" />
<title>Store Locator using GeoJSON data with Custom Controls - Step 1</title>
<link rel="stylesheet" type="text/css" href="https://www.magiclane.com/sdk/js/gem.css" />
<link rel="stylesheet" href="/fonts/webfonts.css" type="text/css" media="all" />
</head>
<body>
<div id="store-locator" style="width: 100%; height: 100%">
<div id="map-canvas" style="width: 100%; height: 100%; position: absolute; overflow: hidden"></div>
</div>
<script src="https://www.magiclane.com/sdk/js/gemapi.js"></script>
<script type="text/javascript" src="token.js"></script>
<script type="text/javascript" src="storeGeojsonCustomControls01.js"></script>
</body>
</html>
Source code at this stage of the project:
(2) Render Store Locations using Map Markers and Information Bubbles
Next, we render store locations on the map using a SVG image as marker.
How it works
Create a directory for this project in the root directory of your web server.
In our case, the root directory for our local web server is htdocs
, in which we create the project directory named storeGeojsonCustomControls
.
So in our case, the project directory is: htdocs/storeGeojsonCustomControls
and the local web server url is: http://localhost/storeGeojsonCustomControls
Copy your SVG store logo file and GeoJSON store locations file into the project directory.
In our case, we are using the SVG file icon.svg
and the GeoJSON file paris.geojson
, which can be downloaded from here:
- JavaScript
- HTML
// stores data source control
let geojsonDataControl = new gem.control.GeoJsonAddedDataControl("paris.geojson", "icon.svg", undefined /*icon filter*/, {
marker: {
cssClass: 'store-marker',
highlightClass: 'highlight-marker-icon'
},
markerBubble: {
markerBubbleClass: 'store-bubble',
markerBubbleFunction: function (elMarker, properties, coords) {
elMarker.innerHTML = '<div class="store-bubble">' +
'<div class="store-bubble--title">Supermarket</div>' +
'<div class="store-bubble--description">' + properties['addr:street'] + ', ' + properties['addr:city'] +
'</div></div>';
}
},
markerGrouping: {
maxLevel: 14
}
});
defaultAppScreen.addControl(geojsonDataControl);
<style>
.store-locator {
font: 400 15px/22px "Source Sans Pro", "Helvetica Neue", Sans-serif;
}
.store-marker {
background-image: url(./icon.svg);
width: 20px;
height: 20px;
position: absolute;
cursor: pointer;
}
.store-marker.active {
width: 25px;
height: 25px;
}
.store-marker button {
position: absolute;
top: -50%;
left: 50%;
background: white;
color: #5390d9;
border-color: #5390d9;
border-style: solid;
padding: 0px;
cursor: pointer;
font-weight: 900;
font-size: 60%;
border-radius: 9px;
min-width: 18px;
min-height: 18px;
box-shadow: 0 2px 4px rgb(0 0 0 / 20%), 0 -1px 0 rgb(0 0 0 / 2%);
}
.highlight-marker-icon {
filter: hue-rotate(300deg);
transform: scale(1.5);
}
.store-bubble {
width: 180px;
bottom: 180%;
left: 50%;
margin-left: -90px;
position: absolute;
}
.store-bubble::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -10px;
border-width: 10px;
border-style: solid;
border-color: white transparent transparent transparent;
}
.store-bubble--title {
background: #48bfe3;
color: #fff;
font-weight: 700;
font-size: 16px;
text-align: center;
display: block;
padding: 10px;
border-radius: 3px 3px 0px 0px;
}
.store-bubble--description {
display: block;
padding: 10px;
background: #fff;
border-radius: 0px 0px 3px 3px;
color: grey;
}
</style>
Source code at this stage of the project:
(3) Add the Store Locations to a Sidebar List
Add a title and information for each store in the left sidebar, as a list.
- JavaScript
- HTML
let listUIControl = new gem.control.ListControl({
sourceControl: geojsonDataControl,
container: 'menu-list-container',
displayCount: false,
flyToItemAltitude: 800,
menuName: 'Store locations',
cssClasses: {
divMenu: {
className: 'list-menu',
type: 'div'
},
listHeader: {
className: 'list-title',
type: 'div'
},
divList: {
className: 'list-group',
type: 'div'
},
divItem: {
className: 'list-item',
type: 'div'
}
},
populateItemFunction: function (divItemObj, properties) {
divItemObj.innerHTML = '<a href="#" class="list-item--title">' + properties['brand'] + ', ' + properties['addr:street'] + '</a>' +
'<div class="list-item--details">' + properties['addr:city'] +
(properties['phone'] ? ' · ' + 'Phone: ' + properties['phone'] : '') +
'<div class="list-item--open-hours">' + 'Open: ' + properties['opening_hours'] + '</div>' +
'</div>';
divItemObj.addEventListener('click', function (e) {
e.preventDefault();
});
}
});
defaultAppScreen.addControl(listUIControl);
.list-menu {
border-right: 1px solid rgba(0, 0, 0, 0.25);
background-color: #f8f8f8;
height: 100%;
}
.list-title {
background: #fff;
padding: 0 10px;
background-color: #48bfe3;
color: #fff;
overflow-y: auto;
}
.list-group {
max-height: 100%;
overflow-y: auto;
word-break: break-word;
}
.list-item {
display: block;
padding: 10px;
background-color: #fff;
border-bottom: 1px solid #eee;
}
.list-item.active {
background-color: #dfeff5;
}
.list-item:hover {
background-color: #f8f8f8;
cursor: pointer;
}
.list-item--title {
display: block;
color: #5390d9;
font-weight: 700;
text-decoration: none;
padding-bottom: 5px;
}
.list-item--details {
color: grey;
}
.list-item--open-hours {
padding-top: 5px;
}
::-webkit-scrollbar {
width: 3px;
height: 3px;
border-left: 0;
background: rgba(0, 0, 0, 0.1);
}
::-webkit-scrollbar-thumb {
background: #5390d9;
border-radius: 0;
}
::-webkit-scrollbar-thumb:hover {
background: #48bfe3;
}
::-webkit-scrollbar-track {
background: none;
}
</style>
</head>
<body>
<div id="store-locator" style="width: 100%; height: 100%">
<div id="menu-list-container" class="menu-list-container" style="width: 30%; height: 100%; position: absolute"></div>
<div id="map-canvas" style="width: 70%; left: 30%; height: 100%; position: absolute; overflow: hidden"></div>
</div>
Source code at this stage of the project:
(4) Adding Search Control
- JavaScript
let searchControl = new gem.control.SearchControl({
highlightOptions: {
showContour: false
},
searchPreferences: {
exactMatch: true,
maximumMatches: 3,
addressSearch: true,
mapPoisSearch: true,
setCursorReferencePoint: true,
placeholderText: 'Search...'
},
searchResults: {
showLandmarkIcons: false
}
});
defaultAppScreen.addControl(searchControl);
Add the above code at the bottom of the JavaScript file. This enables the free-form text search functionality to search for anything on the map.
Source code at this stage of the project:
Files
The finished example consists of the project directory containing these files:
- JavaScript code (
.js
file extension) - HTML code (
.html
file extension) - SVG store logo (
.svg
file extension) - GeoJSON store location file (
.geojson
file extension) - Map style (
.style
file extension)
To run the example, the HTML file is loaded in a browser.
Source code for this example:
Right-click on the links and select Save As.
Related
See Store Locator using GeoJSON Text String as Data Source for an example with GeoJSON data defined in a variable in JavaScript, and rendered on the map.
See Store Locator using Studio Defined Data Source for an example with GeoJSON data uploaded to the map studio, and rendered on the map.
See Store Locator using SQL Server as Data Source for an example with GeoJSON data queried dynamically using SQL from a server, and rendered on the map.
JavaScript Examples
Maps SDK for JavaScript Examples can be downloaded or cloned with Git.