Skip to content

Geo Map Multilayer

This demo shows how to render a Geo Map visualization using geohash queries. It also allows to add external data layers as markers. Please refer to the map geohash and loading external data sections for more information.

The map initially shows the Chicago Taxi Trips rides in US as one single marker. When clicking the marker the map will zoom in into the next level of precision.

To test the external data layer use the company_location source as test data in the new data layer box.

If you'd like to see the demo in full screen click here

Review the complete source code and copy/paste it to create your own.
<!DOCTYPE html>
<html>

<head lang="en">
  <meta charset="UTF-8">
  <title>Geo Map</title>
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <div class="add-data">
      <div class="label">Add a new data layer</div>
      <div class="add-data-form">
          <input id="source-name" type="text" placeholder="source: ie company_location"/>
          <input id="layer-color" type="text" placeholder="marker color: ie #dd8080"/>
          <button id="load-data-btn">Load data</button>
      </div>
  </div>
  <div id="chart" class="chart-ct">
    <div class="loader"></div>
  </div>
  
  <script src="../assets/jquery.min.js"></script>
  <script src="../../lib/cftoolkit.min.js"></script>
  <script src="../assets/cft-geo-charts.min.js"></script>
  <script src="../assets/cft-elasticsearch-provider.min.js"></script>
  <script src="https://chartfactor.com/wp-content/lib/chicago.js"></script>  
  <script src="./index.js"></script>
</body>

</html>
var $ = window.$;
var cf = window.cf;

var hideLoader = function () {
    $('.loader').hide();
};

var loadExternalData = function (source, color) {
    const config = { color: color };
    const map = cf.getVisualization('chart');
    const currentZoom = map.get('zoom');
    const id = `${source}-data-layer`;

    const precisionLevels = {
        raw: { zoom: 18 },
        levels: [
            { zoom: 4, precision: 2 },
            { zoom: 6, precision: 3 },
            { zoom: 9, precision: 4 },
            { zoom: 12, precision: 5 },
            { zoom: 13, precision: 6 },
            { zoom: 15, precision: 7 }
        ]
    };

    map.on('mapzoom', (e) => {
        const companyQuery = cf.getVisualization(id);
        const map = cf.getVisualization('chart');

        if (
      map.get('isGeoHashData')(companyQuery) ||
      map.get('isRawData')(companyQuery)
    ) {
            map.get('changeGeoHashPrecisionLevel')(companyQuery, e.data);
        }
    });

    map.on('mapmove', (e) => {
        const companyQuery = cf.getVisualization(id);
        const map = cf.getVisualization('chart');

        if (
      map.get('isGeoHashData')(companyQuery) ||
      map.get('isRawData')(companyQuery)
    ) {
            map.get('changeMapBoundariesFilter')(companyQuery, e.nativeData);
        }
    });

    // Use the proper precision according to the zoom level of the map at the moment
    // of the query
    const precision = map.get('getGeoHashPrecisionByZoomLevel')(precisionLevels.levels, currentZoom);

    cf.provider('Elasticsearch')
    .source(source)
    .location('location')
    .precision(precision)
    .set('precisionLevels', precisionLevels)
    .element(id)
    .on('execute:stop', (event) => {
        if (!event.error) {
        // Required for when clicking the marker
            config.precisionLevels = precisionLevels;
            config.fields = [
              { name: 'name', label: 'Company Name', type: 'ATTRIBUTE'},
              { name: 'metric', label: 'Metric', type: 'INTEGER'}
            ];
            map.get('addMarkerLayer')(source, event.data, config);
        }
    })
    .execute()
    .catch((e) => {
        console.log(e);
    });
};

var updateChart = function (event) {
    let metric = new cf.Metric('count');

    cf.provider('Elasticsearch')
    .source('chicago_taxi_trips')
    .set('layersControl', true)
    .location('dropoff_location')
    .precision(3)
    .set('maxSpiderifyMarkers', 500)
    .set('precisionLevels', {
        raw: { zoom: 18, fields: ['dropoff_latitude', 'dropoff_longitude'] },
        levels: [
        { zoom: 4, precision: 3 },
        { zoom: 9, precision: 4 },
        { zoom: 12, precision: 5 },
        { zoom: 15, precision: 6 },
        { zoom: 17, precision: 7 }
        ]
    })
    .metrics(metric)
    .limit(10000)
    .graph('Geo Map')
    .set('tileLayers', [
        {
            Base: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
            attribution: `Map data &copy; 
            <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors,
          <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>`
        }
    ])
    .set('center', [42.0622261328433, -84.23257973990964])
    .set('zoom', 2)
    .element('chart')
    .execute();
};

$(document).ready(function () {
    var providers = [
        {
            name: 'Elasticsearch',
            provider: 'elasticsearch',
            url: 'https://chartfactor.com/elastic/'
        }
    ];

    $('#load-data-btn').click(() => {
        const source = $('#source-name').val();
        const color = $('#layer-color').val();

        if (source !== '') {
            loadExternalData(source, color);
        }
    });

    cf.setProviders(providers);

    updateChart();
});
.chart-ct {
    height: calc(100% - 10px);
    width: 100%;
}

html, body, .row {
    height: 100%;
    margin: 0;
}

.loader {
    position: absolute;
    top: calc(50% - 60px);
    left: calc(50% - 60px);
    border: 16px solid #f3f3f3;
    /* Light grey */
    border-top: 16px solid #24282D;
    /* Blue */
    border-radius: 50%;
    width: 120px;
    height: 120px;
    animation: spin 2s linear infinite;
}

@keyframes spin {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}

.add-data {
    width: 240px;
    height: 140px;
    position: fixed;
    bottom: 0px;
    background: #79797970;
    right: 0px;
    padding: 7px;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    margin: 20px;
    margin-bottom: 40px;
    border-radius: 3px;
    z-index: 1000;
}

.add-data > .label {
    font-size: 12px;
    margin: 10px 0px 15px 0px;
    text-transform: uppercase;
    color: #1d1d1d;
}

.add-data-form {
    width: 200px;
    display:flex;
    flex-direction: column;
    justify-items: center;
    align-items: center;
}

.add-data-form > input {
    margin-bottom: 5px;
    width: 100%;
    border: none;
    height: 20px;
    padding: 4px;
}

.add-data-form > button {
    width: 90px;
}