Website Downloads Documentation Knowledgebase Wiki Issue tracker Commercial support

Google Maps Field Editor

Lets assume you have a document and you want to attach a location to that document. For the sake of an example we'll pick landmarks. Thie Landmark document type will have parts :

  • SimpleDocumentContent : for a little description of what we are looking at (we'll just recycle this part)
  • Image : for a picture of the landmark

and fields :

  • GmapLocation : the field that will store the location

Now instead of having to figure out the latitude and longitude of the location we just want to use a map to pin point the location on. Enter the idea of a google maps field editor.

Here is a screenshot of what will be created.

GoogleMap FieldEditor
Click to enlarge

GmapLocation

This field will have to contain a number of different values :

  • latitude : double
  • longitude : double
  • zoom : long -- this is google maps specific and just used to store at which zoom level you wish to show the location

We want to store these 3 values in one field since we are talking about one concept, a location, which just happens to fall apart into a number of other primitives. Also a field editor can only handle one field. To solve this we will turn to  the hierarchical fields. The position in the 'hierarchy' will thus carry a meaning : 0. latitude, 1. longitude 2. zoom. Since this will all be stored in one field we will store all three values as doubles.

To summarize GmapLocation will be a hierarchical field type of doubles.

Field editor

Configuration

We will start of by configuring our editor. Here we will define which class drives the editor and pass a few parameters :

  • google key : this is the gmap api key we generate
  • default latitude, longitude & zoom : where the map should be centered when we open up the editor for the first time

Now this is what our configuration looks like

<?xml version="1.0"?>
<fieldEditor xmlns="http://outerx.org/daisy/1.0#fieldeditor" 
  class="org.outerj.daisy.frontend.editor.GoogleMapFieldEditor$Factory">
  <properties>
    <entry key="GoogleKey">ABQIAAAAOLLfFZQRGe8Hwl8R-4_7ABTb-vLQlFZmc2N8bgWI8YDPp5FEVBRSxdJTJfWoHiTs2nyCjV6ZpIuHvA</entry>
    <entry key="DefaultLongitude">4.21875</entry>
    <entry key="DefaultLatitude">50.635526</entry>
    <entry key="DefaultZoom">8</entry>
  </properties>
</fieldEditor>

Place this xml in <wikidata directory>/resources/fieldeditors/GmapLocation.xml

Editor class

This class extends the AbstractFieldEditor and overrides a number of methods. To compile this class you must have the following classes in your classpath (these can be found in <daisy wiki home>/webapp/WEB-INF/lib)

  • avalon-framework-api-4.3.jar
  • cocoon-2.1.11-dev.jar
  • cocoon-forms-block.jar
  • daisy-repository-api-2.2-dev.jar
  • daisy-util-2.2-dev.jar
  • daisywiki-frontend-2.2-dev.jar
package org.outerj.daisy.frontend.editor;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.service.ServiceManager;
import org.outerj.daisy.frontend.util.GenericPipeConfig;
import org.outerj.daisy.repository.Document;
import org.outerj.daisy.repository.Field;
import org.outerj.daisy.repository.HierarchyPath;
import org.outerj.daisy.repository.Repository;
import org.outerj.daisy.repository.schema.FieldType;
import org.outerj.daisy.repository.schema.FieldTypeUse;
import org.xml.sax.ContentHandler;

public class GoogleMapFieldEditor extends AbstractFieldEditor {
    private String definitionTemplate;

    private String templateTemplate;

    private String googleKey;

    private double latitude = 0.0;

    private double longitude = 0.0;

    private double zoom = 1.0;

    private GoogleMapFieldEditor(FieldTypeUse fieldTypeUse, Map<String, String> properties, ServiceManager serviceManager, Context context, Logger logger) {
        super(fieldTypeUse, serviceManager, context, logger);
        this.definitionTemplate = "wikidata:/resources/fieldeditors/gmapfield_definition.xml";
        this.templateTemplate = "wikidata:/resources/fieldeditors/gmapfield_template.xml";

        this.googleKey = properties.get("GoogleKey");

        if (properties.containsKey("DefaultLatitude")) {
            try {
                this.latitude = Double.parseDouble(properties.get("DefaultLatitude"));
            } catch (NumberFormatException e) {
                logger.warn("The default latitude specified in the configuration does not seem to be a double", e);
            }
        }
        if (properties.containsKey("DefaultLongitude")) {
            try {
                this.longitude = Double.parseDouble(properties.get("DefaultLongitude"));
            } catch (NumberFormatException e) {
                logger.warn("The default longitude specified in the configuration does not seem to be a double", e);
            }
        }
        if (properties.containsKey("DefaultZoom")) {
            try {
                this.zoom = Long.parseLong(properties.get("DefaultZoom"));
            } catch (NumberFormatException e) {
                logger.warn("The default zoom specified in the configuration does not seem to be an integer", e);
            }
        }
    }

    public static class Factory implements FieldEditorFactory {

        public FieldEditor getFieldEditor(FieldTypeUse fieldTypeUse, Map<String, String> properties, ServiceManager serviceManager, Context context,
                Logger logger) {
            GoogleMapFieldEditor editor = new GoogleMapFieldEditor(fieldTypeUse, properties, serviceManager, context, logger);

            return editor;
        }

    }

    <!-- overridden so that viewData can be added -->
    public void generateFormTemplateFragment(ContentHandler contentHandler, Locale locale) throws Exception {
        GenericPipeConfig pipeConfig = new GenericPipeConfig();
        pipeConfig.setApplyI18n(true);
        pipeConfig.setApplyLayout(false);
        pipeConfig.setTransformLinks(false);
        pipeConfig.setXmlSerializer();

        pipeConfig.setTemplate(this.getTemplateTemplate());
        String stylesheet = this.getTemplateStylesheet();
        if (stylesheet != null && stylesheet.length() > 0)
            pipeConfig.setStylesheet(stylesheet);

        Map<String, Object> viewData = new HashMap<String, Object>();
        viewData.put("GoogleKey", googleKey);
        viewData.put("defaultLatitude", this.latitude);
        viewData.put("defaultLongitude", this.longitude);
        viewData.put("defaultZoom", this.zoom);
        processGenericPipe(pipeConfig, viewData, contentHandler, locale);
    }

    @Override
    public String getDefinitionStylesheet() {
        return null;
    }

    @Override
    public String getDefinitionTemplate() {
        return definitionTemplate;
    }

    @Override
    public String getTemplateStylesheet() {
        return null;
    }

    @Override
    public String getTemplateTemplate() {
        return templateTemplate;
    }

    @Override
    public void load(Document document) throws Exception {
        Field field = document.getField(this.fieldType.getId());
        Object value = field.getValue();

        this.widget.setValue(convertHierarchyPathToString((HierarchyPath)value));
    }
<!-- this method is called in the store method of the super class -->
    protected Object getValueToSave(Object value, FieldType fieldType, Repository repository) {
        String[] valuePath = HierarchicalFieldHelper.parseHierarchicalInput((String)value);
        Double[] path = new Double[valuePath.length];

        for (int i = 0; i < valuePath.length; i++) {
            path[i] = Double.parseDouble(valuePath[i]);
        }

        return new HierarchyPath(path);
    }

    private String convertHierarchyPathToString(HierarchyPath path) {
        StringBuffer buf = new StringBuffer();
        for (Object element : path.getElements()) {
            buf.append("/");
            buf.append(element);
        }
        return buf.toString();
    }
}

Form definition

This is a blurb of cforms definition xml that will be inserted into the grand form of the field editor tab. Here we don't use base type 'double' but 'string' since the representation of the hierarchy is not a double but a string.

<?xml version="1.0"?>
<fd:field id="field" xmlns:fd="http://apache.org/cocoon/forms/1.0#definition">  
    <fd:datatype base="string"/>      
</fd:field>

Form template

In this template the field where the actual hierarchical value goes is hidden and a google map is shown in it's stead. The map is centered on the default location when there is no value in the GmapLocation field. If there is one then the map will be centered there.

<?xml version="1.0"?>

<ft:group id="field_${fieldTypeUse.fieldType.id}" 
  xmlns:ft="http://apache.org/cocoon/forms/1.0#template" 
  xmlns:fi="http://apache.org/cocoon/forms/1.0#instance"
  xmlns:jx="http://apache.org/cocoon/templates/jx/1.0">
  <tr>
    <td>
      Map
    </td>
    <td>
      <ft:widget id="field">
        <fi:styling type="hidden"/>
      </ft:widget>
       
      <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=${GoogleKey}" type="text/javascript"/>
      <script type="text/javascript">
        <![CDATA[
          daisyPushOnLoad(load);    
    
          window.onunload = function() {              
              GUnload();
          }
      
          function load() {          
            if (GBrowserIsCompatible()) {
              var fieldId = ${fieldTypeUse.fieldType.id};
              var inputElement = document.getElementById("field_" + fieldId + ".field");              
            
              var markHere = null;
              var map = new GMap2(document.getElementById("map"));
       
              map.addControl(new GSmallMapControl());
              map.addControl(new GMapTypeControl());
              map.setCenter(new GLatLng(${defaultLatitude}, ${defaultLongitude}), ${defaultZoom});     
                 
              
              GEvent.addListener(map, "click", function(marker, point) {
                  if (markHere != null)
                      map.removeOverlay(markHere);         
                  markHere = new GMarker(point);
                  map.addOverlay(markHere);
                  
                  inputElement.value = "/" + point.lat() + "/" + point.lng() + "/" + map.getZoom();    
               });
               
      
              if (inputElement.value != null && inputElement.value != "") {
                var elements = inputElement.value.split("/");
                var point = new GLatLng(elements[1], elements[2]);
                markHere = new GMarker(point);
                map.setCenter(point, elements[3]);
                map.addOverlay(markHere);
              }                             
            }
          }
          ]]>
    </script>
      <div id="map" style="width: 900px; height: 400px; margin-left: auto; margin-right: auto"></div>
    </td>
  </tr>
</ft:group>
Comments (0)
Advertisement

Daisy hosting, installation, support. Workshops and turnkey Daisy CMS projects. Get Daisy from its creators.

outerthought.org

Downloads provided by

SourceForge.net Logo

Open source stats