Hello Geo World

Going back to your site, you will now see a list of the countries we have added. To make this even more exciting, we will add a City content type with geo-location and a City list part with configuration capabilities.

City content

The next steps will create a content type for adding cities with location coordinates.

  1. Create a folder called city inside the project’s site/content-types folder.
  2. Add the content type file below to your project:
City content type - site/content-types/city/city.xml
<content-type>
  <display-name>City</display-name>
  <super-type>base:structured</super-type>
  <form>
    <input type="GeoPoint" name="location">
      <label>Location</label>
      <occurrences minimum="1" maximum="1"/>
    </input>
    <input type="TextLine" name="population">
      <label>Population</label>
      <occurrences minimum="0" maximum="1"/>
    </input>
  </form>
</content-type>

The file above defines a content type for cities with a required field for the location in latitude and longitude.

  1. Copy the image below and save it in the same folder with the City content type. Name it “city.png”.
../../_images/city.png

City list part

We need a part component to display the city data. It will list the cities and show a Google map of each location.

  1. Create a folder called city-list inside the project’s site/parts folder.
  2. Add the part descriptor file.
City list part descriptor - site/parts/city-list/city-list.xml
<part>
  <display-name>City list</display-name>
  <config>
    <input type="ComboBox" name="mapType">
      <label>Map type</label>
      <occurrences minimum="0" maximum="1"/>
      <config>
        <option value="ROADMAP">ROADMAP</option>
        <option value="SATELLITE">SATELLITE</option>
        <option value="HYBRID">HYBRID</option>
        <option value="TERRAIN">TERRAIN</option>
      </config>
    </input>
    <input type="TextLine" name="zoom">
      <label>Zoom level 1-15</label>
      <occurrences minimum="0" maximum="1"/>
    </input>
  </config>
</part>

The part descriptor above has a configuration similar to those found in content types.

  1. Add the part controller file.
City list part controller - site/parts/city-list/city-list.js
var contentLib = require('/lib/xp/content'); // Import the content library functions
var portal = require('/lib/xp/portal'); // Import the portal functions
var thymeleaf = require('/lib/xp/thymeleaf'); // Import the Thymeleaf rendering function

// Handle the GET request
exports.get = function (req) {

    // Get the part configuration for the map
    var config = portal.getComponent().config;
    var zoom = parseInt(config.zoom) && config.zoom <= 15 && config.zoom >= 1 ? config.zoom : 10;
    var mapType = config.mapType || 'ROADMAP';

    // String that will be inserted to the head of the document
    var googleMaps = '<script src="http://maps.googleapis.com/maps/api/js"></script>';

    var countryPath = portal.getContent()._path;

    // Get all the country's cities
    var result = contentLib.query({
        start: 0,
        count: 100,
        contentTypes: [
            app.name + ':city'
        ],
        "query": "_path LIKE '/content" + countryPath + "/*'",
    });

    var hits = result.hits;

    var cities = [];

    if (hits.length > 0) {
        googleMaps += '<script>function initialize() {';

        // Loop through the contents and extract the needed data
        for (var i = 0; i < hits.length; i++) {

            var city = {};
            city.name = hits[i].displayName;
            city.location = hits[i].data.location;
            city.population = hits[i].data.population ? 'Population: ' + hits[i].data.population : null;

            cities.push(city);

            if (city.location) {
                city.mapId = 'googleMap' + i;

                googleMaps += 'var center' + i + ' = new google.maps.LatLng(' + city.location + '); ';

                googleMaps += 'var mapProp = {center:center' + i + ', zoom:' + zoom +
                              ', mapTypeId:google.maps.MapTypeId.' + mapType + ', scrollwheel: false };' +
                              'var map' + i + ' = new google.maps.Map(document.getElementById("googleMap' + i + '"),mapProp); ' +
                              'var marker = new google.maps.Marker({ position:center' + i + '}); marker.setMap(map' + i + ');';
            }

        }

        googleMaps += '} google.maps.event.addDomListener(window, "load", initialize);</script>';
    }

    // Prepare the model object that will be passed to the view file
    var model = {
        cities: cities
    };

    // Specify the view file to use
    var view = resolve('city-list.html');

    // Return the response object
    return {
        body: thymeleaf.render(view, model),
        // Put the maps' javascript into the head of the document
        pageContributions: {
            headEnd: googleMaps
        }
    }
};

This controller uses Page Contributions to put the Google Maps JavaScript into the head of the document.

  1. Add the part view file.
City list part view - site/parts/city-list/city-list.html
<div class="cities" style="min-height:100px;">
    <h3>Cities</h3>
    <div class="city" data-th-each="city : ${cities}">
        <h3 data-th-text="${city.name}"></h3>
        <div data-th-if="${city.population}" data-th-text="${city.population}"></div>
        <div data-th-id="${city.mapId}" data-if="${city.mapId}" style="width:100%;height:300px;"></div>
        <br/><br/>
    </div>
</div>
  1. Build and deploy your project one final time with ./gradlew deploy.

All of the project’s files are now complete. The rest of the steps will be performed in the Content Manager app.