Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel is required to process package imports. If you need a different preprocessor remove all packages first.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

HTML

              
                <!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Places Along Route | ThinkGeo</title>
    <link rel="stylesheet" href="https://cdn.thinkgeo.com/vectormap-js/3.0.0/vectormap.css"/>
</head>

<body>
    <div id="map" style="position:relative; width:100%; height:100%;">
        <!-- Balloon popup that describes each point of interest. -->
        <div id="popup" class="ol-popup">
            <a href="#" id="popup-closer" class="ol-popup-closer"></a>
            <div id="popup-content"></div>
        </div>
    </div>

    <div style="position: absolute; left: 1em; top: 1em; border: 1px solid #666; background: #fff; padding: 10px">
        <label>Find Along Route:</label>
        <select id="place-type">
            <option value="hotel,motel" tag="hotel">Hotels</option>
            <option value="bbq,cafe,fast_food,food_court,restaurant" tag="restaurant" selected>Restaurants</option>
            <option value="fuel" tag="gas-station">Gas Stations</option>
        </select>
        <button id="btn-get-route" onclick="performRouting()">
            Go
        </button>
    </div>

    <script src="https://cdn.thinkgeo.com/vectormap-js/3.0.0/vectormap.js" type="text/javascript"></script>
    <script src="https://cdn.thinkgeo.com/vectormap-icons/3.0.0/webfontloader.js" type="text/javascript"></script>
    <script src="https://cdn.thinkgeo.com/cloudclient-js/1.0.8/thinkgeocloudclient.js" type="text/javascript"></script>

</body>
</html>
              
            
!

CSS

              
                body {
  margin: 0;
  background-color: #3a3a3a;
  position: relative;
  font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

.ol-zoom {
  bottom: .5em;
  right: 0.5em;
  top: unset !important;
  left: unset !important;
}

.ol-zoom button {
  width: 1.2em !important;
  height: 1.2em !important;
}

/* Make the popup look like a speech balloon */
.ol-popup {
  position: absolute;
  background-color: #fff;
  filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
  padding: 5px;
  border-radius: 5px;
  border: 1px solid #ccc;
  bottom: 12px;
  left: -50px;
  min-width: 280px;
  margin-bottom: 30px;
}

/* Add the bubble's "pointer" */
.ol-popup:after,
.ol-popup:before {
  top: 100%;
  border: solid transparent;
  content: " ";
  height: 0;
  width: 0;
  position: absolute;
  pointer-events: none;
}
.ol-popup:after {
  border-top-color: white;
  border-width: 10px;
  left: 48px;
  margin-left: -10px;
}
.ol-popup:before {
  border-top-color: #ccc;
  border-width: 11px;
  left: 48px;
  margin-left: -11px;
}

/* Text styles for the popup content */
#popup-content {
  line-height: 20px;
  font-size: 12px;
}

#popup-content small {
  font-size: 12px;
}

/* Style the popup's 'X' button */
.ol-popup-closer {
  text-decoration: none;
  position: absolute;
  top: 2px;
  right: 8px;
  color: black;
}

.ol-popup-closer:after {
  content: "✖";
}
              
            
!

JS

              
                // Define your ThinkGeo Cloud API key here
const apiKey = 'WPLmkj3P39OPectosnM1jRgDixwlti71l8KYxyfP2P0~';

// Create the map's base layer
const mapBaseLayer = new ol.mapsuite.VectorTileLayer('https://cdn.thinkgeo.com/worldstreets-styles/3.0.0/light.json', {
    apiKey: apiKey,
    layerName: 'map-light'
});

const view = new ol.View({
    center: ol.proj.fromLonLat([-96.751, 32.7498]),
    progressiveZoom: true,
    zoom: 10,
    minZoom: 2,
    maxZoom: 19
});

const initializeMap = () => {
    let map = new ol.Map({
        renderer: 'webgl',
        loadTilesWhileAnimating: true,
        loadTilesWhileInteracting: true,
        layers: [mapBaseLayer],
        target: document.getElementById('map'),
        view: view
    });

    // Add a map layer for showing the start and end points.
    map.addLayer(new ol.layer.Vector({source:vectorSource}));
    showRouteEndpoints();

    // Add a map layer for the points of interest along the route.
    map.addLayer(new ol.layer.Vector({source:placeSource}));

    // Create a popup overlay that will show the name and address 
    // of each point we find along our route.
    const container = document.getElementById('popup');
    const content = document.getElementById('popup-content');
    const closer = document.getElementById('popup-closer');
    
    popup = new ol.Overlay({
        element: container,
        autoPan: true,
        autoPanAnimation: {
            duration: 250
        }
    });
    map.addOverlay(popup);

    // Show the popup when clicking a point.
    map.on('click', function (evt) {
        var feature = map.forEachFeatureAtPixel(evt.pixel,
            function (feature) {
                return feature;
            }, {
                layerFilter: (layer) => {
                    return !(layer instanceof ol.mapsuite.VectorTileLayer)
                }
            });
        if (feature && feature.get('name') === 'place') {
            var coordinates = feature.getGeometry().getCoordinates();
            popup.setPosition(coordinates);
            content.innerHTML = feature.get('content');
        }
    });

    // Close the popup when clicking on its 'X'.
    closer.onclick = function (e) {
        e.preventDefault();
        popup.setPosition(undefined);
        closer.blur();
        return false;
    };

    performRouting();
}

// Load ThinkGeo's base map icon set
WebFont.load({
    custom: {
        families: ["vectormap-icons"],
        urls: ["https://cdn.thinkgeo.com/vectormap-icons/3.0.0/vectormap-icons.css"]
    },
    active: initializeMap
});

// Set up styles for icon points
const styles = {
    start: new ol.style.Style({
        image: new ol.style.Icon({
            anchor: [0.5, 0.9],
            anchorXUnits: 'fraction',
            anchorYUnits: 'fraction',
            opacity: 1,
            crossOrigin: 'Anonymous',
            src: 'https://samples.thinkgeo.com/cloud/example/image/end-point-letter/end-point-a.png'
        })
    }),
    end: new ol.style.Style({
        image: new ol.style.Icon({
            anchor: [0.5, 0.9],
            anchorXUnits: 'fraction',
            anchorYUnits: 'fraction',
            opacity: 1,
            crossOrigin: 'Anonymous',
            src: 'https://samples.thinkgeo.com/cloud/example/image/end-point-letter/end-point-b.png'
        })
    }),
}

// Show the route start and end points on the map.
let vectorSource = new ol.source.Vector();
let startFeature, endFeature;
let startPoint = [-97.1598, 32.7599];
let endPoint = [-96.3213, 32.7203];

const showRouteEndpoints = () => {
    startFeature = new ol.Feature({
        geometry: new ol.geom.Point(ol.proj.fromLonLat(startPoint)),
        name: 'start'
    });
    endFeature = new ol.Feature({
        geometry: new ol.geom.Point(ol.proj.fromLonLat(endPoint)),
        name: 'end'
    });
    
    startFeature.setStyle(styles.start);
    endFeature.setStyle(styles.end);
    vectorSource.addFeature(startFeature);
    vectorSource.addFeature(endFeature);
}

// Generate the route.
const routingClient = new tg.RoutingClient(apiKey);
const performRouting = () => {
    const lineGeom = new ol.geom.LineString(
        [ol.proj.fromLonLat(startPoint), ol.proj.fromLonLat(endPoint)]
    );

    // Remove any existing route from the map before recalculating.
    vectorSource.getFeatures().some(feature => {
        if (feature.get('name') === 'line') {
            vectorSource.removeFeature(feature);
            return true;
        }
    })

    const handleRoutingResponse = (status, response) => {
        if (status === 200) {
            // Draw the route line on the map.
            let drivingLine = drawDrivingLine(response.data);

            // Search the places you are intrested in within the driving line.
            searchPlaces(drivingLine);
        }
    }

    const waypoints = [
        { x: startPoint[0], y: startPoint[1] },
        { x: endPoint[0], y: endPoint[1] }
    ];

    routingClient.getRoute(waypoints, handleRoutingResponse);
}

// Draw a line on the map following the route.
const drawDrivingLine = (res) => {
    // Reproject result from Decimal Degrees (lat/long) to Web Mercator 
    // (our map's coordinate system)
    const drivingLine = (new ol.format.WKT()).readFeature(res.routes[0].geometry, {
        dataProjection: 'EPSG:4326',
        featureProjection: 'EPSG:3857'
    });
    drivingLine.setStyle(new ol.style.Style({
        stroke: new ol.style.Stroke({
            width: 6,
            color: [34, 109, 214, 0.9]
        })
    }));
    drivingLine.set('name', 'line');
    vectorSource.addFeature(drivingLine);

    return drivingLine;
}

// Find points of interest along our route.
let placeSource = new ol.source.Vector();
let reverseGeocodingClient = new tg.ReverseGeocodingClient(apiKey);
const searchPlaces = (drivingLine) => {

    // Remove any points of interest already on the map.
    placeSource.clear();

    // Prepare request parameters.
    const placeType = document.querySelector('#place-type').selectedOptions[0];
    const lineWkt = (new ol.format.WKT()).writeGeometry(drivingLine.getGeometry());
    
    const handleReverseGeocodeResponse = (status, response) => {
        if (status === 200) {
            // Draw the returned points of interest on our map.
            showPlaces(response.data, placeType.innerText);
        }
    }

    // Perform the reverse geocode.
    reverseGeocodingClient.searchPlaceAdvanced({
        wkt: lineWkt,
        srid: 3857,
        locationTypes: placeType.value,
        maxResults: 100,
        searchRadius: 200  // Search within 200 meters of route.
    }, handleReverseGeocodeResponse);
}

const showPlaces = (res, placeType) => {
    for (let i = 0; i < res.nearbyLocations.length; i++) {
        // For each place in the reverse geocode result,
        // construct a map feature and a popup bubble.
        let place = res.nearbyLocations[i].data;
        const title = place.locationName ? `<strong>${place.locationName}</strong> ` : '';
        const content = `<div>${title}<small>` +
                        `(${place.locationType})</small><br/>` +
                        `${place.address.substring(place.address.indexOf(',') + 1)},` +
                        `${place.address.lastIndexOf(',')}</div>`
        const placeFeature = new ol.Feature({
            geometry: new ol.geom.Point([place.locationPoint.pointX, place.locationPoint.pointY]),
            content: content,
            name: 'place'
        });

        // Give the map point for each place an icon specific 
        // to its type (hotel, restaurant or gas station).
        let style;
        switch (placeType) {
            case 'Hotels':
                style = new ol.style.Style({
                    image: new ol.style.Icon({
                        anchor: [0.5, 1],
                        anchorXUnits: 'fraction',
                        anchorYUnits: 'fraction',
                        opacity: 1,
                        crossOrigin: "Anonymous",
                        src: 'https://samples.thinkgeo.com/cloud/example/image/place-icons/hotel.png',
                        imgSize: [32, 32]
                    }),
                    zIndex: 2
                });
                break;
            case 'Restaurants':
                style = new ol.style.Style({
                    image: new ol.style.Icon({
                        anchor: [0.5, 1],
                        anchorXUnits: 'fraction',
                        anchorYUnits: 'fraction',
                        opacity: 1,
                        crossOrigin: "Anonymous",
                        src: 'https://samples.thinkgeo.com/cloud/example/image/place-icons/restaurant.png',
                        imgSize: [32, 32]
                    }),
                    zIndex: 2
                });
                break;
            case 'Gas Stations':
                style = new ol.style.Style({
                    image: new ol.style.Icon({
                        anchor: [0.5, 1],
                        anchorXUnits: 'fraction',
                        anchorYUnits: 'fraction',
                        opacity: 1,
                        crossOrigin: "Anonymous",
                        src: 'https://samples.thinkgeo.com/cloud/example/image/place-icons/fuel.png',
                        imgSize: [32, 32]
                    }),
                    zIndex: 2
                });
                break;
        }

        placeFeature.setStyle(style);
        placeSource.addFeature(placeFeature);
    }
}

              
            
!
999px

Console