Skip to content

Store Locator using GeoJSON Data Source with Custom Marker Bubbles

This guide shows you how to customize store marker bubbles using previously added data source control for your stores data. A GeoJSON file with stores locations and properties is used as an example data source.

The finished example will look like this:

See the example fullscreen

What is needed

  • General Magic API key token

  • Web server (an example is provided)

  • GeoJSON file containing the store locations (or use our sample GeoJSON)

Setup

Get your GeneralMagic 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.

Adding controls

To see how to add GeoJSON data control, store list control and free text search control check out the guides Store Locator using a GeoJSON File as Data Source or Store Locator using GeoJSON Text String as Data Source.

Customizing the marker bubbles

To customize the store marker bubbles you can use the data source control option markerBubbleFunction for specifying your own style rules for marker hover or click event.

 1let geojsonDataControl = new gem.control.GeoJsonAddedDataControl("Paris.geojson", "" /*icon default*/, "" /*no icon filter*/,
 2  {
 3    markerBubble: {
 4      markerBubbleClass: 'store-bubble',
 5      markerBubbleFunction: function (elMarker, properties, coords) {
 6        elMarker.innerHTML = '<div class="store-bubble--img-container">' +
 7          '<img class="store-bubble--img" src=' + properties.preview + '></div>' +
 8          '<div class="store-bubble--content">' +
 9          '<div class="store-bubble--title">' + properties['name'] + '</div></div>';
10      }
11    },
12    marker: {
13      highlightClass: 'highlight-marker-icon'
14    }
15  });
16defaultAppScreen.addControl(geojsonDataControl);
 1<style>
 2  .store-bubble {
 3    display: block;
 4    -webkit-box-sizing: border-box;
 5    -moz-box-sizing: border-box;
 6    box-sizing: border-box;
 7    position: absolute;
 8    width: 240px;
 9    height: 200px;
10    background: #fff;
11    border-radius: 7px;
12    text-decoration: none;
13    -webkit-font-smoothing: antialiased;
14    bottom: 120%;
15    left: 50%;
16    margin-left: -120px;
17    font-size: 14px;
18    display: flex;
19    flex-direction: column-reverse;
20    -webkit-box-shadow: 0px 5px 15px rgb(0 0 0 / 15%);
21    box-shadow: 0px 5px 15px rgb(0 0 0 / 15%);
22  }
23
24  .store-bubble--title {
25    position: absolute;
26    text-overflow: ellipsis;
27    text-align: center;
28    white-space: nowrap;
29    overflow: hidden;
30    bottom: 12px;
31    width: 100%;
32    font-weight: 600;
33    color: #04aa6d;
34  }
35
36  .store-bubble--description {
37    padding: 17px 10px 7px 10px;
38    background: #fff;
39    color: grey;
40    text-overflow: ellipsis;
41    text-align: center;
42    white-space: nowrap;
43    overflow: hidden;
44  }
45
46  .store-bubble--img-container {
47    position: absolute;
48    width: 100%;
49    height: 80%;
50    top: 0;
51    bottom: 0px;
52    left: 0;
53    border-radius: 3px 3px 0 0;
54    overflow: hidden;
55    display: flex;
56    flex-wrap: wrap;
57    flex-direction: column;
58  }
59
60  .store-bubble--img {
61    background-position-x: 40px !important;
62    background-position-y: 5px !important;
63    position: absolute;
64    top: 0px;
65    right: 0;
66    left: 0px;
67    background-position-x: 12px;
68    background-position-y: 15px;
69    background-size: cover;
70    width: 100%;
71    height: 100%;
72    object-fit: cover;
73  }
74
75  .store-bubble--content {
76    background: #fff;
77    height: 20%;
78    width: 100%;
79    border-radius: 10px;
80    overflow: hidden;
81  }
82
83  .highlight-marker-icon {
84    filter: hue-rotate(70deg) contrast(6.5);
85    transform: scale(1.2);
86  }
87</style>

Complete example code

 1// Start by setting your token from https://www.generalmagic.com/api/projects
 2if (gem.core.App.token === undefined) gem.core.App.token = "";
 3
 4defaultAppScreen = gem.core.App.initAppScreen({
 5  container: "map-canvas"
 6});
 7
 8let geojsonDataControl = new gem.control.GeoJsonAddedDataControl("Paris.geojson", "" /*icon default*/, "" /*no icon filter*/,
 9  {
10    markerBubble: {
11      markerBubbleClass: 'store-bubble',
12      markerBubbleFunction: function (elMarker, properties, coords) {
13        elMarker.innerHTML = '<div class="store-bubble--img-container">' +
14          '<img class="store-bubble--img" src=' + properties.preview + '></div>' +
15          '<div class="store-bubble--content">' +
16          '<div class="store-bubble--title">' + properties['name'] + '</div></div>';
17      }
18    },
19    marker: {
20      highlightClass: 'highlight-marker-icon'
21    }
22  });
23let listUIControl = new gem.control.ListControl({
24  sourceControl: geojsonDataControl,
25  container: 'menu-list-container',
26  menuName: 'Store locations example',
27  titleProperties: ['name'],
28  detailsProperties: ['kinds'],
29  imageProperty: ['preview']
30});
31defaultAppScreen.addControl(geojsonDataControl);
32defaultAppScreen.addControl(listUIControl);
33
34let searchControl = new gem.control.SearchControl({
35  searchPreferences: {
36    exactMatch: true,
37    maximumMatches: 3,
38    setCursorReferencePoint: true
39  },
40  highlightOptions: {
41    contourColor: { r: 0, g: 255, b: 0, a: 255 }
42  }
43});
44defaultAppScreen.addControl(searchControl);
  1<html>
  2  <meta charset="utf-8" />
  3  <link rel="stylesheet" type="text/css" href="https://www.generalmagic.com/sdk/js/gem.css" />
  4  <title>Store Locator Custom Bubble</title>
  5
  6  <head>
  7    <style>
  8      .store-bubble {
  9        display: block;
 10        -webkit-box-sizing: border-box;
 11        -moz-box-sizing: border-box;
 12        box-sizing: border-box;
 13        position: absolute;
 14        width: 240px;
 15        height: 200px;
 16        background: #fff;
 17        border-radius: 7px;
 18        text-decoration: none;
 19        -webkit-font-smoothing: antialiased;
 20        bottom: 120%;
 21        left: 50%;
 22        margin-left: -120px;
 23        font-size: 14px;
 24        display: flex;
 25        flex-direction: column-reverse;
 26        -webkit-box-shadow: 0px 5px 15px rgb(0 0 0 / 15%);
 27        box-shadow: 0px 5px 15px rgb(0 0 0 / 15%);
 28      }
 29
 30      .store-bubble--title {
 31        position: absolute;
 32        text-overflow: ellipsis;
 33        text-align: center;
 34        white-space: nowrap;
 35        overflow: hidden;
 36        bottom: 12px;
 37        width: 100%;
 38        font-weight: 600;
 39        color: #04aa6d;
 40      }
 41
 42      .store-bubble--description {
 43        padding: 17px 10px 7px 10px;
 44        background: #fff;
 45        color: grey;
 46        text-overflow: ellipsis;
 47        text-align: center;
 48        white-space: nowrap;
 49        overflow: hidden;
 50      }
 51
 52      .store-bubble--img-container {
 53        position: absolute;
 54        width: 100%;
 55        height: 80%;
 56        top: 0;
 57        bottom: 0px;
 58        left: 0;
 59        border-radius: 3px 3px 0 0;
 60        overflow: hidden;
 61        display: flex;
 62        flex-wrap: wrap;
 63        flex-direction: column;
 64      }
 65
 66      .store-bubble--img {
 67        background-position-x: 40px !important;
 68        background-position-y: 5px !important;
 69        position: absolute;
 70        top: 0px;
 71        right: 0;
 72        left: 0px;
 73        background-position-x: 12px;
 74        background-position-y: 15px;
 75        background-size: cover;
 76        width: 100%;
 77        height: 100%;
 78        object-fit: cover;
 79      }
 80
 81      .store-bubble--content {
 82        background: #fff;
 83        height: 20%;
 84        width: 100%;
 85        border-radius: 10px;
 86        overflow: hidden;
 87      }
 88
 89      .highlight-marker-icon {
 90        filter: hue-rotate(70deg) contrast(6.5);
 91        transform: scale(1.2);
 92      }
 93    </style>
 94  </head>
 95
 96  <body>
 97    <div id="store-locator" style="width: 100%; height: 100%">
 98      <div id="menu-list-container" style="width: 30%; height: 100%; position: absolute"></div>
 99      <div id="map-canvas" style="width: 70%; height: 100%; position: absolute; left: 30%"></div>
100    </div>
101    <script type="text/javascript" src="https://www.generalmagic.com/sdk/js/gemapi.js"></script>
102    <script type="text/javascript" src="token.js"></script>
103    <script type="text/javascript" src="jsStoreLocatorCustomBubble.js"></script>
104  </body>
105</html>

Files

The finished example consists of the project directory containing these files:

  • JavaScript code (.js file extension)

  • HTML code (.html file extension)

  • Store locations in GeoJSON format (.geojson file extension)

To run the example, the HTML file is loaded in a browser.

Source code for this example:

JavaScript
HTML
Store locations in GeoJSON format

right-click on the links and select Save As.

JavaScript Examples

Maps SDK for JavaScript Examples can be downloaded or cloned with Git