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
    <input type="GeoPoint" name="location">
      <occurrences minimum="1" maximum="1"/>
    <input type="TextLine" name="population">
      <occurrences minimum="0" maximum="1"/>

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”.

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
  <display-name>City list</display-name>
    <input type="ComboBox" name="mapType">
      <label>Map type</label>
      <occurrences minimum="0" maximum="1"/>
        <option value="ROADMAP">ROADMAP</option>
        <option value="SATELLITE">SATELLITE</option>
        <option value="HYBRID">HYBRID</option>
        <option value="TERRAIN">TERRAIN</option>
    <input type="TextLine" name="zoom">
      <label>Zoom level 1-15</label>
      <occurrences minimum="0" maximum="1"/>

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;


            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;">
    <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>
  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.