org.waterforpeople.mapping.app.web.KMLGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.waterforpeople.mapping.app.web.KMLGenerator.java

Source

/*
 *  Copyright (C) 2010-2012 Stichting Akvo (Akvo Foundation)
 *
 *  This file is part of Akvo FLOW.
 *
 *  Akvo FLOW is free software: you can redistribute it and modify it under the terms of
 *  the GNU Affero General Public License (AGPL) as published by the Free Software Foundation,
 *  either version 3 of the License or any later version.
 *
 *  Akvo FLOW is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *  See the GNU Affero General Public License included below for more details.
 *
 *  The full license text can also be seen at <http://www.gnu.org/licenses/agpl.html>.
 */

package org.waterforpeople.mapping.app.web;

import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.velocity.VelocityContext;
import org.waterforpeople.mapping.dao.AccessPointDao;
import org.waterforpeople.mapping.dao.GeoRegionDAO;
import org.waterforpeople.mapping.domain.AccessPoint;
import org.waterforpeople.mapping.domain.AccessPoint.AccessPointType;
import org.waterforpeople.mapping.domain.GeoRegion;
import org.waterforpeople.mapping.domain.TechnologyType;
import org.waterforpeople.mapping.helper.AccessPointHelper;

import com.gallatinsystems.common.Constants;
import com.gallatinsystems.common.util.PropertyUtil;
import com.gallatinsystems.common.util.VelocityUtil;
import com.gallatinsystems.editorial.dao.EditorialPageDao;
import com.gallatinsystems.editorial.domain.EditorialPage;
import com.gallatinsystems.framework.dao.BaseDAO;
import com.gallatinsystems.standards.dao.LOSScoreToStatusMappingDao;
import com.gallatinsystems.standards.dao.LevelOfServiceScoreDao;
import com.gallatinsystems.standards.domain.LOSScoreToStatusMapping;
import com.gallatinsystems.standards.domain.LevelOfServiceScore;
import com.gallatinsystems.standards.domain.Standard.StandardType;
import com.gallatinsystems.surveyal.domain.SurveyalValue;
import com.gallatinsystems.surveyal.domain.SurveyedLocale;
import com.google.appengine.api.datastore.Key;

public class KMLGenerator {
    private static final String IMAGE_ROOT = "mapiconimageroot";

    private static final Logger log = Logger.getLogger(KMLGenerator.class.getName());

    public static final String GOOGLE_EARTH_DISPLAY = "googleearth";
    // public static final String WATER_POINT_FUNCTIONING_GREEN_ICON_URL =
    // PropertyUtil
    // .getProperty(IMAGE_ROOT) + "/images/iconGreen36.png";
    // public static final String WATER_POINT_FUNCTIONING_YELLOW_ICON_URL =
    // PropertyUtil
    // .getProperty(IMAGE_ROOT) + "/images/iconYellow36.png";
    // public static final String WATER_POINT_FUNCTIONING_RED_ICON_URL =
    // PropertyUtil
    // .getProperty(IMAGE_ROOT) + "/images/iconRed36.png";
    public static final String WATER_POINT_FUNCTIONING_GREEN_ICON_URL = PropertyUtil.getProperty(IMAGE_ROOT)
            + "/images/glassGreen32.png";
    public static final String WATER_POINT_FUNCTIONING_YELLOW_ICON_URL = PropertyUtil.getProperty(IMAGE_ROOT)
            + "/images/glassOrange32.png";
    public static final String WATER_POINT_FUNCTIONING_RED_ICON_URL = PropertyUtil.getProperty(IMAGE_ROOT)
            + "/images/glassRed32.png";
    public static final String WATER_POINT_FUNCTIONING_BLACK_ICON_URL = PropertyUtil.getProperty(IMAGE_ROOT)
            + "/images/iconBlack36.png";
    public static final String PUBLIC_INSTITUTION_FUNCTIONING_GREEN_ICON_URL = PropertyUtil.getProperty(IMAGE_ROOT)
            + "/images/houseGreen36.png";
    public static final String PUBLIC_INSTITUTION_FUNCTIONING_YELLOW_ICON_URL = PropertyUtil.getProperty(IMAGE_ROOT)
            + "/images/houseYellow36.png";
    public static final String PUBLIC_INSTITUTION_FUNCTIONING_RED_ICON_URL = PropertyUtil.getProperty(IMAGE_ROOT)
            + "/images/houseRed36.png";
    public static final String PUBLIC_INSTITUTION_FUNCTIONING_BLACK_ICON_URL = PropertyUtil.getProperty(IMAGE_ROOT)
            + "/images/houseBlack36.png";
    public static final String PUBLIC_INSTITUTION_FUNCTION_BLACK_ICON_URL_2 = PropertyUtil.getProperty(IMAGE_ROOT)
            + "/images/iconBlack36.png";
    public static final String SCHOOL_INSTITUTION_FUNCTIONING_GREEN_ICON_URL = PropertyUtil.getProperty(IMAGE_ROOT)
            + "/images/pencilGreen36.png";
    public static final String SCHOOL_INSTITUTION_FUNCTIONING_YELLOW_ICON_URL = PropertyUtil.getProperty(IMAGE_ROOT)
            + "/images/pencilYellow36.png";
    public static final String SCHOOL_INSTITUTION_FUNCTIONING_RED_ICON_URL = PropertyUtil.getProperty(IMAGE_ROOT)
            + "/images/pencilRed36.png";
    public static final String SCHOOL_INSTITUTION_FUNCTIONING_BLACK_ICON_URL = PropertyUtil.getProperty(IMAGE_ROOT)
            + "/images/pencilBlack36.png";
    public static final Boolean useScore = Boolean.parseBoolean(PropertyUtil.getProperty("scoreAPFlag"));
    public static final String ORGANIZATION_KEY = "organization";
    public static final String ORGANIZATION = PropertyUtil.getProperty("organization");
    private static final ThreadLocal<DateFormat> LONG_DATE_FORMAT = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("dd-MM-yyyy HH:mm:ss z");
        };
    };

    private static final Map<String, String> ICON_TYPE_MAPPING;
    private static final Map<String, String> ICON_COLOR_MAPPING;
    private static final String IMAGE_PREFIX = PropertyUtil.getProperty(IMAGE_ROOT);
    private static final String DEFAULT = "DEFAULT";
    public static final String defaultPhotoCaption = PropertyUtil.getProperty("defaultPhotoCaption");

    private static final String DYNAMIC_SCORING_FLAG = "scoreAPDynamicFlag";

    static {
        ICON_TYPE_MAPPING = new HashMap<String, String>();
        ICON_TYPE_MAPPING.put("WaterPoint", "glass");
        ICON_TYPE_MAPPING.put("PublicInstitution", "house");
        ICON_TYPE_MAPPING.put("Household", "house");
        ICON_TYPE_MAPPING.put("School", "pencil");
        ICON_TYPE_MAPPING.put("Trawler", "glass");
        ICON_TYPE_MAPPING.put(DEFAULT, "glass");

        ICON_COLOR_MAPPING = new HashMap<String, String>();
        ICON_COLOR_MAPPING.put(DEFAULT, "Black36.png");
        ICON_COLOR_MAPPING.put("FUNCTIONING_OK", "Green36.png");
        ICON_COLOR_MAPPING.put("FUNCTIONING_HIGH", "Green36.png");
        ICON_COLOR_MAPPING.put("FUNCTIONING_OK", "Yellow36.png");
        ICON_COLOR_MAPPING.put("FUNCTIONING_WITH_PROBLEMS", "Yellow36.png");
        ICON_COLOR_MAPPING.put("BROKEN_DOWN", "Black36.png");
        ICON_COLOR_MAPPING.put("NO_IMPROVED_SYSTEM", "Black36.png");
    }

    /**
     * forms the url for the placemark image based on the status
     * 
     * @param type
     * @param status
     * @return
     */
    public static String getMarkerImageUrl(String type, String status) {
        String url = KMLGenerator.IMAGE_PREFIX + "/images/";

        String typePart = KMLGenerator.ICON_TYPE_MAPPING.get(type != null ? type : KMLGenerator.DEFAULT);
        if (typePart == null) {
            typePart = KMLGenerator.ICON_TYPE_MAPPING.get(KMLGenerator.DEFAULT);
        }
        url += typePart;
        String statusPart = KMLGenerator.ICON_COLOR_MAPPING.get(status != null ? status : KMLGenerator.DEFAULT);
        if (statusPart == null) {
            statusPart = KMLGenerator.ICON_COLOR_MAPPING.get(KMLGenerator.DEFAULT);
        }
        url += statusPart;
        return url;
    }

    public static final String useLongDates = PropertyUtil.getProperty("useLongDates");

    public KMLGenerator() {

    }

    public String generateRegionDocumentString(String regionVMName) {
        String regionKML = generateRegionOutlines(regionVMName);
        return regionKML;
    }

    public String generateDocument(String placemarksVMName) {
        return generateDocument(placemarksVMName, Constants.ALL_RESULTS);
    }

    public String generateDocument(String placemarksVMName, String countryCode) {
        try {
            VelocityContext context = new VelocityContext();
            String placemarks = generatePlacemarks(placemarksVMName, countryCode);
            context.put("folderContents", placemarks);
            context.put("regionPlacemark", generateRegionOutlines("Regions.vm"));
            return mergeContext(context, "Document.vm");
        } catch (Exception ex) {
            log.log(Level.SEVERE, "Could create kml", ex);
        }
        return null;
    }

    @SuppressWarnings("unused")
    private String generateFolderContents(HashMap<String, ArrayList<String>> contents, String vmName)
            throws Exception {
        VelocityContext context = new VelocityContext();
        StringBuilder techFolders = new StringBuilder();

        for (Entry<String, ArrayList<String>> techItem : contents.entrySet()) {
            String key = techItem.getKey();
            StringBuilder sbFolderPl = new StringBuilder();
            for (String placemark : techItem.getValue()) {
                sbFolderPl.append(placemark);
            }
            context.put("techFolderName", key);
            context.put("techPlacemarks", sbFolderPl);
            techFolders.append(mergeContext(context, "techFolders.vm"));
        }
        context.put("techFolders", techFolders.toString());
        return mergeContext(context, vmName);

    }

    public void generateCountryOrderedPlacemarks(String vmName, String countryCode, String technologyType) {

    }

    public HashMap<String, ArrayList<String>> generateCountrySpecificPlacemarks(String vmName, String countryCode) {
        if (countryCode.equals("MW")) {
            HashMap<String, ArrayList<AccessPoint>> techMap = new HashMap<String, ArrayList<AccessPoint>>();
            BaseDAO<TechnologyType> techDAO = new BaseDAO<TechnologyType>(TechnologyType.class);
            List<TechnologyType> techTypeList = (List<TechnologyType>) techDAO.list(Constants.ALL_RESULTS);
            AccessPointDao apDao = new AccessPointDao();
            List<AccessPoint> waterAPList = apDao.searchAccessPoints(countryCode, null, null, null, "WATER_POINT",
                    null, null, null, null, null, null, Constants.ALL_RESULTS);
            for (TechnologyType techType : techTypeList) {
                // log.info("TechnologyType: " + techType.getName());
                ArrayList<AccessPoint> techTypeAPList = new ArrayList<AccessPoint>();
                for (AccessPoint item : waterAPList) {

                    if (techType.getName().toLowerCase().equals("unimproved waterpoint")
                            && item.getTypeTechnologyString().toLowerCase().contains("unimproved waterpoint")) {
                        techTypeAPList.add(item);
                    } else if (item.getTypeTechnologyString().equals(techType.getName())) {
                        techTypeAPList.add(item);
                    }
                }
                techMap.put(techType.getName(), techTypeAPList);
            }

            List<AccessPoint> sanitationAPList = apDao.searchAccessPoints(countryCode, null, null, null,
                    "SANITATION_POINT", null, null, null, null, null, null, Constants.ALL_RESULTS);
            HashMap<String, AccessPoint> sanitationMap = new HashMap<String, AccessPoint>();
            for (AccessPoint item : sanitationAPList) {
                sanitationMap.put(item.getGeocells().toString(), item);
            }
            sanitationAPList = null;
            HashMap<String, ArrayList<String>> techPlacemarksMap = new HashMap<String, ArrayList<String>>();
            for (Entry<String, ArrayList<AccessPoint>> item : techMap.entrySet()) {
                String key = item.getKey();
                ArrayList<String> placemarks = new ArrayList<String>();
                for (AccessPoint waterAP : item.getValue()) {

                    AccessPoint sanitationAP = sanitationMap.get(waterAP.getGeocells().toString());
                    if (sanitationAP != null) {
                        placemarks.add(buildMainPlacemark(waterAP, sanitationAP, vmName));
                    } else {
                        log.info("No matching sanitation point found for " + waterAP.getLatitude() + ":"
                                + waterAP.getLongitude() + ":" + waterAP.getCommunityName());
                    }
                }
                techPlacemarksMap.put(key, placemarks);
            }
            return techPlacemarksMap;
        }

        return null;
    }

    private HashMap<String, String> loadContextBindings(AccessPoint waterAP, AccessPoint sanitationAP) {
        // log.info(waterAP.getCommunityCode());
        try {
            HashMap<String, String> contextBindingsMap = new HashMap<String, String>();
            if (waterAP.getCollectionDate() != null) {
                String timestamp = DateFormatUtils.formatUTC(waterAP.getCollectionDate(),
                        DateFormatUtils.ISO_DATE_FORMAT.getPattern());
                String formattedDate = DateFormat.getDateInstance(DateFormat.SHORT)
                        .format(waterAP.getCollectionDate());
                contextBindingsMap.put("collectionDate", formattedDate);
                contextBindingsMap.put("timestamp", timestamp);
                String collectionYear = new SimpleDateFormat("yyyy").format(waterAP.getCollectionDate());
                contextBindingsMap.put("collectionYear", collectionYear);
            } else {
                // TODO: This block is a problem. We should never have data
                // without a collectionDate so this is a hack so it display
                // properly until I can sort out what to do with this data.
                String timestamp = DateFormatUtils.formatUTC(new Date(),
                        DateFormatUtils.ISO_DATE_FORMAT.getPattern());
                String formattedDate = DateFormat.getDateInstance(DateFormat.SHORT).format(new Date());
                contextBindingsMap.put("collectionDate", formattedDate);
                contextBindingsMap.put("timestamp", timestamp);
                String collectionYear = new SimpleDateFormat("yyyy").format(new Date());
                contextBindingsMap.put("collectionYear", collectionYear);
            }
            contextBindingsMap.put("communityCode", encodeNullDefault(waterAP.getCommunityCode(), "Unknown"));
            contextBindingsMap.put("communityName", encodeNullDefault(waterAP.getCommunityName(), "Unknown"));
            contextBindingsMap.put("typeOfWaterPointTechnology",
                    encodeNullDefault(waterAP.getTypeTechnologyString(), "Unknown"));
            contextBindingsMap.put("constructionDateOfWaterPoint",
                    encodeNullDefault(waterAP.getConstructionDateYear(), "Unknown"));
            contextBindingsMap.put("numberOfHouseholdsUsingWaterPoint",
                    encodeNullDefault(waterAP.getNumberOfHouseholdsUsingPoint(), "Unknown"));
            contextBindingsMap.put("costPer20ML", encodeNullDefault(waterAP.getCostPer(), "Unknown"));
            contextBindingsMap.put("farthestHouseholdFromWaterPoint",
                    encodeNullDefault(waterAP.getFarthestHouseholdfromPoint(), "Unknown"));
            contextBindingsMap.put("currentManagementStructureOfWaterPoint",
                    encodeNullDefault(waterAP.getCurrentManagementStructurePoint(), "Unknown"));
            contextBindingsMap.put("waterSystemStatus", encodeStatusString(waterAP.getPointStatus()));
            contextBindingsMap.put("photoUrl", encodeNullDefault(waterAP.getPhotoURL(), "Unknown"));
            contextBindingsMap.put("waterPointPhotoCaption",
                    encodeNullDefault(waterAP.getPointPhotoCaption(), "Unknown"));
            contextBindingsMap.put("primarySanitationTechnology",
                    encodeNullDefault(sanitationAP.getTypeTechnologyString(), "Unknown"));
            contextBindingsMap.put("percentageOfHouseholdsWithImprovedSanitation",
                    encodeNullDefault(sanitationAP.getNumberOfHouseholdsUsingPoint(), "Unknown"));
            contextBindingsMap.put("photoOfPrimarySanitationtechnology",
                    encodeNullDefault(sanitationAP.getPhotoURL(), "Unknown"));
            contextBindingsMap.put("sanitationPhotoCaption",
                    encodeNullDefault(sanitationAP.getPointPhotoCaption(), "Unknown"));
            contextBindingsMap.put("footer", encodeNullDefault(waterAP.getFooter(), "Unknown"));
            contextBindingsMap.put("longitude", encodeNullDefault(waterAP.getLongitude().toString(), "Unknown"));
            contextBindingsMap.put("latitude", encodeNullDefault(waterAP.getLatitude().toString(), "Unknown"));
            contextBindingsMap.put("altitude", encodeNullDefault(waterAP.getAltitude().toString(), "0.0"));
            contextBindingsMap.put("pinStyle", encodePinStyle(waterAP.getPointType(), waterAP.getPointStatus()));
            return contextBindingsMap;
        } catch (NullPointerException nex) {
            log.log(Level.SEVERE, "Could not load context bindings", nex);
        }
        return null;
    }

    private String buildMainPlacemark(AccessPoint waterAP, AccessPoint sanitationAP, String vmName) {
        HashMap<String, String> contextBindingsMap = loadContextBindings(waterAP, sanitationAP);
        VelocityContext context = new VelocityContext();

        for (Map.Entry<String, String> entry : contextBindingsMap.entrySet()) {
            context.put(entry.getKey(), entry.getValue());
        }
        StringBuilder sb = new StringBuilder();
        String output = null;
        try {
            output = mergeContext(context, vmName);
        } catch (Exception e) {
            log.log(Level.SEVERE, "Could not build main placemark", e);
        }
        sb.append(output);

        return sb.toString();
    }

    private String encodeNullDefault(Object value, String defaultMissingVal) {
        try {
            if (value != null) {
                return value.toString();
            } else {
                return defaultMissingVal;
            }
        } catch (Exception ex) {
            // log.info("value that generated nex: " + value);
            log.log(Level.SEVERE, "Could not encode null default", ex);
        }
        return null;
    }

    public String generatePlacemarks(String vmName, String countryCode) {
        return generatePlacemarks(vmName, countryCode, GOOGLE_EARTH_DISPLAY);
    }

    public String generatePlacemarks(String vmName, String countryCode, String display) {
        StringBuilder sb = new StringBuilder();
        AccessPointDao apDAO = new AccessPointDao();
        List<AccessPoint> entries = null;
        if (countryCode.equals(Constants.ALL_RESULTS))
            entries = apDAO.list(Constants.ALL_RESULTS);
        else
            entries = apDAO.searchAccessPoints(countryCode, null, null, null, null, null, null, null, null, null,
                    null, Constants.ALL_RESULTS);

        // loop through accessPoints and bind to variables
        int i = 0;
        try {
            for (AccessPoint ap : entries) {
                if (!ap.getPointType().equals(AccessPoint.AccessPointType.SANITATION_POINT)) {
                    try {
                        VelocityContext context = new VelocityContext();
                        String pmContents = bindPlacemark(ap,
                                display.equalsIgnoreCase(GOOGLE_EARTH_DISPLAY) ? "placemarkGoogleEarth.vm"
                                        : "placemarkExternalMap.vm",
                                display, null);

                        if (ap.getCollectionDate() != null) {
                            String timestamp = DateFormatUtils.formatUTC(ap.getCollectionDate(),
                                    DateFormatUtils.ISO_DATE_FORMAT.getPattern());
                            String formattedDate = DateFormat.getDateInstance(DateFormat.SHORT)
                                    .format(ap.getCollectionDate());
                            context.put("collectionDate", formattedDate);
                            context.put("timestamp", timestamp);
                            String collectionYear = new SimpleDateFormat("yyyy").format(ap.getCollectionDate());
                            context.put("collectionYear", collectionYear);
                        } else {
                            String timestamp = DateFormatUtils.formatUTC(new Date(),
                                    DateFormatUtils.ISO_DATE_FORMAT.getPattern());
                            String formattedDate = DateFormat.getDateInstance(DateFormat.SHORT).format(new Date());
                            context.put("collectionDate", formattedDate);
                            context.put("timestamp", timestamp);
                        }
                        if (ap.getCommunityName() == null) {
                            context.put("communityName", "Unknown");
                        } else {
                            context.put("communityName", ap.getCommunityName());
                        }
                        if (ap.getCommunityCode() != null)
                            context.put("communityCode", ap.getCommunityCode());
                        else
                            context.put("communityCode", "Unknown" + new Date());
                        // Need to check this
                        if (ap.getPointType() != null) {
                            if (Boolean.parseBoolean(PropertyUtil.getProperty(DYNAMIC_SCORING_FLAG))) {

                            } else {
                                encodeStatusString(ap, context);
                                context.put("pinStyle", encodePinStyle(ap.getPointType(), ap.getPointStatus()));
                            }
                        } else {
                            context.put("pinStyle", "waterpushpinblk");
                        }
                        context.put("latitude", ap.getLatitude());
                        context.put("longitude", ap.getLongitude());
                        if (ap.getAltitude() == null)
                            context.put("altitude", 0.0);
                        else
                            context.put("altitude", ap.getAltitude());

                        context.put("balloon", pmContents);
                        String placemarkStr = mergeContext(context, vmName);
                        sb.append(placemarkStr);
                        i++;
                    } catch (Exception e) {
                        log.log(Level.INFO, "Error generating placemarks: " + ap.getCommunityCode(), e);
                    }
                }
            }
        } catch (Exception ex) {
            log.log(Level.SEVERE, "Bad access point: " + entries.get(i + 1).toString());
        }
        return sb.toString();
    }

    /**
     * attempts to merge the context with the template. The template will be resolved from cache
     * and, if there is a miss, it will use the EditorialPageDao as the templateBackingStore. If the
     * template is still not found, it will attempt to use a static template file from the
     * application context.
     * 
     * @param context
     * @param template
     * @return
     * @throws Exception
     */
    private String mergeContext(VelocityContext context, String template) throws Exception {
        return VelocityUtil.mergeContext(context, template, new VelocityUtil.TemplateCacheBackingStore() {
            @Override
            public String getByKey(String key) {
                EditorialPageDao edDao = new EditorialPageDao();
                EditorialPage page = edDao.findByTargetPage(key);
                if (page != null) {
                    return page.getTemplate().getValue();
                } else {
                    return null;
                }
            }
        });
    }

    public String bindPlacemark(SurveyedLocale ap, String vmName, String display) throws Exception {
        if (ap.getCountryCode() == null) {
            ap.setCountryCode("Unknown");
        }

        VelocityContext context = new VelocityContext();
        context.put("organization", ORGANIZATION);
        if (display != null) {
            context.put("display", display);
        }
        context.put("countryCode", ap.getCountryCode());
        if (ap.getLastSurveyedDate() != null) {
            String timestamp = DateFormatUtils.formatUTC(ap.getLastSurveyedDate(),
                    DateFormatUtils.ISO_DATE_FORMAT.getPattern());
            String formattedDate = null;
            if ("true".equals(useLongDates)) {
                formattedDate = LONG_DATE_FORMAT.get().format(ap.getLastSurveyedDate());
            } else {
                formattedDate = DateFormat.getDateInstance(DateFormat.SHORT).format(ap.getLastSurveyedDate());
            }
            context.put("collectionDate", formattedDate);
            context.put("timestamp", timestamp);
            String collectionYear = new SimpleDateFormat("yyyy").format(ap.getLastSurveyedDate());
            context.put("collectionYear", collectionYear);
        } else {
            String timestamp = DateFormatUtils.formatUTC(ap.getCreatedDateTime(),
                    DateFormatUtils.ISO_DATE_FORMAT.getPattern());
            String formattedDate = null;
            if ("true".equals(useLongDates)) {
                formattedDate = LONG_DATE_FORMAT.get().format(ap.getCreatedDateTime());
            } else {
                formattedDate = DateFormat.getDateInstance(DateFormat.SHORT).format(ap.getCreatedDateTime());
            }
            context.put("collectionDate", formattedDate);
            context.put("timestamp", timestamp);
        }

        if (ap.getIdentifier() != null) {
            context.put("identifier", ap.getIdentifier());
        } else {
            context.put("identifier", "Unknown" + new Date());
        }

        boolean foundPhoto = false;
        boolean foundStatus = false;
        if (ap.getSurveyalValues() != null) {
            // TODO: handle case where we have multiple values (with different
            // dates) for same question/metric
            List<SurveyalValue> valuesToBind = new ArrayList<SurveyalValue>(ap.getSurveyalValues());
            for (SurveyalValue val : ap.getSurveyalValues()) {
                if (val.getQuestionType() != null) {
                    if (!"free_text".equalsIgnoreCase(val.getQuestionType())
                            && !"option".equalsIgnoreCase(val.getQuestionType())) {
                        valuesToBind.remove(val);
                    }
                }
                if (val.getStringValue() == null) {
                    valuesToBind.remove(val);
                } else if (val.getStringValue().trim().toLowerCase().endsWith(".jpg")
                        || val.getStringValue().trim().toLowerCase().endsWith(".jpeg")) {
                    String urlBase = val.getStringValue();
                    if (urlBase.contains("/")) {
                        urlBase = urlBase.substring(urlBase.lastIndexOf("/") + 1);
                    }
                    if (!urlBase.toLowerCase().startsWith("http")) {
                        if (urlBase.endsWith("/")) {
                            urlBase = urlBase.substring(0, urlBase.length() - 1);
                        }
                        urlBase = PropertyUtil.getProperty("photo_url_root") + urlBase;
                    }
                    context.put("photoUrl", urlBase);
                    foundPhoto = true;
                    valuesToBind.remove(val);
                } else if (ap.getCurrentStatus() == null) {
                    if (val.getMetricName() != null
                            && val.getMetricName().trim().toLowerCase().contains("status")) {
                        context.put("waterSystemStatus", val.getStringValue());
                        foundStatus = true;
                    }
                }
            }

            context.put("surveyalValues", valuesToBind);
        }

        if (ap.getCurrentStatus() != null) {
            try {
                context.put("waterSystemStatus",
                        encodeStatusString(AccessPoint.Status.valueOf(ap.getCurrentStatus())));
            } catch (Exception e) {
                context.put("waterSystemStatus", "Unknown");
            }
        } else {
            if (!foundStatus) {
                context.put("waterSystemStatus", "Unknown");
            }
        }
        // TODO: parameterize the default logo
        if (!foundPhoto) {
            context.put("photoUrl", "http://waterforpeople.s3.amazonaws.com/images/wfplogo.jpg");
        }

        context.put("latitude", ap.getLatitude());
        context.put("longitude", ap.getLongitude());

        if (ap.getLocaleType() != null) {
            context.put("type", ap.getLocaleType());
        } else {
            context.put("type", "water");
        }

        String output = mergeContext(context, vmName);
        context = null;
        return output;
    }

    public String bindPlacemark(AccessPoint ap, String vmName, String display, StandardType standardType)
            throws Exception {
        // if (ap.getCountryCode() != null && !ap.getCountryCode().equals("MW"))
        // {
        if (display != null && display.trim().equalsIgnoreCase(GOOGLE_EARTH_DISPLAY)) {
            vmName = "placemarkGoogleEarth.vm";
        }
        if (ap.getCountryCode() == null)
            ap.setCountryCode("Unknown");
        if (ap.getCountryCode() != null) {

            VelocityContext context = new VelocityContext();
            context.put("organization", ORGANIZATION);
            if (display != null) {
                context.put("display", display);
            }
            context.put("countryCode", ap.getCountryCode());
            if (ap.getCollectionDate() != null) {
                String timestamp = DateFormatUtils.formatUTC(ap.getCollectionDate(),
                        DateFormatUtils.ISO_DATE_FORMAT.getPattern());
                String formattedDate = DateFormat.getDateInstance(DateFormat.SHORT).format(ap.getCollectionDate());
                context.put("collectionDate", formattedDate);
                context.put("timestamp", timestamp);
                String collectionYear = new SimpleDateFormat("yyyy").format(ap.getCollectionDate());
                context.put("collectionYear", collectionYear);
            } else {
                String timestamp = DateFormatUtils.formatUTC(ap.getCreatedDateTime(),
                        DateFormatUtils.ISO_DATE_FORMAT.getPattern());
                String formattedDate = DateFormat.getDateInstance(DateFormat.SHORT).format(ap.getCreatedDateTime());
                context.put("collectionDate", formattedDate);
                context.put("timestamp", timestamp);
            }

            if (ap.getCommunityCode() != null)
                context.put("communityCode", ap.getCommunityCode());
            else
                context.put("communityCode", "Unknown" + new Date());

            if (ap.getWaterForPeopleProjectFlag() != null) {
                context.put("waterForPeopleProject", encodeBooleanDisplay(ap.getWaterForPeopleProjectFlag()));
            } else {
                context.put("waterForPeopleProject", "null");
            }

            if (ap.getCurrentProblem() != null) {
                context.put("currentProblem", ap.getCurrentProblem());
            } else {
                context.put("currentProblem", ap.getCurrentProblem());
            }

            if (ap.getWaterForPeopleRole() != null) {
                context.put("waterForPeopleRole", ap.getWaterForPeopleRole());
            } else {
                context.put("waterForPeopleRole", "null");
            }

            if (ap.getPhotoURL() != null && ap.getPhotoURL().trim() != "")
                context.put("photoUrl", ap.getPhotoURL());
            else
                context.put("photoUrl", "http://waterforpeople.s3.amazonaws.com/images/wfplogo.jpg");
            if (ap.getPointType() != null) {
                if (ap.getPointType().equals(AccessPoint.AccessPointType.WATER_POINT)) {
                    context.put("typeOfPoint", "Water");
                    context.put("type", "water");
                } else if (ap.getPointType().equals(AccessPointType.SANITATION_POINT)) {
                    context.put("typeOfPoint", "Sanitation");
                    context.put("type", "sanitation");
                } else if (ap.getPointType().equals(AccessPointType.PUBLIC_INSTITUTION)) {
                    context.put("typeOfPoint", "Public Institutions");
                    context.put("type", "public_institutions");
                } else if (ap.getPointType().equals(AccessPointType.HEALTH_POSTS)) {
                    context.put("typeOfPoint", "Health Posts");
                    context.put("type", "health_posts");
                } else if (ap.getPointType().equals(AccessPointType.SCHOOL)) {
                    context.put("typeOfPoint", "School");
                    context.put("type", "school");
                }
            } else {
                context.put("typeOfPoint", "Water");
                context.put("type", "water");
            }

            if (ap.getTypeTechnologyString() == null) {
                context.put("primaryTypeTechnology", "Unknown");
            } else {
                context.put("primaryTypeTechnology", ap.getTypeTechnologyString());
            }

            if (ap.getHasSystemBeenDown1DayFlag() == null) {
                context.put("down1DayFlag", "Unknown");
            } else {
                context.put("down1DayFlag", encodeBooleanDisplay(ap.getHasSystemBeenDown1DayFlag()));
            }

            if (ap.getInstitutionName() == null) {
                context.put("institutionName", "Unknown");
            } else {
                context.put("institutionName", ap.getInstitutionName());
            }

            if (ap.getExtimatedPopulation() != null) {
                context.put("estimatedPopulation", ap.getExtimatedPopulation());
            } else {
                context.put("estimatedPopulation", "null");
            }

            if (ap.getConstructionDateYear() == null || ap.getConstructionDateYear().trim().equals("")) {
                context.put("constructionDateOfWaterPoint", "Unknown");
            } else {
                String constructionDateYear = ap.getConstructionDateYear();
                if (constructionDateYear.contains(".0")) {
                    constructionDateYear = constructionDateYear.replace(".0", "");
                }
                context.put("constructionDateOfWaterPoint", constructionDateYear);
            }
            if (ap.getNumberOfHouseholdsUsingPoint() != null) {
                context.put("numberOfHouseholdsUsingWaterPoint", ap.getNumberOfHouseholdsUsingPoint());
            } else {
                context.put("numberOfHouseholdsUsingWaterPoint", "null");
            }
            if (ap.getCostPer() == null) {
                context.put("costPer", "N/A");
            } else {
                context.put("costPer", ap.getCostPer());
            }
            if (ap.getFarthestHouseholdfromPoint() == null
                    || ap.getFarthestHouseholdfromPoint().trim().equals("")) {
                context.put("farthestHouseholdfromWaterPoint", "N/A");
            } else {
                context.put("farthestHouseholdfromWaterPoint", ap.getFarthestHouseholdfromPoint());
            }
            if (ap.getCurrentManagementStructurePoint() == null) {
                context.put("currMgmtStructure", "N/A");
            } else {
                context.put("currMgmtStructure", ap.getCurrentManagementStructurePoint());
            }
            if (ap.getPointPhotoCaption() == null || ap.getPointPhotoCaption().trim().equals("")) {
                context.put("waterPointPhotoCaption", defaultPhotoCaption);
            } else {
                context.put("waterPointPhotoCaption", ap.getPointPhotoCaption());
            }
            if (ap.getCommunityName() == null) {
                context.put("communityName", "Unknown");
            } else {
                context.put("communityName", ap.getCommunityName());
            }

            if (ap.getHeader() == null) {
                context.put("header", "Water For People");
            } else {
                context.put("header", ap.getHeader());
            }

            if (ap.getFooter() == null) {
                context.put("footer", "Water For People");
            } else {
                context.put("footer", ap.getFooter());
            }

            if (ap.getPhotoName() == null) {
                context.put("photoName", "Water For People");
            } else {
                context.put("photoName", ap.getPhotoName());
            }

            // if (ap.getCountryCode() == "RW") {

            if (ap.getMeetGovtQualityStandardFlag() == null) {
                context.put("meetGovtQualityStandardFlag", "N/A");
            } else {
                context.put("meetGovtQualityStandardFlag",
                        encodeBooleanDisplay(ap.getMeetGovtQualityStandardFlag()));
            }
            // } else {
            // context.put("meetGovtQualityStandardFlag", "unknown");
            // }
            if (ap.getMeetGovtQuantityStandardFlag() == null) {
                context.put("meetGovtQuantityStandardFlag", "N/A");
            } else {
                context.put("meetGovtQuantityStandardFlag",
                        encodeBooleanDisplay(ap.getMeetGovtQuantityStandardFlag()));
            }

            if (ap.getWhoRepairsPoint() == null) {
                context.put("whoRepairsPoint", "N/A");
            } else {
                context.put("whoRepairsPoint", ap.getWhoRepairsPoint());
            }

            if (ap.getSecondaryTechnologyString() == null) {
                context.put("secondaryTypeTechnology", "N/A");
            } else {
                context.put("secondaryTypeTechnology", ap.getSecondaryTechnologyString());
            }

            if (ap.getProvideAdequateQuantity() == null) {
                context.put("provideAdequateQuantity", "N/A");
            } else {
                context.put("provideAdequateQuantity", encodeBooleanDisplay(ap.getProvideAdequateQuantity()));
            }

            if (ap.getBalloonTitle() == null) {
                context.put("title", "Water For People");
            } else {
                context.put("title", ap.getBalloonTitle());
            }

            if (ap.getProvideAdequateQuantity() == null) {
                context.put("provideAdequateQuantity", "N/A");
            } else {
                context.put("provideAdequateQuantity", encodeBooleanDisplay(ap.getProvideAdequateQuantity()));
            }

            if (ap.getQualityDescription() != null) {
                context.put("qualityDescription", ap.getQualityDescription());
            }
            if (ap.getQuantityDescription() != null) {
                context.put("quantityDescription", ap.getQuantityDescription());
            }

            if (ap.getSub1() != null) {
                context.put("sub1", ap.getSub1());
            }
            if (ap.getSub2() != null) {
                context.put("sub2", ap.getSub2());
            }
            if (ap.getSub3() != null) {
                context.put("sub3", ap.getSub3());
            }
            if (ap.getSub4() != null) {
                context.put("sub4", ap.getSub4());
            }
            if (ap.getSub5() != null) {
                context.put("sub5", ap.getSub5());
            }
            if (ap.getSub6() != null) {
                context.put("sub6", ap.getSub6());
            }

            if (ap.getAccessPointCode() != null) {
                context.put("accessPointCode", ap.getAccessPointCode());
            }

            if (ap.getAccessPointUsage() != null) {
                context.put("accessPointUsage", ap.getAccessPointUsage());
            }

            if (ap.getDescription() != null)
                context.put("description", ap.getDescription());
            else
                context.put("description", "Unknown");

            // Need to check this
            if (ap.getPointType() != null) {
                if (Boolean.parseBoolean(PropertyUtil.getProperty(DYNAMIC_SCORING_FLAG))) {
                    TreeMap<String, String> combinedScore = fetchLevelOfServiceScoreStatus(ap);
                    for (Map.Entry<String, String> entry : combinedScore.entrySet()) {
                        context.put(entry.getKey(), entry.getValue());
                        String style = null;
                        if (standardType != null) {
                            if (standardType.equals(StandardType.WaterPointLevelOfService) && entry.getKey()
                                    .equals(StandardType.WaterPointLevelOfService.toString() + "-pinStyle")) {
                                style = entry.getValue();
                            } else if (standardType.equals(StandardType.WaterPointSustainability) && entry.getKey()
                                    .equals(StandardType.WaterPointSustainability.toString() + "-pinStyle")) {
                                style = entry.getValue();
                            }
                        }
                        context.put("pinStyle", style);
                    }
                } else {
                    encodeStatusString(ap, context);
                    context.put("pinStyle", encodePinStyle(ap.getPointType(), ap.getPointStatus()));
                }
            } else {
                context.put("pinStyle", "waterpushpinblk");
            }
            String output = mergeContext(context, vmName);
            context = null;
            return output;

        }
        return null;

    }

    private TreeMap<String, String> fetchLevelOfServiceScoreStatus(AccessPoint ap) {
        TreeMap<String, String> losStyles = new TreeMap<String, String>();
        LevelOfServiceScoreDao losScoreDao = new LevelOfServiceScoreDao();
        List<LevelOfServiceScore> losList = losScoreDao.listByAccessPoint(ap.getKey());
        LOSScoreToStatusMappingDao losScoreToStatusMappingDao = new LOSScoreToStatusMappingDao();
        for (LevelOfServiceScore losItem : losList) {
            LOSScoreToStatusMapping losScoreToStatusMapping = losScoreToStatusMappingDao
                    .findByLOSScoreTypeAndScore(losItem.getScoreType(), losItem.getScore());
            losStyles.put(losScoreToStatusMapping.getLevelOfServiceScoreType().toString() + "-color",
                    losScoreToStatusMapping.getColor().toString());
            losStyles.put(losScoreToStatusMapping.getLevelOfServiceScoreType().toString() + "-desc",
                    losScoreToStatusMapping.getDescription());
            losStyles.put(losScoreToStatusMapping.getLevelOfServiceScoreType().toString() + "-score",
                    losItem.getScore().toString());
            losStyles.put(losScoreToStatusMapping.getLevelOfServiceScoreType().toString() + "-pinstyle",
                    losScoreToStatusMapping.getIconStyle());
            losStyles.put(losScoreToStatusMapping.getLevelOfServiceScoreType().toString() + "-iconSmallUrl",
                    losScoreToStatusMapping.getIconSmallUrl());
            // losStyles.put(losScoreToStatusMapping.getLevelOfServiceScoreType().toString()+"details",
            // losItem.getScoreDetails().toString());

        }
        return losStyles;
    }

    public String generateRegionOutlines(String vmName) {
        StringBuilder sb = new StringBuilder();
        GeoRegionDAO grDAO = new GeoRegionDAO();
        List<GeoRegion> grList = grDAO.list();
        try {
            if (grList != null && grList.size() > 0) {
                String currUUID = grList.get(0).getUuid();
                VelocityContext context = new VelocityContext();
                StringBuilder sbCoor = new StringBuilder();

                // loop through GeoRegions and bind to variables
                for (int i = 0; i < grList.size(); i++) {
                    GeoRegion gr = grList.get(i);

                    if (currUUID.equals(gr.getUuid())) {
                        sbCoor.append(gr.getLongitude() + "," + gr.getLatitiude() + "," + 0 + "\n");
                    } else {
                        currUUID = gr.getUuid();
                        context.put("coordinateString", sbCoor.toString());
                        sb.append(mergeContext(context, vmName));

                        context = new VelocityContext();
                        sbCoor = new StringBuilder();
                        sbCoor.append(gr.getLongitude() + "," + gr.getLatitiude() + "," + 0 + "\n");
                    }
                }

                context.put("coordinateString", sbCoor.toString());
                sb.append(mergeContext(context, vmName));
                return sb.toString();
            }

        } catch (Exception e) {
            log.log(Level.SEVERE, "Error generating region outlines", e);
        }
        return "";
    }

    private String encodeBooleanDisplay(Boolean value) {
        if (value) {
            return "Yes";
        } else {
            return "No";
        }
    }

    public LOSScoreToStatusMapping encodePinStyle(Key accessPointKey, StandardType standardType) {
        LevelOfServiceScoreDao losScoreDao = new LevelOfServiceScoreDao();
        LevelOfServiceScore losScore = losScoreDao.findByAccessPoint(accessPointKey, standardType);
        LOSScoreToStatusMappingDao losMapDao = new LOSScoreToStatusMappingDao();
        LOSScoreToStatusMapping losMapItem = losMapDao.findByLOSScoreTypeAndScore(standardType,
                losScore.getScore());
        return losMapItem;
    }

    public static String encodePinStyle(AccessPointType type, AccessPoint.Status status) {
        String prefix = "water";
        if (AccessPointType.SANITATION_POINT == type) {
            prefix = "sani";
        } else if (AccessPointType.SCHOOL == type) {
            prefix = "schwater";
        } else if (AccessPointType.PUBLIC_INSTITUTION == type || AccessPointType.HEALTH_POSTS == type) {
            prefix = "pubwater";
        }
        if (AccessPoint.Status.FUNCTIONING_HIGH == status) {
            return prefix + "pushpingreen";
        } else if (AccessPoint.Status.FUNCTIONING_OK == status
                || AccessPoint.Status.FUNCTIONING_WITH_PROBLEMS == status) {
            return prefix + "pushpinyellow";
        } else if (AccessPoint.Status.BROKEN_DOWN == status) {
            return prefix + "pushpinred";
        } else if (AccessPoint.Status.NO_IMPROVED_SYSTEM == status) {
            return prefix + "pushpinblk";
        } else {
            return prefix + "pushpinblk";
        }
    }

    public static String encodePinStyle(String type, String status) {
        String prefix = "water";
        if (type != null) {
            if ("SanitationPoint".equalsIgnoreCase(type)) {
                prefix = "sani";
            } else if ("School".equalsIgnoreCase(type)) {
                prefix = "schwater";
            } else if ("PublicInstitution".equalsIgnoreCase(type)) {
                prefix = "pubwater";
            }
        }

        if ("FUNCTIONING_HIGH".equalsIgnoreCase(status)) {
            return prefix + "pushpingreen";
        } else if ("FUNCTIONING_OK".equalsIgnoreCase(status)
                || "FUNCTIONING_WITH_PROBLEMS".equalsIgnoreCase(status)) {
            return prefix + "pushpinyellow";
        } else if ("BROKEN_DOWN".equalsIgnoreCase(status)) {
            return prefix + "pushpinred";
        } else if ("NO_IMPROVED_SYSTEM".equalsIgnoreCase(status)) {
            return prefix + "pushpinblk";
        } else {
            return prefix + "pushpinblk";
        }
    }

    private String encodeStatusString(AccessPoint ap, VelocityContext context) {
        AccessPoint.Status status = ap.getPointStatus();
        if (ap.getCollectionDate() == null || ap.getCollectionDate().before(new Date("01/01/2011")) || !useScore) {
            if (status != null) {
                if (AccessPoint.Status.FUNCTIONING_HIGH == status) {
                    context.put("waterSystemStatus", "Meets Government Standards");
                    return "System Functioning and Meets Government Standards";
                } else if (AccessPoint.Status.FUNCTIONING_OK == status
                        || AccessPoint.Status.FUNCTIONING_WITH_PROBLEMS == status) {
                    context.put("waterSystemStatus", "Functioning but with Problems");
                    return "Functioning but with Problems";
                } else if (AccessPoint.Status.BROKEN_DOWN == status) {
                    context.put("waterSystemStatus", "Broken-down system");
                    return "Broken-down system";
                } else if (AccessPoint.Status.NO_IMPROVED_SYSTEM == status) {
                    context.put("waterSystemStatus", "No Improved System");
                    return "No Improved System";
                } else {
                    context.put("waterSystemStatus", "Unknown");
                    return "Unknown";
                }
            } else {
                context.put("waterSystemStatus", "Unknown");
                return "Unknown";
            }
        } else {
            String statusString = null;
            try {
                statusString = encodeStatusUsingScore(ap);
            } catch (Exception ex) {
                log.log(Level.INFO, "Couldn't score  ap: " + ap.toString() + " " + ex);
            }
            if (statusString == null) {
                statusString = "Unknown";
            }
            context.put("waterSystemStatus", statusString);
            // will remove soon not necessary now that APs are getting scored on
            // save
            AccessPointDao apDao = new AccessPointDao();
            apDao.save(ap);
            return statusString;
        }
    }

    private String encodeStatusString(AccessPoint.Status status) {
        if (status == null) {
            return "Unknown";
        }
        if (status.equals(AccessPoint.Status.FUNCTIONING_HIGH)) {
            return "System Functioning and Meets Government Standards";
        } else if (status.equals(AccessPoint.Status.FUNCTIONING_OK)
                || status.equals(AccessPoint.Status.FUNCTIONING_WITH_PROBLEMS)) {
            return "Functioning but with Problems";
        } else if (status.equals(AccessPoint.Status.BROKEN_DOWN)) {
            return "Broken-down system";
        } else if (status.equals(AccessPoint.Status.NO_IMPROVED_SYSTEM)) {
            return "No Improved System";
        } else {
            return "Unknown";
        }
    }

    public String encodeStatusUsingScore(AccessPoint ap) throws InvocationTargetException, NoSuchMethodException {
        Integer score = AccessPointHelper.scoreAccessPoint(ap).getScore();

        new AccessPointHelper().scoreAccessPointDynamic(ap).getScore();

        if (score == 0) {
            return "No Improved System";
        } else if (score >= 1 && score <= 2) {
            return "Basic Level Service";
        } else if (score >= 3 && score <= 4) {
            return "Intermediate Level Service";
        } else if (score >= 5) {
            return "High Level Service";
        } else {
            return "Unknown";
        }
    }
}