diff --git a/pom.xml b/pom.xml
index afc61e4..93d67b0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,17 +2,41 @@
4.0.0
-
- nfms
- org.fao.unredd
- 2.0-SNAPSHOT
-
org.fao.unredd
portal
+ 3.0
war
UNREDD Portal
http://maven.apache.org
+
+
+ UTF-8
+ 9.2
+
+
+ scm:git:git://github.com/nfms4redd/nfms.git
+ scm:git:https://github.com/nfms4redd/nfms.git
+ https://github.com/nfms4redd/nfms.git
+ HEAD
+
+
+
+ nfms4redd
+ ftp://maven.nfms4redd.org/repo
+ false
+
+
+
+ osgeo
+ Open Source Geospatial Foundation Repository
+ http://download.osgeo.org/webdav/geotools/
+
+
+ nfms4redd
+ nfms4redd maven repository
+ http://maven.nfms4redd.org/
+
EclipseLink
http://download.eclipse.org/rt/eclipselink/maven.repo
@@ -28,6 +52,7 @@
junit
junit
test
+ 4.8.2
org.mockito
@@ -35,6 +60,12 @@
1.9.5
test
+
+ org.codehaus.jackson
+ jackson-mapper-asl
+ 1.9.12
+ test
+
net.sf.json-lib
json-lib
@@ -46,24 +77,15 @@
commons-io
1.3.2
-
- org.springframework
- spring-webmvc
- 3.1.1.RELEASE
-
net.tanesha.recaptcha4j
recaptcha4j
0.0.7
-
- org.fao.unredd
- commons
- 2.0-SNAPSHOT
-
org.apache.velocity
velocity
+ 1.7
@@ -84,31 +106,10 @@
javax.servlet
- servlet-api
- 2.5
+ javax.servlet-api
+ 3.0.1
provided
-
- javax.servlet.jsp
- jsp-api
- 2.1
- provided
-
-
- javax.servlet
- jstl
- 1.1.2
-
-
- taglibs
- standard
- 1.1.2
-
-
- net.sf.packtag
- packtag-core
- 3.8
-
org.slf4j
@@ -136,6 +137,24 @@
unredd-portal
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 1.6
+ 1.6
+
+
+
+ com.mycila.maven-license-plugin
+ maven-license-plugin
+
+
+
+ **/*.java
+
+
+
org.apache.maven.plugins
maven-eclipse-plugin
@@ -161,5 +180,12 @@
+
+
+ org.apache.maven.wagon
+ wagon-ftp
+ 2.3
+
+
diff --git a/src/main/assembly/portal.properties b/src/main/assembly/portal.properties
index 0c1c6f1..467040a 100644
--- a/src/main/assembly/portal.properties
+++ b/src/main/assembly/portal.properties
@@ -1,4 +1,7 @@
languages = {"en": "English", "fr": "Fran\u00e7ais", "es": "Espa\u00f1ol"}
recaptcha.publickey = 6Ld5ydQSAAAAAGtZJG67QkQM7Z13X6MGf72RtmDE
recaptcha.privatekey = 6Ld5ydQSAAAAAJW3To_tN6czS7C-HCnsBVhENfD9
-layers.rootFolder=/var/portal/indicators
+layers.rootFolder=/tmp
+info.queryUrl=http://demo1.geo-solutions.it/diss_geoserver/wms
+info.layerUrl=http://demo1.geo-solutions.it/diss_geoserver/gwc/service/wms
+client.modules=layers,communication,iso8601,error-management,map,banner,toolbar,time-slider,layer-list,info-control,info-dialog,center,zoom-bar,layer-list-selector,active-layer-list,legend-button,legend-panel
\ No newline at end of file
diff --git a/src/main/java/org/fao/unredd/AppContextListener.java b/src/main/java/org/fao/unredd/AppContextListener.java
new file mode 100644
index 0000000..8cdfb3a
--- /dev/null
+++ b/src/main/java/org/fao/unredd/AppContextListener.java
@@ -0,0 +1,37 @@
+package org.fao.unredd;
+
+import java.io.File;
+import java.util.Properties;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.fao.unredd.layers.LayerFactory;
+import org.fao.unredd.layers.folder.FolderLayerFactory;
+import org.fao.unredd.portal.Config;
+
+public class AppContextListener implements ServletContextListener {
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+ ServletContext servletContext = sce.getServletContext();
+ String rootPath = servletContext.getRealPath("/");
+ String configInitParameter = servletContext
+ .getInitParameter("PORTAL_CONFIG_DIR");
+ Config config = new Config(rootPath, configInitParameter);
+ servletContext.setAttribute("config", config);
+
+ Properties configurationProperties = config.getProperties();
+ String indicatorsFolder = configurationProperties
+ .getProperty("layers.rootFolder");
+ LayerFactory layerFactory = new FolderLayerFactory(new File(
+ indicatorsFolder));
+ servletContext.setAttribute("layer-factory", layerFactory);
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce) {
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/charts/generated/DataType.java b/src/main/java/org/fao/unredd/charts/generated/DataType.java
new file mode 100644
index 0000000..45b226b
--- /dev/null
+++ b/src/main/java/org/fao/unredd/charts/generated/DataType.java
@@ -0,0 +1,104 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.2-hudson-jaxb-ri-2.2-63-
+// See http://java.sun.com/xml/jaxb
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2013.10.11 at 09:17:10 AM CEST
+//
+
+
+package org.fao.unredd.charts.generated;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
value;
+ @XmlAttribute(name = "zone-id", required = true)
+ protected String zoneId;
+
+ /**
+ * Gets the value of the value property.
+ *
+ *
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a set
method for the value property.
+ *
+ *
+ * For example, to add a new item, do as follows:
+ *
+ * getValue().add(newItem);
+ *
+ *
+ *
+ *
+ * Objects of the following type(s) are allowed in the list
+ * {@link Double }
+ *
+ *
+ */
+ public List getValue() {
+ if (value == null) {
+ value = new ArrayList();
+ }
+ return this.value;
+ }
+
+ /**
+ * Gets the value of the zoneId property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getZoneId() {
+ return zoneId;
+ }
+
+ /**
+ * Sets the value of the zoneId property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setZoneId(String value) {
+ this.zoneId = value;
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/charts/generated/LabelType.java b/src/main/java/org/fao/unredd/charts/generated/LabelType.java
new file mode 100644
index 0000000..fd1bfd9
--- /dev/null
+++ b/src/main/java/org/fao/unredd/charts/generated/LabelType.java
@@ -0,0 +1,76 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.2-hudson-jaxb-ri-2.2-63-
+// See http://java.sun.com/xml/jaxb
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2013.10.11 at 09:17:10 AM CEST
+//
+
+
+package org.fao.unredd.charts.generated;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ * Java class for LabelType complex type.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <complexType name="LabelType">
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element name="label" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded"/>
+ * </sequence>
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "LabelType", propOrder = {
+ "label"
+})
+public class LabelType {
+
+ @XmlElement(required = true)
+ protected List label;
+
+ /**
+ * Gets the value of the label property.
+ *
+ *
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a set
method for the label property.
+ *
+ *
+ * For example, to add a new item, do as follows:
+ *
+ * getLabel().add(newItem);
+ *
+ *
+ *
+ *
+ * Objects of the following type(s) are allowed in the list
+ * {@link String }
+ *
+ *
+ */
+ public List getLabel() {
+ if (label == null) {
+ label = new ArrayList();
+ }
+ return this.label;
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/charts/generated/ObjectFactory.java b/src/main/java/org/fao/unredd/charts/generated/ObjectFactory.java
new file mode 100644
index 0000000..25ab500
--- /dev/null
+++ b/src/main/java/org/fao/unredd/charts/generated/ObjectFactory.java
@@ -0,0 +1,63 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.2-hudson-jaxb-ri-2.2-63-
+// See http://java.sun.com/xml/jaxb
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2013.10.11 at 09:17:10 AM CEST
+//
+
+
+package org.fao.unredd.charts.generated;
+
+import javax.xml.bind.annotation.XmlRegistry;
+
+
+/**
+ * This object contains factory methods for each
+ * Java content interface and Java element interface
+ * generated in the org.fao.unredd.charts.generated package.
+ * An ObjectFactory allows you to programatically
+ * construct new instances of the Java representation
+ * for XML content. The Java representation of XML
+ * content can consist of schema derived interfaces
+ * and classes representing the binding of schema
+ * type definitions, element declarations and model
+ * groups. Factory methods for each of these are
+ * provided in this class.
+ *
+ */
+@XmlRegistry
+public class ObjectFactory {
+
+
+ /**
+ * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.fao.unredd.charts.generated
+ *
+ */
+ public ObjectFactory() {
+ }
+
+ /**
+ * Create an instance of {@link StatisticsChartInput }
+ *
+ */
+ public StatisticsChartInput createStatisticsChartInput() {
+ return new StatisticsChartInput();
+ }
+
+ /**
+ * Create an instance of {@link LabelType }
+ *
+ */
+ public LabelType createLabelType() {
+ return new LabelType();
+ }
+
+ /**
+ * Create an instance of {@link DataType }
+ *
+ */
+ public DataType createDataType() {
+ return new DataType();
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/charts/generated/StatisticsChartInput.java b/src/main/java/org/fao/unredd/charts/generated/StatisticsChartInput.java
new file mode 100644
index 0000000..bc6bfbb
--- /dev/null
+++ b/src/main/java/org/fao/unredd/charts/generated/StatisticsChartInput.java
@@ -0,0 +1,293 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.2-hudson-jaxb-ri-2.2-63-
+// See http://java.sun.com/xml/jaxb
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2013.10.11 at 09:17:10 AM CEST
+//
+
+
+package org.fao.unredd.charts.generated;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ *
Java class for anonymous complex type.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <complexType>
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element name="title" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ * <element name="subtitle" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ * <element name="y-label" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ * <element name="units" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ * <element name="tooltipDecimals" type="{http://www.w3.org/2001/XMLSchema}int"/>
+ * <element name="hover" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ * <element name="footer" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ * <element name="labels" type="{http://www.nfms.unredd.fao.org/statistics-chart}LabelType"/>
+ * <element name="data" type="{http://www.nfms.unredd.fao.org/statistics-chart}DataType" maxOccurs="unbounded"/>
+ * </sequence>
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+ "title",
+ "subtitle",
+ "yLabel",
+ "units",
+ "tooltipDecimals",
+ "hover",
+ "footer",
+ "labels",
+ "data"
+})
+@XmlRootElement(name = "statistics-chart-input")
+public class StatisticsChartInput {
+
+ @XmlElement(required = true)
+ protected String title;
+ @XmlElement(required = true)
+ protected String subtitle;
+ @XmlElement(name = "y-label", required = true)
+ protected String yLabel;
+ @XmlElement(required = true)
+ protected String units;
+ protected int tooltipDecimals;
+ @XmlElement(required = true)
+ protected String hover;
+ @XmlElement(required = true)
+ protected String footer;
+ @XmlElement(required = true)
+ protected LabelType labels;
+ @XmlElement(required = true)
+ protected List data;
+
+ /**
+ * Gets the value of the title property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * Sets the value of the title property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setTitle(String value) {
+ this.title = value;
+ }
+
+ /**
+ * Gets the value of the subtitle property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getSubtitle() {
+ return subtitle;
+ }
+
+ /**
+ * Sets the value of the subtitle property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setSubtitle(String value) {
+ this.subtitle = value;
+ }
+
+ /**
+ * Gets the value of the yLabel property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getYLabel() {
+ return yLabel;
+ }
+
+ /**
+ * Sets the value of the yLabel property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setYLabel(String value) {
+ this.yLabel = value;
+ }
+
+ /**
+ * Gets the value of the units property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getUnits() {
+ return units;
+ }
+
+ /**
+ * Sets the value of the units property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setUnits(String value) {
+ this.units = value;
+ }
+
+ /**
+ * Gets the value of the tooltipDecimals property.
+ *
+ */
+ public int getTooltipDecimals() {
+ return tooltipDecimals;
+ }
+
+ /**
+ * Sets the value of the tooltipDecimals property.
+ *
+ */
+ public void setTooltipDecimals(int value) {
+ this.tooltipDecimals = value;
+ }
+
+ /**
+ * Gets the value of the hover property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getHover() {
+ return hover;
+ }
+
+ /**
+ * Sets the value of the hover property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setHover(String value) {
+ this.hover = value;
+ }
+
+ /**
+ * Gets the value of the footer property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getFooter() {
+ return footer;
+ }
+
+ /**
+ * Sets the value of the footer property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setFooter(String value) {
+ this.footer = value;
+ }
+
+ /**
+ * Gets the value of the labels property.
+ *
+ * @return
+ * possible object is
+ * {@link LabelType }
+ *
+ */
+ public LabelType getLabels() {
+ return labels;
+ }
+
+ /**
+ * Sets the value of the labels property.
+ *
+ * @param value
+ * allowed object is
+ * {@link LabelType }
+ *
+ */
+ public void setLabels(LabelType value) {
+ this.labels = value;
+ }
+
+ /**
+ * Gets the value of the data property.
+ *
+ *
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a set
method for the data property.
+ *
+ *
+ * For example, to add a new item, do as follows:
+ *
+ * getData().add(newItem);
+ *
+ *
+ *
+ *
+ * Objects of the following type(s) are allowed in the list
+ * {@link DataType }
+ *
+ *
+ */
+ public List getData() {
+ if (data == null) {
+ data = new ArrayList();
+ }
+ return this.data;
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/charts/generated/package-info.java b/src/main/java/org/fao/unredd/charts/generated/package-info.java
new file mode 100644
index 0000000..18140a3
--- /dev/null
+++ b/src/main/java/org/fao/unredd/charts/generated/package-info.java
@@ -0,0 +1,9 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.2-hudson-jaxb-ri-2.2-63-
+// See http://java.sun.com/xml/jaxb
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2013.10.11 at 09:17:10 AM CEST
+//
+
+@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.nfms.unredd.fao.org/statistics-chart", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
+package org.fao.unredd.charts.generated;
diff --git a/src/main/java/org/fao/unredd/layers/CannotFindLayerException.java b/src/main/java/org/fao/unredd/layers/CannotFindLayerException.java
new file mode 100644
index 0000000..1399a74
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/CannotFindLayerException.java
@@ -0,0 +1,29 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+/**
+ * The {@link DataLocator} of the layer cannot be obtained
+ *
+ * @author fergonco
+ */
+public class CannotFindLayerException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public CannotFindLayerException(String layerName) {
+ super("The location of the layer cannot be found: " + layerName);
+ }
+}
diff --git a/src/main/java/org/fao/unredd/layers/DBLocation.java b/src/main/java/org/fao/unredd/layers/DBLocation.java
new file mode 100644
index 0000000..1a2c589
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/DBLocation.java
@@ -0,0 +1,86 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+import java.io.File;
+import java.io.IOException;
+
+public class DBLocation implements Location {
+
+ private String host;
+ private String port;
+ private String database;
+ private String schema;
+ private String tableName;
+ private String user;
+
+ public DBLocation(String host, String port, String database, String schema,
+ String tableName, String user) {
+ this.host = host;
+ this.port = port;
+ this.database = database;
+ this.schema = schema;
+ this.tableName = tableName;
+ this.user = user;
+ }
+
+ @Override
+ public String getGDALString(PasswordGetter passwordGetter)
+ throws IOException {
+ return getConnectionInfo() + " password="
+ + passwordGetter.getPassword(getConnectionInfo()) + "\"";
+ }
+
+ private String getConnectionInfo() {
+ return "PG:\"host=" + host + " port=" + port + " dbname=" + database
+ + " user=" + user;
+ }
+
+ @Override
+ public String getGDALFeatureName() {
+ return schema + "." + tableName;
+ }
+
+ @Override
+ public File getFile() {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return getConnectionInfo();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof DBLocation) {
+ DBLocation that = (DBLocation) obj;
+ return this.host.equals(that.host) && this.port.equals(that.port)
+ && this.database.equals(that.database)
+ && this.schema.equals(that.schema)
+ && this.tableName.equals(that.tableName)
+ && this.user.equals(that.user);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return host.hashCode() + port.hashCode() + database.hashCode()
+ + schema.hashCode() + tableName.hashCode() + user.hashCode();
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/layers/DataLocator.java b/src/main/java/org/fao/unredd/layers/DataLocator.java
new file mode 100644
index 0000000..a354813
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/DataLocator.java
@@ -0,0 +1,38 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+import java.io.IOException;
+
+/**
+ * Interface that obtains a {@link Location} instance to locate the place where
+ * {@link Layer}s have their data
+ *
+ * @author fergonco
+ */
+public interface DataLocator {
+
+ /**
+ * @param layer
+ * @return
+ * @throws IOException
+ * If there is a problem obtaining the layer location
+ * @throws CannotFindLayerException
+ * If the layer location cannot be found
+ */
+ Location locate(Layer layer) throws IOException, CannotFindLayerException;
+
+}
diff --git a/src/main/java/org/fao/unredd/layers/FileLocation.java b/src/main/java/org/fao/unredd/layers/FileLocation.java
new file mode 100644
index 0000000..94b608b
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/FileLocation.java
@@ -0,0 +1,61 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+import java.io.File;
+
+public class FileLocation implements Location {
+ private File file;
+
+ public FileLocation(File file) {
+ this.file = file;
+ }
+
+ @Override
+ public String getGDALString(PasswordGetter passwordGetter) {
+ return file.getAbsolutePath();
+ }
+
+ @Override
+ public String getGDALFeatureName() {
+ String name = file.getName();
+ return name.substring(0, name.lastIndexOf('.'));
+ }
+
+ @Override
+ public File getFile() {
+ return file;
+ }
+
+ @Override
+ public String toString() {
+ return "File: " + file.getAbsolutePath();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof FileLocation) {
+ FileLocation that = (FileLocation) obj;
+ return that.file.equals(this.file);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return file.hashCode();
+ }
+}
diff --git a/src/main/java/org/fao/unredd/layers/GeoserverDataLocator.java b/src/main/java/org/fao/unredd/layers/GeoserverDataLocator.java
new file mode 100644
index 0000000..04f513b
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/GeoserverDataLocator.java
@@ -0,0 +1,168 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import com.sun.jndi.toolkit.url.Uri;
+
+/**
+ * Implementation based on GeoServer
+ *
+ * @author fergonco
+ */
+public class GeoserverDataLocator implements DataLocator {
+
+ private File geoserverDataDir;
+
+ public GeoserverDataLocator(File geoserverDataDir) {
+ this.geoserverDataDir = geoserverDataDir;
+ }
+
+ @Override
+ public Location locate(final Layer layer) throws CannotFindLayerException,
+ IOException {
+ final File workspacesFolder = new File(geoserverDataDir, "workspaces");
+ File layerFolder = find(workspacesFolder, new FileFilter() {
+
+ @Override
+ public boolean accept(File file) {
+ File workspace = file.getParentFile().getParentFile();
+ return file.isDirectory()
+ && file.getName().equals(layer.getName())
+ && workspace.getName().equals(layer.getWorkspace())
+ && workspace.getParentFile().equals(workspacesFolder);
+ }
+
+ });
+ if (layerFolder == null) {
+ throw new CannotFindLayerException(layer.getQualifiedName());
+ }
+ File coverageStore = new File(layerFolder.getParentFile(),
+ "coveragestore.xml");
+ File dataStore = new File(layerFolder.getParentFile(), "datastore.xml");
+ try {
+ if (coverageStore.exists()) {
+ String url = xpath(coverageStore, "/coverageStore/url");
+ File file = getFile(url);
+ return new FileLocation(file);
+ } else if (dataStore.exists()) {
+ String type = xpath(dataStore, "/dataStore/type");
+ if (type.equals("Shapefile")) {
+ String url = xpath(dataStore,
+ getDatastoreXPathConnectionParameter("url"));
+ File file = getFile(url);
+ return new FileLocation(file);
+ } else if (type
+ .equals("Directory of spatial files (shapefiles)")) {
+ String url = xpath(dataStore,
+ getDatastoreXPathConnectionParameter("url"));
+ String fileName = xpath(new File(layerFolder,
+ "featuretype.xml"), "/featureType/nativeName");
+ fileName = fileName + ".shp";
+ File file = new File(getFile(url), fileName);
+ return new FileLocation(file);
+ } else if (type.equals("PostGIS")) {
+ String host = xpath(dataStore,
+ getDatastoreXPathConnectionParameter("host"));
+ String port = xpath(dataStore,
+ getDatastoreXPathConnectionParameter("port"));
+ String user = xpath(dataStore,
+ getDatastoreXPathConnectionParameter("user"));
+ String schema = xpath(dataStore,
+ getDatastoreXPathConnectionParameter("schema"));
+ String database = xpath(dataStore,
+ getDatastoreXPathConnectionParameter("database"));
+ String tableName = xpath(new File(layerFolder,
+ "featuretype.xml"), "/featureType/nativeName");
+ return new DBLocation(host, port, database, schema,
+ tableName, user);
+ } else {
+ throw new UnsupportedOperationException(
+ "Unsupported type: " + type);
+ }
+ } else {
+ throw new IllegalArgumentException(
+ "Can only locate vector or raster layers");
+ }
+ } catch (XPathExpressionException e) {
+ throw new RuntimeException("bug", e);
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException("bug", e);
+ } catch (SAXException e) {
+ throw new IOException("Cannot parse the geoserver data files", e);
+ }
+ }
+
+ private String getDatastoreXPathConnectionParameter(String key) {
+ return "/dataStore/connectionParameters/entry[@key='" + key + "']";
+ }
+
+ private File getFile(String url) throws MalformedURLException {
+ String path = new Uri(url).getPath();
+ File file = new File(path);
+ if (!file.isAbsolute()) {
+ file = new File(geoserverDataDir, path);
+ }
+ return file;
+ }
+
+ private File find(File folder, FileFilter fileFilter) {
+ if (fileFilter.accept(folder)) {
+ return folder;
+ } else if (folder.isDirectory()) {
+ File[] files = folder.listFiles();
+ for (File file : files) {
+ File find = find(file, fileFilter);
+ if (find != null) {
+ return find;
+ }
+ }
+ }
+ return null;
+ }
+
+ private String xpath(File file, String expression)
+ throws XPathExpressionException, ParserConfigurationException,
+ SAXException, IOException {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
+ file));
+ Document doc = builder.parse(bis);
+ bis.close();
+ XPathFactory xPathfactory = XPathFactory.newInstance();
+ XPath xpath = xPathfactory.newXPath();
+ XPathExpression expr = xpath.compile(expression);
+ return expr.evaluate(doc);
+ }
+}
diff --git a/src/main/java/org/fao/unredd/layers/Layer.java b/src/main/java/org/fao/unredd/layers/Layer.java
new file mode 100644
index 0000000..72f7430
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/Layer.java
@@ -0,0 +1,110 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Represents the configuration for a layer. Layers have a name that can be used
+ * to identify them in some catalog (typically, a GeoServer instance), it is
+ * possible to associate temporal files in the work area that are persistent
+ * over time and it is possible also to obtain configuration associated to the
+ * layer
+ *
+ * @author fergonco
+ */
+public interface Layer {
+
+ /**
+ * Get a list of all the output identifiers in this layer
+ *
+ * @return
+ * @throws IOException
+ */
+ Outputs getOutputs() throws IOException;
+
+ /**
+ * Get the output content with the specified id
+ *
+ * @param outputId
+ * @return
+ * @throws NoSuchIndicatorException
+ */
+ String getOutput(String outputId) throws NoSuchIndicatorException,
+ IOException;
+
+ /**
+ * Sets the content of the output with the specified id. If the output
+ * already exists it is overwritten, otherwise it is created. The output
+ * shall contain information for every object in the layer identified
+ * uniquely by the value of the field specified by fieldId.
+ *
+ * @param id
+ * @param outputName
+ * @param fieldId
+ * @param content
+ * @throws IOException
+ */
+ void setOutput(String id, String outputName, String fieldId, String content)
+ throws IOException;
+
+ /**
+ * Associates a file in the layer work area to the layer. The file is
+ * identified by the specified id. Every call to this method with the same
+ * parameters should result in the same File being returned.
+ *
+ * @param id
+ * @return
+ */
+ File getWorkFile(String id);
+
+ /**
+ * Get the workspace part of the layer name. If {@link #getQualifiedName()}
+ * returns "work:lay", this method will return "work"
+ *
+ * @return
+ */
+ String getWorkspace();
+
+ /**
+ * Get the name part of the layer name. If {@link #getQualifiedName()}
+ * returns "work:lay", this method will return "lay"
+ *
+ * @return
+ */
+ String getName();
+
+ /**
+ * The same as {@link #getWorkspace()} + ":" + {@link #getName()}
+ *
+ * @return
+ */
+ String getQualifiedName();
+
+ /**
+ * Gets the contents of a configuration item identified by the specified id
+ *
+ * @param id
+ * @return
+ * @throws NoSuchConfigurationException
+ * If the configuration item does not exist
+ * @throws IOException
+ * If there is any problem accessing the configuration item
+ */
+ String getConfiguration(String id) throws NoSuchConfigurationException,
+ IOException;
+}
diff --git a/src/main/java/org/fao/unredd/layers/LayerFactory.java b/src/main/java/org/fao/unredd/layers/LayerFactory.java
new file mode 100644
index 0000000..84b422d
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/LayerFactory.java
@@ -0,0 +1,58 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+import java.io.IOException;
+
+/**
+ * Builds instances to manage the layer configurations. Layer names are
+ * qualified with the workspace name like this: workspace_name:layer_name
+ *
+ * @author fergonco
+ */
+public interface LayerFactory {
+
+ /**
+ * Returns an instance to manage the layer configuration, building the
+ * folder for its configuration if necessary
+ *
+ * @param layerName
+ * @return
+ * @throws IOException
+ * If the layer folder does not exist and cannot be created
+ */
+ Layer newLayer(String layerName) throws IOException;
+
+ /**
+ * Creates a new layer, building the folder for it's configuration if
+ * necessary
+ *
+ * @param layer
+ * @return
+ * @throws IOException
+ * If the layer configuration does not exist and cannot be
+ * created or there is any problem analyzing it
+ */
+ MosaicLayer newMosaicLayer(String layer) throws IOException;
+
+ /**
+ * Checks if the layer configuration exists
+ *
+ * @param layerName
+ * @return true if there is an entry for this layer, false otherwise
+ */
+ boolean exists(String layerName);
+}
diff --git a/src/main/java/org/fao/unredd/layers/Location.java b/src/main/java/org/fao/unredd/layers/Location.java
new file mode 100644
index 0000000..a71bcad
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/Location.java
@@ -0,0 +1,55 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Represents the place a layer is stored. It may reference a file, a folder, a
+ * database table, etc.
+ *
+ * @author fergonco
+ */
+public interface Location {
+
+ /**
+ * Return the GDAL string to use to refer to this location in GDAL commands
+ * like ogr2ogr
+ *
+ * @param passwordGetter
+ * interface to ask for passwords if necessary
+ * @return
+ * @throws IOException
+ * If there is any problem getting the password
+ */
+ String getGDALString(PasswordGetter passwordGetter) throws IOException;
+
+ /**
+ * Return the GDAL feature name to use to refer to this location in GDAL
+ * commands like ogr2ogr
+ *
+ * @return
+ */
+ String getGDALFeatureName();
+
+ /**
+ * @return a File instance if this location represents a file or a folder.
+ * Null otherwise
+ */
+ File getFile();
+
+}
diff --git a/src/main/java/org/fao/unredd/layers/MosaicLayer.java b/src/main/java/org/fao/unredd/layers/MosaicLayer.java
new file mode 100644
index 0000000..d291917
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/MosaicLayer.java
@@ -0,0 +1,47 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.TreeMap;
+
+import org.fao.unredd.layers.folder.InvalidFolderStructureException;
+
+/**
+ * Special interface for mosaic layers to obtain a sorted map with the
+ * timestamps
+ *
+ * @author fergonco
+ */
+public interface MosaicLayer extends Layer {
+
+ /**
+ * Obtains the timestamps of the mosaic folder in a map from the date to the
+ * file containing the actual snapshot
+ *
+ * @param location
+ * @return
+ * @throws InvalidFolderStructureException
+ * If the location does not point to a image mosaic folder
+ * @throws IOException
+ * If there is any problem analyzing the folder
+ */
+ TreeMap getTimestamps(Location location)
+ throws InvalidFolderStructureException, IOException;
+
+}
diff --git a/src/main/java/org/fao/unredd/layers/NoSuchConfigurationException.java b/src/main/java/org/fao/unredd/layers/NoSuchConfigurationException.java
new file mode 100644
index 0000000..2104bc9
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/NoSuchConfigurationException.java
@@ -0,0 +1,26 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+/**
+ * Indicates the specified configuration does not exists in the layer
+ *
+ * @author fergonco
+ */
+public class NoSuchConfigurationException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/src/main/java/org/fao/unredd/layers/NoSuchIndicatorException.java b/src/main/java/org/fao/unredd/layers/NoSuchIndicatorException.java
new file mode 100644
index 0000000..7869820
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/NoSuchIndicatorException.java
@@ -0,0 +1,30 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+/**
+ * Indicates the specified output does not exist in the layer
+ *
+ * @author fergonco
+ */
+public class NoSuchIndicatorException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public NoSuchIndicatorException(String outputId) {
+ super(outputId);
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/layers/OutputDescriptor.java b/src/main/java/org/fao/unredd/layers/OutputDescriptor.java
new file mode 100644
index 0000000..f06e17d
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/OutputDescriptor.java
@@ -0,0 +1,65 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+/**
+ * Describes one of the outputs a layer can have.
+ *
+ * @author fergonco
+ */
+public class OutputDescriptor {
+
+ /**
+ * A unique id that is used by the client when requesting a specific output
+ */
+ private String id;
+
+ /**
+ * The name of the output
+ */
+ private String name;
+
+ /**
+ * The fieldId that identifies the objects in the layer for whom this output
+ * contains information
+ */
+ private String fieldId;
+
+ public OutputDescriptor(String id, String name, String fieldId) {
+ super();
+ this.id = id;
+ this.name = name;
+ this.fieldId = fieldId;
+ }
+
+ public String toJSON() {
+ return "{" + "\"id\":\"" + id + "\"," + //
+ "\"name\":\"" + name + "\"," + //
+ "\"fieldId\":\"" + fieldId + "\"" + "}";
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getFieldId() {
+ return fieldId;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/main/java/org/fao/unredd/layers/Outputs.java b/src/main/java/org/fao/unredd/layers/Outputs.java
new file mode 100644
index 0000000..cef0d13
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/Outputs.java
@@ -0,0 +1,51 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * A collection of {@link OutputDescriptor}s
+ *
+ * @author fergonco
+ */
+public class Outputs extends ArrayList {
+ private static final long serialVersionUID = 1L;
+
+ public Outputs(ArrayList outputDescriptors) {
+ this.addAll(outputDescriptors);
+ }
+
+ public Outputs(OutputDescriptor... outputDescriptors) {
+ Collections.addAll(this, outputDescriptors);
+ }
+
+ public String toJSON() {
+ StringBuilder ret = new StringBuilder("[");
+ String separator = "";
+ Iterator it = iterator();
+ while (it.hasNext()) {
+ OutputDescriptor outputDescriptor = it.next();
+ ret.append(separator).append(outputDescriptor.toJSON());
+ separator = ",";
+ }
+
+ return ret.append("]").toString();
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/layers/PasswordGetter.java b/src/main/java/org/fao/unredd/layers/PasswordGetter.java
new file mode 100644
index 0000000..27044eb
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/PasswordGetter.java
@@ -0,0 +1,29 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers;
+
+import java.io.IOException;
+
+/**
+ * Interface to get a password, normally from console
+ *
+ * @author fergonco
+ */
+public interface PasswordGetter {
+
+ String getPassword(String connectionInfo) throws IOException;
+
+}
diff --git a/src/main/java/org/fao/unredd/layers/folder/AbstractLayerFolder.java b/src/main/java/org/fao/unredd/layers/folder/AbstractLayerFolder.java
new file mode 100644
index 0000000..7ba6204
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/folder/AbstractLayerFolder.java
@@ -0,0 +1,217 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers.folder;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Properties;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.fao.unredd.layers.Layer;
+import org.fao.unredd.layers.NoSuchConfigurationException;
+import org.fao.unredd.layers.NoSuchIndicatorException;
+import org.fao.unredd.layers.OutputDescriptor;
+import org.fao.unredd.layers.Outputs;
+
+/**
+ * Abstract {@link Layer} folder based implementation
+ *
+ * @author fergonco
+ */
+public abstract class AbstractLayerFolder implements Layer {
+
+ private static final String METADATA_FIELD_ID_PROPERTY_NAME = "field-id";
+ private static final String METADATA_INDICATOR_NAME_PROPERTY_NAME = "indicator-name";
+ private static final String METADATA_PROPERTIES_FILE_NAME = "metadata.properties";
+ private static final String OUTPUT_FILE_NAME = "result.xml";
+ private static final String OUTPUT = "output";
+ private static final String CONFIGURATION = "configuration";
+ private static final String WORK = "work";
+ private File root;
+ private String workspaceName;
+ private String layerName;
+ private String qName;
+
+ public AbstractLayerFolder(String layerName, File root) throws IOException {
+ String[] workspaceAndName = layerName.split(Pattern.quote(":"));
+ if (workspaceAndName.length != 2) {
+ throw new IllegalArgumentException(
+ "The layer name must have the form workspaceName:layerName");
+ }
+ this.workspaceName = workspaceAndName[0];
+ this.layerName = workspaceAndName[1];
+ this.qName = layerName;
+ this.root = root;
+ if (!root.exists()) {
+ if (!root.mkdirs()) {
+ throw new IOException(
+ "Layer folder doesn't exist and could not be created: "
+ + root);
+ }
+ }
+ }
+
+ @Override
+ public String getWorkspace() {
+ return workspaceName;
+ }
+
+ @Override
+ public String getName() {
+ return layerName;
+ }
+
+ public String getQualifiedName() {
+ return qName;
+ }
+
+ public File getWorkFolder() {
+ return new File(root, WORK);
+ }
+
+ /**
+ * Convenience method to get a file on the work folder
+ *
+ * @param fileName
+ * @return
+ */
+ public File getWorkFile(String fileName) {
+ return new File(getWorkFolder(), fileName);
+ }
+
+ @Override
+ public String getConfiguration(String id)
+ throws NoSuchConfigurationException, IOException {
+ File file = new File(getConfigurationFolder(), id);
+ if (!file.exists()) {
+ throw new NoSuchConfigurationException();
+ }
+ BufferedInputStream input = new BufferedInputStream(
+ new FileInputStream(file));
+ String ret;
+ try {
+ ret = new String(IOUtils.toByteArray(input));
+ } finally {
+ input.close();
+ }
+ return ret;
+ }
+
+ public File getConfigurationFolder() {
+ return new File(root, CONFIGURATION);
+ }
+
+ public File getOutputFolder() {
+ return new File(root, OUTPUT);
+ }
+
+ @Override
+ public Outputs getOutputs() throws IOException {
+ File outputFolder = getOutputFolder();
+ if (!outputFolder.exists()) {
+ return new Outputs();
+ }
+ File[] outputFolders = outputFolder.listFiles();
+ ArrayList outputDescriptors = new ArrayList();
+ for (int i = 0; i < outputFolders.length; i++) {
+ String folderName = outputFolders[i].getName();
+ File outputRoot = new File(getOutputFolder(), folderName);
+ Properties metadata = getMetadataProperties(outputRoot);
+ String fieldId = metadata
+ .getProperty(METADATA_FIELD_ID_PROPERTY_NAME);
+ String indicatorName = metadata
+ .getProperty(METADATA_INDICATOR_NAME_PROPERTY_NAME);
+ if (fieldId != null
+ && new File(outputRoot, OUTPUT_FILE_NAME).exists()) {
+ outputDescriptors.add(new OutputDescriptor(folderName,
+ indicatorName, fieldId));
+ }
+ }
+
+ return new Outputs(outputDescriptors);
+ }
+
+ private Properties getMetadataProperties(File outputRoot)
+ throws IOException, FileNotFoundException {
+ Properties metadata = new Properties();
+ File metadataFile = getMetadataFile(outputRoot);
+ if (metadataFile.exists()) {
+ metadata.load(new FileInputStream(metadataFile));
+ }
+ return metadata;
+ }
+
+ private File getMetadataFile(File outputRoot) {
+ return new File(outputRoot, METADATA_PROPERTIES_FILE_NAME);
+ }
+
+ @Override
+ public String getOutput(String outputId) throws NoSuchIndicatorException,
+ IOException {
+ File outputFolder = new File(getOutputFolder(), outputId);
+ if (!outputFolder.exists()) {
+ throw new NoSuchIndicatorException(outputId);
+ }
+ InputStream input = new BufferedInputStream(new FileInputStream(
+ new File(outputFolder, OUTPUT_FILE_NAME)));
+ String ret = IOUtils.toString(input);
+ input.close();
+ return ret;
+ }
+
+ @Override
+ public void setOutput(String id, String outputName, String fieldId,
+ String content) throws IOException {
+ File outputFolder = new File(getOutputFolder(), id);
+ if (outputFolder.exists()) {
+ FileUtils.cleanDirectory(outputFolder);
+ } else {
+ if (!outputFolder.mkdirs()) {
+ throw new IOException(
+ "Cannot create the indicator output folder: "
+ + outputFolder.getAbsolutePath());
+ }
+ }
+
+ BufferedOutputStream resultStream = new BufferedOutputStream(
+ new FileOutputStream(new File(outputFolder, OUTPUT_FILE_NAME)));
+ try {
+ IOUtils.write(content, resultStream);
+ } finally {
+ resultStream.close();
+ }
+
+ Properties metadata = getMetadataProperties(outputFolder);
+ metadata.setProperty(METADATA_FIELD_ID_PROPERTY_NAME, fieldId);
+ metadata.setProperty(METADATA_INDICATOR_NAME_PROPERTY_NAME, outputName);
+ FileOutputStream metadataStream = new FileOutputStream(
+ getMetadataFile(outputFolder));
+ try {
+ metadata.store(metadataStream, "");
+ } finally {
+ metadataStream.close();
+ }
+ }
+}
diff --git a/src/main/java/org/fao/unredd/layers/folder/FolderLayerFactory.java b/src/main/java/org/fao/unredd/layers/folder/FolderLayerFactory.java
new file mode 100644
index 0000000..f57634f
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/folder/FolderLayerFactory.java
@@ -0,0 +1,76 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers.folder;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+import org.fao.unredd.layers.Layer;
+import org.fao.unredd.layers.LayerFactory;
+import org.fao.unredd.layers.MosaicLayer;
+
+/**
+ * Folder based implementation of {@link LayerFactory}
+ *
+ * @author fergonco
+ */
+public class FolderLayerFactory implements LayerFactory {
+
+ private File layerFolderRoot;
+
+ public FolderLayerFactory(File layerFolderRoot) {
+ if (!layerFolderRoot.exists()) {
+ throw new IllegalArgumentException(
+ "The layer folder root does not exist: "
+ + layerFolderRoot.getAbsolutePath());
+ }
+ this.layerFolderRoot = layerFolderRoot;
+ }
+
+ @Override
+ public Layer newLayer(String layerName) throws IOException {
+ return new LayerFolderImpl(layerName, getConfigurationFolder(layerName));
+ }
+
+ private File getConfigurationFolder(String layerName) throws IOException {
+ File layerFolder = getLayerFolder(layerName);
+ if (!layerFolder.exists() && !layerFolder.mkdirs()) {
+ throw new IOException(
+ "The folder does not exist and could not be created");
+ }
+ return layerFolder;
+ }
+
+ private File getLayerFolder(String layerName) {
+ String[] workspaceAndName = layerName.split(Pattern.quote(":"));
+ File workspaceFolder = new File(layerFolderRoot, workspaceAndName[0]);
+ File layerFolder = new File(workspaceFolder, workspaceAndName[1]);
+ return layerFolder;
+ }
+
+ @Override
+ public MosaicLayer newMosaicLayer(String layerName) throws IOException {
+ return new MosaicLayerFolder(layerName,
+ getConfigurationFolder(layerName));
+ }
+
+ @Override
+ public boolean exists(String layerName) {
+ return getLayerFolder(layerName).exists();
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/layers/folder/InvalidFolderStructureException.java b/src/main/java/org/fao/unredd/layers/folder/InvalidFolderStructureException.java
new file mode 100644
index 0000000..5b3ef73
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/folder/InvalidFolderStructureException.java
@@ -0,0 +1,43 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers.folder;
+
+import java.io.File;
+
+/**
+ * Signals that the layer folder does not have the expected structure
+ *
+ * @author fergonco
+ */
+public class InvalidFolderStructureException extends Exception {
+ private static final long serialVersionUID = 1L;
+ private File offendingFile;
+
+ public InvalidFolderStructureException(String msg, File offendingFile) {
+ super(msg + ": " + offendingFile.getAbsolutePath());
+ this.offendingFile = offendingFile;
+ }
+
+ /**
+ * Get the folder or file that does not fulfill the layer folder convention
+ * specification
+ *
+ * @return
+ */
+ public File getOffendingFile() {
+ return offendingFile;
+ }
+}
diff --git a/src/main/java/org/fao/unredd/layers/folder/LayerFolderImpl.java b/src/main/java/org/fao/unredd/layers/folder/LayerFolderImpl.java
new file mode 100644
index 0000000..d81ffdb
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/folder/LayerFolderImpl.java
@@ -0,0 +1,42 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers.folder;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.fao.unredd.layers.Layer;
+
+/**
+ * Concrete implementation of the {@link Layer} interface based on folders
+ *
+ * @author fergonco
+ */
+public class LayerFolderImpl extends AbstractLayerFolder {
+
+ /**
+ * Builds a new instance
+ *
+ * @param folder
+ * @throws IOException
+ * If the layer folder does not exist and cannot be created
+ */
+ public LayerFolderImpl(String name, File folder)
+ throws IllegalArgumentException, IOException {
+ super(name, folder);
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/layers/folder/MosaicLayerFolder.java b/src/main/java/org/fao/unredd/layers/folder/MosaicLayerFolder.java
new file mode 100644
index 0000000..9f3817c
--- /dev/null
+++ b/src/main/java/org/fao/unredd/layers/folder/MosaicLayerFolder.java
@@ -0,0 +1,142 @@
+/**
+ * nfms4redd Portal Interface - http://nfms4redd.org/
+ *
+ * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
+ *
+ * This application is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * version 3.0 of the License.
+ *
+ * This library 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 General Public License for more details.
+ */
+package org.fao.unredd.layers.folder;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Properties;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.fao.unredd.layers.Location;
+import org.fao.unredd.layers.MosaicLayer;
+
+/**
+ * Manages the configuration of image mosaic layers
+ *
+ * @author fergonco
+ */
+public class MosaicLayerFolder extends AbstractLayerFolder implements
+ MosaicLayer {
+
+ private TreeMap files;
+
+ /**
+ * Creates a new instance
+ *
+ * @param layerName
+ * Name of the layer
+ * @param folder
+ * Folder containing the configuration
+ * @throws IOException
+ * If the layer folder does not exist and cannot be created
+ */
+ public MosaicLayerFolder(String layerName, File folder) throws IOException {
+ super(layerName, folder);
+ }
+
+ /**
+ * Gets a map that associates the timestamps of the snapshots in the time
+ * mosaic with the tiff file that represents the snapshot
+ *
+ * @throws InvalidFolderStructureException
+ * If the folder does not match the expected structure for a
+ * mosaic
+ * @throws IOException
+ * If there is any problem analyzing the folder
+ */
+ @Override
+ public TreeMap getTimestamps(Location location)
+ throws InvalidFolderStructureException, IOException {
+ if (true) {
+ /*
+ * There is a reference to TimeParser below brings a huge set of
+ * dependencies and to avoid this we just comment it. This is
+ * convenient because this code is not used by the portal but
+ * instead by the command line stats generator tool, which have an
+ * uncertain future. Additionally, the way to generate and access
+ * the statistics will change in the near future and this class may
+ * disappear or no longer be used
+ */
+ throw new UnsupportedOperationException();
+ }
+ if (files == null) {
+ File dataFolder = location.getFile();
+ File[] snapshotFiles = dataFolder.listFiles(new FilenameFilter() {
+
+ @Override
+ public boolean accept(File dir, String name) {
+ String lowerCaseName = name.toLowerCase();
+ return lowerCaseName.endsWith(".tiff")
+ || lowerCaseName.endsWith(".tif");
+ }
+ });
+ Properties timeregexProperties = new Properties();
+ File timeregexPropertiesFile = new File(dataFolder,
+ "timeregex.properties");
+ try {
+ timeregexProperties.load(new FileInputStream(
+ timeregexPropertiesFile));
+ } catch (FileNotFoundException e) {
+ throw new InvalidFolderStructureException(
+ "The folder does not contain a timeregex.properties"
+ + " file in the 'mosaic' subfolder",
+ timeregexPropertiesFile);
+ }
+ String timeregex = timeregexProperties.getProperty("regex");
+ if (timeregex == null) {
+ throw new InvalidFolderStructureException(
+ "The timeregex.properties file does not contain"
+ + " the regex property in the folder",
+ timeregexPropertiesFile);
+ }
+ Pattern pattern = Pattern.compile(timeregex);
+ files = new TreeMap();
+ for (File snapshotFile : snapshotFiles) {
+ String name = snapshotFile.getName();
+ Matcher matcher = pattern.matcher(name);
+ if (!matcher.find()) {
+ throw new InvalidFolderStructureException(
+ "The date of the snapshot could not be obtained",
+ snapshotFile);
+ }
+ String dateString = matcher.group();
+ // try {
+ // TimeParser timeParser = new TimeParser();
+ // List dates = timeParser.parse(dateString);
+ // files.put(dates.get(0), snapshotFile);
+ // } catch (java.text.ParseException e) {
+ // throw new InvalidFolderStructureException(
+ // "The date of the snapshot "
+ // + "could not be obtained: " + dateString,
+ // snapshotFile);
+ // }
+ }
+ if (files.isEmpty()) {
+ throw new InvalidFolderStructureException(
+ "There are no snapshots in the mosaic folder: "
+ + dataFolder.getAbsolutePath(), dataFolder);
+ }
+ }
+ return files;
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/portal/ApplicationController.java b/src/main/java/org/fao/unredd/portal/ApplicationController.java
index 62eb1a4..635aeb9 100644
--- a/src/main/java/org/fao/unredd/portal/ApplicationController.java
+++ b/src/main/java/org/fao/unredd/portal/ApplicationController.java
@@ -15,50 +15,28 @@
*/
package org.fao.unredd.portal;
-import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.FileNameMap;
-import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import javax.xml.bind.JAXBException;
import net.sf.json.JSONObject;
-import org.apache.commons.io.IOUtils;
-import org.apache.log4j.Logger;
-import org.fao.unredd.layers.LayerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.servlet.HandlerMapping;
-import org.springframework.web.servlet.ModelAndView;
-
-@Controller
public class ApplicationController {
-
- private static Logger logger = Logger
- .getLogger(ApplicationController.class);
-
- @Autowired
- org.fao.unredd.portal.Config config;
-
- @Autowired
- net.tanesha.recaptcha.ReCaptchaImpl reCaptcha;
-
- @Autowired
- private LayerFactory layerFactory;
-
+ //
+ // private static Logger logger = Logger
+ // .getLogger(ApplicationController.class);
+ //
+ // @Autowired
+ // org.fao.unredd.portal.Config config;
+ //
+ // @Autowired
+ // net.tanesha.recaptcha.ReCaptchaImpl reCaptcha;
+ //
+ // @Autowired
+ // private LayerFactory layerFactory;
+ //
/**
* A collection of the possible error causes.
*
@@ -112,7 +90,6 @@ String getJson() {
contents.put("id", id);
contents.put("message", message);
- new Config();
JSONObject json = new JSONObject();
json.putAll(contents);
return json.toString();
@@ -127,229 +104,229 @@ public void writeError(HttpServletResponse response) throws IOException {
response.getWriter().write(getJson());
}
}
-
- @RequestMapping(value = "/index.do", method = RequestMethod.GET)
- public ModelAndView index(Model model) {
- ModelAndView mv = new ModelAndView();
- model.addAttribute("captchaHtml",
- reCaptcha.createRecaptchaHtml(null, null));
- mv.setViewName("index");
- return mv;
- }
-
- @RequestMapping("/messages.json")
- public ModelAndView getLocalizedMessages() {
- return new ModelAndView("messages", "messages", config.getMessages());
- }
-
- @RequestMapping("/static/**")
- public void getCustomStaticFile(HttpServletRequest request,
- HttpServletResponse response) throws IOException {
- // Get path to file
- String fileName = (String) request
- .getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
-
- // Verify file exists
- File file = new File(config.getDir() + "/static/" + fileName);
- if (!file.isFile()) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
-
- // Manage cache headers: Last-Modified and If-Modified-Since
- long ifModifiedSince = request.getDateHeader("If-Modified-Since");
- long lastModified = file.lastModified();
- if (ifModifiedSince >= (lastModified / 1000 * 1000)) {
- response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
- return;
- }
- response.setDateHeader("Last-Modified", lastModified);
-
- // Set content type
- FileNameMap fileNameMap = URLConnection.getFileNameMap();
- String type = fileNameMap.getContentTypeFor(fileName);
- response.setContentType(type);
-
- // Send contents
- try {
- InputStream is = new FileInputStream(file);
- IOUtils.copy(is, response.getOutputStream());
- response.flushBuffer();
- } catch (IOException e) {
- logger.error("Error reading file", e);
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- }
- }
-
- @RequestMapping("/layers.json")
- public void getLayers(HttpServletRequest request,
- HttpServletResponse response) throws IOException {
-
- response.setContentType("application/json;charset=UTF-8");
- try {
- if (request.getParameterMap().containsKey("jsonp")) {
- response.getWriter().print("layers_json = ");
- }
- response.getWriter().print(setLayerTimes());
- response.flushBuffer();
- } catch (IOException e) {
- logger.error("Error reading file", e);
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- }
- }
-
- @RequestMapping(value = "/indicators.json", method = RequestMethod.GET)
- public void getLayerIndicators(HttpServletRequest request,
- HttpServletResponse response) throws IOException {
- IndicatorsController controller = new IndicatorsController(response,
- layerFactory);
-
- controller.returnIndicators(request
- .getParameter(IndicatorsController.PARAM_LAYER_ID));
- }
-
- @RequestMapping(value = "/indicator.json", method = RequestMethod.GET)
- public void getLayerIndicatorOutput(HttpServletRequest request,
- HttpServletResponse response) throws IOException {
- IndicatorsController controller = new IndicatorsController(response,
- layerFactory);
-
- controller.returnIndicator(
- request.getParameter(IndicatorsController.PARAM_OBJECT_ID),
- request.getParameter(IndicatorsController.PARAM_LAYER_ID),
- request.getParameter(IndicatorsController.PARAM_INDICATOR_ID));
- }
-
- @RequestMapping("/charts.json")
- public void getCharts(HttpServletRequest request,
- HttpServletResponse response) throws IOException {
- response.setContentType("application/json;charset=UTF-8");
- try {
- response.getWriter().print(getCharts());
- response.flushBuffer();
- } catch (Exception e) {
- logger.error(e);
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- }
- }
-
- @RequestMapping(value = "/report.json", method = RequestMethod.POST)
- public void buildCustomReport(HttpServletRequest request,
- HttpServletResponse response) throws IOException, JAXBException {
- throw new UnsupportedOperationException();
- // response.setContentType("application/json;charset=UTF-8");
- //
- // // Get posted attributes
- // @SuppressWarnings("unchecked")
- // Map attributes = flattenParamValues(request
- // .getParameterMap());
- //
- // // Get Chart Script Resource from ChartScriptId parameter
- // long chartScriptId = Long.valueOf(attributes.get("ChartScriptId"));
- //
- // // POSTed body, should be a WKT geometry
- // String wktROI = getRequestBodyAsString(request, response);
- //
- // try {
- // // Generate Report
- // ReportManager report = new ReportManager(getGeostore(),
- // config.getProperties());
- // String reportContents = report.get(wktROI, chartScriptId);
- //
- // // Persist report in GeoStore, get the URL
- // Long reportId = getGeostore().insertReport(attributes,
- // reportContents);
- // String resourceName = getGeostore().getClient()
- // .getResource(reportId).getName();
- // String reportURL = getGeostore().getClient().getGeostoreRestUrl()
- // + "/misc/category/name/Report/resource/name/"
- // + resourceName + "/data?name=" + attributes.get("UserName")
- // + " Stats";
- //
- // // Build JSON response
- // String responseBody = "{ \n" + " \"success\": true, \n"
- // + " \"response_type\": \"result_embedded\", \n"
- // + " \"link\": { \n" + " \"type\": \"text/html\", \n"
- // + " \"href\": \"" + reportURL + "\" \n" + " } \n"
- // + "}";
- // response.getWriter().print(responseBody);
- //
- // } catch (ReportException e) {
- // // Will send the errorCause whose name equals the
- // // RealTimeStatsException.Code name
- // ErrorCause cause = ErrorCause.valueOf(e.getCode().name());
- // response.setStatus(cause.status);
- // response.getWriter().write(cause.getJson());
- // }
- //
- // response.flushBuffer();
- }
-
- @RequestMapping(value = "/feedback", method = RequestMethod.POST)
- public void postFeedback(HttpServletRequest request,
- HttpServletResponse response) throws IOException {
-
- FeedbackController controller = new FeedbackController(request,
- response, reCaptcha);
-
- controller.postFeedback();
- }
-
- private String getCharts() throws UnsupportedEncodingException,
- JAXBException {
- throw new UnsupportedOperationException();
- // Map resp = new HashMap();
- // List charts = getGeostore().getUNREDDResources(
- // UNREDDCategories.CHARTSCRIPT);
- // for (Resource chart : charts) {
- // resp.put(chart.getId(), chart.getName());
- // }
- // JSONObject json = new JSONObject();
- // json.putAll(resp);
- // return json.toString();
- }
-
- private String getLayerTimesFromGeostore(String layerName)
- throws JAXBException, UnsupportedEncodingException {
- throw new UnsupportedOperationException();
- // List layerUpdates = getGeostore()
- // .searchLayerUpdatesByLayerName(layerName);
- // if (layerUpdates.size() == 0) {
- // logger.warn("Requested times for \""
- // + layerName
- // + "\", but no corresponding LayerUpdates found in GeoStore.");
- // }
- //
- // String times = "";
- // Iterator iterator = layerUpdates.iterator();
- // while (iterator.hasNext()) {
- // UNREDDLayerUpdate unreddLayerUpdate = new UNREDDLayerUpdate(
- // iterator.next());
- //
- // times += unreddLayerUpdate.getDateAsString();
- //
- // if (iterator.hasNext()) {
- // times += ",";
- // }
- // }
- // return times += toString();
- }
-
- private String setLayerTimes() {
- String jsonLayers = config.getLayers();
- Pattern patt = Pattern.compile("\\$\\{time\\.([\\w.]*)\\}");
- Matcher m = patt.matcher(jsonLayers);
- StringBuffer sb = new StringBuffer(jsonLayers.length());
- while (m.find()) { // Found time-dependant layer in json file
- String layerName = m.group(1);
- try {
- m.appendReplacement(sb, getLayerTimesFromGeostore(layerName));
- } catch (Exception e) {
- m.appendReplacement(sb, "");
- logger.error("Error getting layer times from GeoStore.");
- }
- }
- m.appendTail(sb);
- return sb.toString();
- }
+ //
+ // @RequestMapping(value = "/index.do", method = RequestMethod.GET)
+ // public ModelAndView index(Model model) {
+ // ModelAndView mv = new ModelAndView();
+ // model.addAttribute("captchaHtml",
+ // reCaptcha.createRecaptchaHtml(null, null));
+ // mv.setViewName("index");
+ // return mv;
+ // }
+ //
+ // @RequestMapping("/messages.json")
+ // public ModelAndView getLocalizedMessages() {
+ // return new ModelAndView("messages", "messages", config.getMessages());
+ // }
+ //
+ // @RequestMapping("/static/**")
+ // public void getCustomStaticFile(HttpServletRequest request,
+ // HttpServletResponse response) throws IOException {
+ // // Get path to file
+ // String fileName = (String) request
+ // .getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
+ //
+ // // Verify file exists
+ // File file = new File(config.getDir() + "/static/" + fileName);
+ // if (!file.isFile()) {
+ // response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ // return;
+ // }
+ //
+ // // Manage cache headers: Last-Modified and If-Modified-Since
+ // long ifModifiedSince = request.getDateHeader("If-Modified-Since");
+ // long lastModified = file.lastModified();
+ // if (ifModifiedSince >= (lastModified / 1000 * 1000)) {
+ // response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+ // return;
+ // }
+ // response.setDateHeader("Last-Modified", lastModified);
+ //
+ // // Set content type
+ // FileNameMap fileNameMap = URLConnection.getFileNameMap();
+ // String type = fileNameMap.getContentTypeFor(fileName);
+ // response.setContentType(type);
+ //
+ // // Send contents
+ // try {
+ // InputStream is = new FileInputStream(file);
+ // IOUtils.copy(is, response.getOutputStream());
+ // response.flushBuffer();
+ // } catch (IOException e) {
+ // logger.error("Error reading file", e);
+ // response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ // }
+ // }
+ //
+ // @RequestMapping("/layers.json")
+ // public void getLayers(HttpServletRequest request,
+ // HttpServletResponse response) throws IOException {
+ //
+ // response.setContentType("application/json;charset=UTF-8");
+ // try {
+ // if (request.getParameterMap().containsKey("jsonp")) {
+ // response.getWriter().print("layers_json = ");
+ // }
+ // response.getWriter().print(setLayerTimes());
+ // response.flushBuffer();
+ // } catch (IOException e) {
+ // logger.error("Error reading file", e);
+ // response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ // }
+ // }
+ //
+ // @RequestMapping(value = "/indicators.json", method = RequestMethod.GET)
+ // public void getLayerIndicators(HttpServletRequest request,
+ // HttpServletResponse response) throws IOException {
+ // IndicatorsController controller = new IndicatorsController(response,
+ // layerFactory);
+ //
+ // controller.returnIndicators(request
+ // .getParameter(IndicatorsController.PARAM_LAYER_ID));
+ // }
+ //
+ // @RequestMapping(value = "/indicator.json", method = RequestMethod.GET)
+ // public void getLayerIndicatorOutput(HttpServletRequest request,
+ // HttpServletResponse response) throws IOException {
+ // IndicatorsController controller = new IndicatorsController(response,
+ // layerFactory);
+ //
+ // controller.returnIndicator(
+ // request.getParameter(IndicatorsController.PARAM_OBJECT_ID),
+ // request.getParameter(IndicatorsController.PARAM_LAYER_ID),
+ // request.getParameter(IndicatorsController.PARAM_INDICATOR_ID));
+ // }
+ //
+ // @RequestMapping("/charts.json")
+ // public void getCharts(HttpServletRequest request,
+ // HttpServletResponse response) throws IOException {
+ // response.setContentType("application/json;charset=UTF-8");
+ // try {
+ // response.getWriter().print(getCharts());
+ // response.flushBuffer();
+ // } catch (Exception e) {
+ // logger.error(e);
+ // response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ // }
+ // }
+ //
+ // @RequestMapping(value = "/report.json", method = RequestMethod.POST)
+ // public void buildCustomReport(HttpServletRequest request,
+ // HttpServletResponse response) throws IOException, JAXBException {
+ // throw new UnsupportedOperationException();
+ // // response.setContentType("application/json;charset=UTF-8");
+ // //
+ // // // Get posted attributes
+ // // @SuppressWarnings("unchecked")
+ // // Map attributes = flattenParamValues(request
+ // // .getParameterMap());
+ // //
+ // // // Get Chart Script Resource from ChartScriptId parameter
+ // // long chartScriptId = Long.valueOf(attributes.get("ChartScriptId"));
+ // //
+ // // // POSTed body, should be a WKT geometry
+ // // String wktROI = getRequestBodyAsString(request, response);
+ // //
+ // // try {
+ // // // Generate Report
+ // // ReportManager report = new ReportManager(getGeostore(),
+ // // config.getProperties());
+ // // String reportContents = report.get(wktROI, chartScriptId);
+ // //
+ // // // Persist report in GeoStore, get the URL
+ // // Long reportId = getGeostore().insertReport(attributes,
+ // // reportContents);
+ // // String resourceName = getGeostore().getClient()
+ // // .getResource(reportId).getName();
+ // // String reportURL = getGeostore().getClient().getGeostoreRestUrl()
+ // // + "/misc/category/name/Report/resource/name/"
+ // // + resourceName + "/data?name=" + attributes.get("UserName")
+ // // + " Stats";
+ // //
+ // // // Build JSON response
+ // // String responseBody = "{ \n" + " \"success\": true, \n"
+ // // + " \"response_type\": \"result_embedded\", \n"
+ // // + " \"link\": { \n" + " \"type\": \"text/html\", \n"
+ // // + " \"href\": \"" + reportURL + "\" \n" + " } \n"
+ // // + "}";
+ // // response.getWriter().print(responseBody);
+ // //
+ // // } catch (ReportException e) {
+ // // // Will send the errorCause whose name equals the
+ // // // RealTimeStatsException.Code name
+ // // ErrorCause cause = ErrorCause.valueOf(e.getCode().name());
+ // // response.setStatus(cause.status);
+ // // response.getWriter().write(cause.getJson());
+ // // }
+ // //
+ // // response.flushBuffer();
+ // }
+ //
+ // @RequestMapping(value = "/feedback", method = RequestMethod.POST)
+ // public void postFeedback(HttpServletRequest request,
+ // HttpServletResponse response) throws IOException {
+ //
+ // FeedbackController controller = new FeedbackController(request,
+ // response, reCaptcha);
+ //
+ // controller.postFeedback();
+ // }
+ //
+ // private String getCharts() throws UnsupportedEncodingException,
+ // JAXBException {
+ // throw new UnsupportedOperationException();
+ // // Map resp = new HashMap();
+ // // List charts = getGeostore().getUNREDDResources(
+ // // UNREDDCategories.CHARTSCRIPT);
+ // // for (Resource chart : charts) {
+ // // resp.put(chart.getId(), chart.getName());
+ // // }
+ // // JSONObject json = new JSONObject();
+ // // json.putAll(resp);
+ // // return json.toString();
+ // }
+ //
+ // private String getLayerTimesFromGeostore(String layerName)
+ // throws JAXBException, UnsupportedEncodingException {
+ // throw new UnsupportedOperationException();
+ // // List layerUpdates = getGeostore()
+ // // .searchLayerUpdatesByLayerName(layerName);
+ // // if (layerUpdates.size() == 0) {
+ // // logger.warn("Requested times for \""
+ // // + layerName
+ // // + "\", but no corresponding LayerUpdates found in GeoStore.");
+ // // }
+ // //
+ // // String times = "";
+ // // Iterator iterator = layerUpdates.iterator();
+ // // while (iterator.hasNext()) {
+ // // UNREDDLayerUpdate unreddLayerUpdate = new UNREDDLayerUpdate(
+ // // iterator.next());
+ // //
+ // // times += unreddLayerUpdate.getDateAsString();
+ // //
+ // // if (iterator.hasNext()) {
+ // // times += ",";
+ // // }
+ // // }
+ // // return times += toString();
+ // }
+ //
+ // private String setLayerTimes() {
+ // String jsonLayers = config.getLayers();
+ // Pattern patt = Pattern.compile("\\$\\{time\\.([\\w.]*)\\}");
+ // Matcher m = patt.matcher(jsonLayers);
+ // StringBuffer sb = new StringBuffer(jsonLayers.length());
+ // while (m.find()) { // Found time-dependant layer in json file
+ // String layerName = m.group(1);
+ // try {
+ // m.appendReplacement(sb, getLayerTimesFromGeostore(layerName));
+ // } catch (Exception e) {
+ // m.appendReplacement(sb, "");
+ // logger.error("Error getting layer times from GeoStore.");
+ // }
+ // }
+ // m.appendTail(sb);
+ // return sb.toString();
+ // }
}
diff --git a/src/main/java/org/fao/unredd/portal/BundleMessage.java b/src/main/java/org/fao/unredd/portal/BundleMessage.java
deleted file mode 100644
index 0eb5fd0..0000000
--- a/src/main/java/org/fao/unredd/portal/BundleMessage.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * nfms4redd Portal Interface - http://nfms4redd.org/
- *
- * (C) 2012, FAO Forestry Department (http://www.fao.org/forestry/)
- *
- * This application is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation;
- * version 3.0 of the License.
- *
- * This library 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 General Public License for more details.
- */
-package org.fao.unredd.portal;
-
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.springframework.context.support.ReloadableResourceBundleMessageSource;
-
-/**
- * Same as {@link ReloadableResourceBundleMessageSource}, with a new
- * method to get all of the translated messages at once.
- *
- * @author Oscar Fonts
- */
-public class BundleMessage extends ReloadableResourceBundleMessageSource {
-
- public Map getMessages(Locale locale) {
- Map msg = new HashMap();
- for(Entry loc : getMergedProperties(locale).getProperties().entrySet()) {
- String val = this.getMessage(loc.getKey().toString(), null, locale);
- msg.put(loc.getKey().toString(), val);
- }
- return msg;
- }
-}
diff --git a/src/main/java/org/fao/unredd/portal/CachedProperties.java b/src/main/java/org/fao/unredd/portal/CachedProperties.java
new file mode 100644
index 0000000..0cf79f8
--- /dev/null
+++ b/src/main/java/org/fao/unredd/portal/CachedProperties.java
@@ -0,0 +1,39 @@
+package org.fao.unredd.portal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.log4j.Logger;
+
+public class CachedProperties {
+
+ private static Logger logger = Logger.getLogger(CachedProperties.class);
+
+ private long lastRead = 0;
+ private File propertyFile;
+ private Properties properties;
+
+ public CachedProperties(File propertyFile) {
+ this.propertyFile = propertyFile;
+ }
+
+ public Properties getProperties() {
+ long lastModified = propertyFile.lastModified();
+ if (lastModified > lastRead) {
+ logger.debug("Reading portal properties file " + propertyFile);
+ properties = new Properties();
+ try {
+ properties.load(new FileInputStream(propertyFile));
+ } catch (IOException e) {
+ logger.error("Error reading portal properties file", e);
+ }
+
+ lastRead = lastModified;
+ }
+
+ return properties;
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/portal/Config.java b/src/main/java/org/fao/unredd/portal/Config.java
index 084f236..e88fae0 100644
--- a/src/main/java/org/fao/unredd/portal/Config.java
+++ b/src/main/java/org/fao/unredd/portal/Config.java
@@ -18,139 +18,170 @@
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.InputStream;
import java.io.UnsupportedEncodingException;
-import java.util.Map;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.MissingResourceException;
import java.util.Properties;
+import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import javax.annotation.PostConstruct;
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import net.sf.json.JSONObject;
-
+import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import org.springframework.web.context.ServletContextAware;
-import org.springframework.web.servlet.support.RequestContextUtils;
/**
* Utility class to access the custom resources placed in PORTAL_CONFIG_DIR.
*
* @author Oscar Fonts
*/
-@Component("config")
-public class Config implements ServletContextAware {
-
- static Logger logger = Logger.getLogger(Config.class);
-
- ServletContext context;
- HttpServletRequest request;
- HttpServletResponse response;
-
- File dir = null;
- Properties properties = null;
-
- @Autowired
- BundleMessage messageSource;
-
- public void setServletContext(ServletContext servletContext) {
- this.context = servletContext;
+public class Config {
+
+ private static final String PROPERTY_CLIENT_MODULES = "client.modules";
+ private static final String PROPERTY_SERVER_QUERY_URL = "info.queryUrl";
+ private static final String PROPERTY_SERVER_LAYER_URL = "info.layerUrl";
+
+ private static Logger logger = Logger.getLogger(Config.class);
+
+ private File dir = null;
+ private CachedProperties cachedProperties;
+
+ private String rootPath;
+ private String configInitParameter;
+ private HashMap localeBundles = new HashMap();
+
+ public Config(String rootPath, String configInitParameter) {
+ this.rootPath = rootPath;
+ this.configInitParameter = configInitParameter;
+
+ cachedProperties = new CachedProperties(getPortalPropertiesFile());
+ }
+
+ public File getPortalPropertiesFile() {
+ return new File(getDir() + "/portal.properties");
}
- public void setServletRequest(HttpServletRequest request) {
- this.request = request;
- }
-
- public void setServletResponse(HttpServletResponse response) {
- this.response = response;
- }
-
- @PostConstruct
- public void init() {
- context.setAttribute("config", this);
- }
-
public File getDir() {
if (dir == null) {
- String default_dir = context.getRealPath("/") + "/WEB-INF/default_config/";
+ String defaultDir = rootPath + File.separator + "WEB-INF"
+ + File.separator + "default_config";
+
+ // Get the portal config dir property from Java system properties
+ String portalConfigDir = System.getProperty("PORTAL_CONFIG_DIR");
- String property = System.getProperty("PORTAL_CONFIG_DIR");
- if (property == null) {
+ // If not set in the system properties, get it from the Servlet
+ // context parameters (web.xml)
+ if (portalConfigDir == null)
+ portalConfigDir = configInitParameter;
+
+ // Otherwise:
+ if (portalConfigDir == null) {
+ // if not set already, use the default portal config dir
logger.warn("PORTAL_CONFIG_DIR property not found. Using default config.");
- dir = new File(default_dir);
+ dir = new File(defaultDir);
} else {
- dir = new File(property);
+ // if set but not existing, use the default portal config dir
+ dir = new File(portalConfigDir);
if (!dir.exists()) {
- logger.warn("PORTAL_CONFIG_DIR is set to " + dir.getAbsolutePath() +
- ", but it doesn't exist. Using default config.");
- dir = new File(default_dir);
+ logger.warn("PORTAL_CONFIG_DIR is set to "
+ + dir.getAbsolutePath()
+ + ", but it doesn't exist. Using default config.");
+ dir = new File(defaultDir);
}
- }
- logger.info("PORTAL_CONFIG_DIR:");
+ }
+
logger.info("============================================================================");
- logger.info(dir.getAbsolutePath());
+ logger.info("PORTAL_CONFIG_DIR: " + dir.getAbsolutePath());
logger.info("============================================================================");
}
+
return dir;
}
-
+
public boolean isMinifiedJs() {
- return Boolean.parseBoolean(System.getProperty("MINIFIED_JS", "true"));
+ return Boolean.parseBoolean(System.getProperty("MINIFIED_JS", "false"));
}
-
+
public Properties getProperties() {
- if (properties == null) {
- String location = getDir()+"/portal.properties";
- logger.debug("Reading portal properties file "+location);
- properties = new Properties();
- try {
- properties.load(new FileInputStream(location));
- } catch (IOException e) {
- logger.error("Error reading portal properties file", e);
+ return cachedProperties.getProperties();
+ }
+
+ public ArrayList getLanguages() {
+ File translationFolder = getTranslationFolder();
+ final Pattern pattern = Pattern.compile("messages_(..)\\.properties");
+ File[] translationFiles = translationFolder.listFiles();
+ ArrayList locales = new ArrayList();
+ if (translationFiles != null) {
+ for (File translationFile : translationFiles) {
+ Matcher matcher = pattern.matcher(translationFile.getName());
+ if (matcher.matches()) {
+ String localeString = matcher.group(1);
+ locales.add(localeString);
+ }
}
}
- return properties;
- }
-
- @SuppressWarnings("unchecked")
- public Map getLanguages() {
- String json = getProperties().getProperty("languages", "{\"en\": \"English\"}");
- return (Map)JSONObject.toBean(JSONObject.fromObject(json), java.util.HashMap.class);
+
+ return locales;
}
-
- public Map getMessages() {
- return messageSource.getMessages(RequestContextUtils.getLocale(request));
+
+ private File getTranslationFolder() {
+ return new File(getDir(), "messages");
}
-
- public String getHeader() {
- return getLocalizedFileContents(new File(getDir()+"/header.tpl"));
+
+ public String getLayers(Locale locale) throws IOException,
+ ConfigurationException {
+ return getLocalizedFileContents(getLayersFile(), locale);
}
-
- public String getFooter() {
- return getLocalizedFileContents(new File(getDir()+"/footer.tpl"));
+
+ public File getLayersFile() {
+ return new File(getDir() + "/layers.json");
}
-
- public String getLayers() {
- return getLocalizedFileContents(new File(getDir()+"/layers.json"));
+
+ public ResourceBundle getMessages(Locale locale)
+ throws ConfigurationException {
+ ResourceBundle bundle = localeBundles.get(locale);
+ if (bundle == null) {
+ URLClassLoader urlClassLoader;
+ try {
+ urlClassLoader = new URLClassLoader(
+ new URL[] { getTranslationFolder().toURI().toURL() });
+ } catch (MalformedURLException e) {
+ logger.error(
+ "Something is wrong with the configuration directory",
+ e);
+ throw new ConfigurationException(e);
+ }
+ bundle = ResourceBundle.getBundle("messages", locale,
+ urlClassLoader);
+ localeBundles.put(locale, bundle);
+ }
+
+ return bundle;
}
-
- String getLocalizedFileContents(File file) {
+
+ public String getLocalizedFileContents(File file, Locale locale)
+ throws IOException, ConfigurationException {
try {
- String template = new String(getFileContents(file), "UTF-8");
+ BufferedInputStream bis = new BufferedInputStream(
+ new FileInputStream(file));
+ String template = IOUtils.toString(bis, "UTF-8");
+ bis.close();
Pattern patt = Pattern.compile("\\$\\{([\\w.]*)\\}");
Matcher m = patt.matcher(template);
StringBuffer sb = new StringBuffer(template.length());
+ ResourceBundle messages = getMessages(locale);
while (m.find()) {
- String text = getMessages().get(m.group(1));
- if (text != null) {
+ String text;
+ try {
+ text = messages.getString(m.group(1));
m.appendReplacement(sb, text);
+ } catch (MissingResourceException e) {
+ // do not replace
}
}
m.appendTail(sb);
@@ -160,31 +191,27 @@ String getLocalizedFileContents(File file) {
return "";
}
}
-
- byte[] getFileContents(File file) {
- byte[] result = new byte[(int) file.length()];
- try {
- InputStream input = null;
- try {
- int totalBytesRead = 0;
- input = new BufferedInputStream(new FileInputStream(file));
- while (totalBytesRead < result.length) {
- int bytesRemaining = result.length - totalBytesRead;
- // input.read() returns -1, 0, or more :
- int bytesRead = input.read(result, totalBytesRead,
- bytesRemaining);
- if (bytesRead > 0) {
- totalBytesRead = totalBytesRead + bytesRead;
- }
- }
- } finally {
- input.close();
- }
- } catch (FileNotFoundException ex) {
- logger.error("File not found.", ex);
- } catch (IOException ex) {
- logger.error("Error reading file contents.", ex);
+
+ public String[] getModules() throws ConfigurationException {
+ return getProperty(PROPERTY_CLIENT_MODULES).split(",");
+ }
+
+ public String getQueryURL() throws ConfigurationException {
+ return getProperty(PROPERTY_SERVER_QUERY_URL);
+ }
+
+ public String getLayerURL() {
+ return getProperty(PROPERTY_SERVER_LAYER_URL);
+ }
+
+ private String getProperty(String propertyName) throws ConfigurationException {
+ String value = getProperties().getProperty(propertyName);
+ if (value != null) {
+ return value;
+ } else {
+ throw new ConfigurationException("No \"" + propertyName
+ + "\" property in configuration");
}
- return result;
}
+
}
diff --git a/src/main/java/org/fao/unredd/portal/ConfigServlet.java b/src/main/java/org/fao/unredd/portal/ConfigServlet.java
new file mode 100644
index 0000000..12f0409
--- /dev/null
+++ b/src/main/java/org/fao/unredd/portal/ConfigServlet.java
@@ -0,0 +1,65 @@
+package org.fao.unredd.portal;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import net.sf.json.JSONObject;
+import net.sf.json.JSONSerializer;
+
+public class ConfigServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ Config config = (Config) getServletContext().getAttribute("config");
+ Locale locale = (Locale) req.getAttribute("locale");
+
+ ResourceBundle bundle = config.getMessages(locale);
+
+ String title = bundle.getString("title");
+
+ JSONObject moduleConfig = new JSONObject();
+ moduleConfig.element("customization",
+ buildCustomizationObject(config, locale, title));
+ moduleConfig.element("i18n", buildI18NObject(bundle));
+ moduleConfig.element("layers",
+ JSONSerializer.toJSON(config.getLayers(locale)));
+
+ String json = new JSONObject().element("config", moduleConfig)
+ .toString();
+
+ resp.setContentType("application/javascript");
+ resp.setCharacterEncoding("utf8");
+ PrintWriter writer = resp.getWriter();
+ writer.write("var require = " + json);
+ }
+
+ private HashMap buildI18NObject(ResourceBundle bundle) {
+ HashMap messages = new HashMap();
+ for (String key : bundle.keySet()) {
+ messages.put(key, bundle.getString(key));
+ }
+
+ return messages;
+ }
+
+ private JSONObject buildCustomizationObject(Config config, Locale locale,
+ String title) {
+ return new JSONObject().element("title", title)//
+ .element("languages", config.getLanguages())//
+ .element("languageCode", locale.getLanguage())//
+ .element("queryURL", config.getQueryURL())//
+ .element("layerURL", config.getLayerURL())//
+ .element("modules", config.getModules());
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/portal/ConfigurationException.java b/src/main/java/org/fao/unredd/portal/ConfigurationException.java
new file mode 100644
index 0000000..7830358
--- /dev/null
+++ b/src/main/java/org/fao/unredd/portal/ConfigurationException.java
@@ -0,0 +1,14 @@
+package org.fao.unredd.portal;
+
+public class ConfigurationException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public ConfigurationException(Throwable cause) {
+ super(cause);
+ }
+
+ public ConfigurationException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/portal/ErrorServlet.java b/src/main/java/org/fao/unredd/portal/ErrorServlet.java
new file mode 100644
index 0000000..6d50fe0
--- /dev/null
+++ b/src/main/java/org/fao/unredd/portal/ErrorServlet.java
@@ -0,0 +1,53 @@
+package org.fao.unredd.portal;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+
+public class ErrorServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ private static Logger logger = Logger.getLogger(ErrorServlet.class);
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ Throwable throwable = (Throwable) req
+ .getAttribute("javax.servlet.error.exception");
+ Integer statusCode = (Integer) req
+ .getAttribute("javax.servlet.error.status_code");
+ String servletName = (String) req
+ .getAttribute("javax.servlet.error.servlet_name");
+ String errorMsg = (String) req
+ .getAttribute("javax.servlet.error.message");
+ if (servletName == null) {
+ servletName = "Unknown";
+ }
+ String requestUri = (String) req
+ .getAttribute("javax.servlet.error.request_uri");
+ if (requestUri == null) {
+ requestUri = "Unknown";
+ }
+
+ logger.error("Error handling request: " + requestUri, throwable);
+ if (throwable == null && statusCode == null) {
+ PrintWriter out = resp.getWriter();
+ out.println("Error information is missing ");
+ } else {
+ if (throwable instanceof StatusServletException) {
+ resp.setStatus(((StatusServletException) throwable).getStatus());
+ } else {
+ resp.setStatus(500);
+ }
+ resp.setContentType("application/json");
+ resp.setCharacterEncoding("utf8");
+ resp.getWriter().write("{\"message\":\"" + errorMsg + "\"}");
+ }
+ }
+}
diff --git a/src/main/java/org/fao/unredd/portal/IndexHTMLServlet.java b/src/main/java/org/fao/unredd/portal/IndexHTMLServlet.java
new file mode 100644
index 0000000..7903760
--- /dev/null
+++ b/src/main/java/org/fao/unredd/portal/IndexHTMLServlet.java
@@ -0,0 +1,76 @@
+package org.fao.unredd.portal;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
+import org.apache.velocity.runtime.resource.util.StringResourceRepository;
+
+public class IndexHTMLServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ VelocityEngine engine = new VelocityEngine();
+ engine.setProperty("resource.loader", "string");
+ engine.setProperty("string.resource.loader.class",
+ "org.apache.velocity.runtime.resource.loader.StringResourceLoader");
+ engine.setProperty("string.resource.loader.repository.class",
+ "org.apache.velocity.runtime.resource.util.StringResourceRepositoryImpl");
+ engine.init();
+ VelocityContext context = new VelocityContext();
+
+ ServletContext servletContext = getServletContext();
+ ArrayList styleSheets = getStyleSheets(servletContext, "styles");
+ styleSheets.addAll(getStyleSheets(servletContext, "modules"));
+ context.put("styleSheets", styleSheets);
+
+ String lang = req.getParameter("lang");
+ String url = "config.js";
+ if (lang != null && lang.trim().length() > 0) {
+ url += "?lang=" + lang;
+ }
+ context.put("configUrl", url);
+
+ StringResourceRepository repo = StringResourceLoader.getRepository();
+ String templateName = "/index.html";
+ File index = new File(servletContext.getRealPath("index.html"));
+ BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
+ index));
+ String indexContent = IOUtils.toString(bis);
+ bis.close();
+ repo.putStringResource(templateName, indexContent);
+
+ Template t = engine.getTemplate("/index.html");
+
+ t.merge(context, resp.getWriter());
+ }
+
+ private ArrayList getStyleSheets(ServletContext servletContext,
+ String path) {
+ File styleFolder = new File(servletContext.getRealPath(path));
+ File[] styleSheetFiles = styleFolder.listFiles();
+ ArrayList styleSheets = new ArrayList();
+ for (File file : styleSheetFiles) {
+ String fileName = file.getName();
+ if (fileName.toLowerCase().endsWith(".css")) {
+ styleSheets.add(path + "/" + fileName);
+ }
+ }
+ return styleSheets;
+ }
+}
diff --git a/src/main/java/org/fao/unredd/portal/IndicatorDataServlet.java b/src/main/java/org/fao/unredd/portal/IndicatorDataServlet.java
new file mode 100644
index 0000000..e26a382
--- /dev/null
+++ b/src/main/java/org/fao/unredd/portal/IndicatorDataServlet.java
@@ -0,0 +1,51 @@
+package org.fao.unredd.portal;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.fao.unredd.charts.ChartGenerator;
+import org.fao.unredd.layers.Layer;
+import org.fao.unredd.layers.LayerFactory;
+import org.fao.unredd.layers.NoSuchIndicatorException;
+
+public class IndicatorDataServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ String layerId = req.getParameter("layerId");
+ String indicatorId = req.getParameter("indicatorId");
+ String objectId = req.getParameter("objectId");
+ if (layerId == null || indicatorId == null) {
+ throw new StatusServletExceptionImpl(400,
+ "layerId and indicatorId parameters are mandatory");
+ } else {
+ try {
+ LayerFactory layerFactory = (LayerFactory) getServletContext()
+ .getAttribute("layer-factory");
+ if (layerFactory.exists(layerId)) {
+ Layer layer = layerFactory.newLayer(layerId);
+ ChartGenerator chartGenerator = new ChartGenerator(
+ new ByteArrayInputStream(layer.getOutput(
+ indicatorId).getBytes("UTF-8")));
+ resp.setContentType(chartGenerator.getContentType());
+ chartGenerator.generate(objectId, resp.getWriter());
+ resp.flushBuffer();
+ } else {
+ throw new StatusServletExceptionImpl(400, "The layer "
+ + layerId + " does not exist");
+ }
+ } catch (NoSuchIndicatorException e) {
+ throw new StatusServletExceptionImpl(400, "The indicator "
+ + indicatorId + " does not exist");
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/portal/IndicatorListServlet.java b/src/main/java/org/fao/unredd/portal/IndicatorListServlet.java
new file mode 100644
index 0000000..9b3c45e
--- /dev/null
+++ b/src/main/java/org/fao/unredd/portal/IndicatorListServlet.java
@@ -0,0 +1,45 @@
+package org.fao.unredd.portal;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.fao.unredd.layers.Layer;
+import org.fao.unredd.layers.LayerFactory;
+import org.fao.unredd.layers.Outputs;
+
+public class IndicatorListServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ String layerId = req.getParameter("layerId");
+ if (layerId == null) {
+ throw new StatusServletExceptionImpl(400,
+ "layerId parameter is mandatory");
+ } else {
+ String answer = "[]";
+ LayerFactory layerFactory = (LayerFactory) getServletContext()
+ .getAttribute("layer-factory");
+ if (layerFactory.exists(layerId)) {
+ Layer layer = layerFactory.newLayer(layerId);
+ Outputs indicators = layer.getOutputs();
+ answer = indicators.toJSON();
+ }
+ try {
+ resp.setContentType("application/json");
+ resp.setCharacterEncoding("utf8");
+ resp.getWriter().print(answer);
+ } catch (IOException e) {
+ throw new ServletException(
+ "Could not obtain indicators for layer: " + layerId, e);
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/portal/IndicatorsController.java b/src/main/java/org/fao/unredd/portal/IndicatorsController.java
index d3119a6..66f8588 100644
--- a/src/main/java/org/fao/unredd/portal/IndicatorsController.java
+++ b/src/main/java/org/fao/unredd/portal/IndicatorsController.java
@@ -85,7 +85,7 @@ public void returnIndicator(String objectId, String layerId,
try {
ChartGenerator chartGenerator = new ChartGenerator(
new ByteArrayInputStream(layer.getOutput(
- indicatorId).getBytes()));
+ indicatorId).getBytes("UTF-8")));
response.setContentType(chartGenerator.getContentType());
chartGenerator.generate(objectId, response.getWriter());
response.flushBuffer();
diff --git a/src/main/java/org/fao/unredd/portal/LangFilter.java b/src/main/java/org/fao/unredd/portal/LangFilter.java
new file mode 100644
index 0000000..0022f73
--- /dev/null
+++ b/src/main/java/org/fao/unredd/portal/LangFilter.java
@@ -0,0 +1,37 @@
+package org.fao.unredd.portal;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+public class LangFilter implements Filter {
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response,
+ FilterChain chain) throws IOException, ServletException {
+ String lang = request.getParameter("lang");
+ Locale locale;
+ if (lang != null && lang.trim().length() > 0) {
+ locale = new Locale(lang);
+ } else {
+ locale = request.getLocale();
+ }
+ request.setAttribute("locale", locale);
+ chain.doFilter(request, response);
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/portal/StaticContentServlet.java b/src/main/java/org/fao/unredd/portal/StaticContentServlet.java
new file mode 100644
index 0000000..e668576
--- /dev/null
+++ b/src/main/java/org/fao/unredd/portal/StaticContentServlet.java
@@ -0,0 +1,61 @@
+package org.fao.unredd.portal;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.FileNameMap;
+import java.net.URLConnection;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+
+public class StaticContentServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ private static Logger logger = Logger.getLogger(StaticContentServlet.class);
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ Config config = (Config) getServletContext().getAttribute("config");
+
+ String pathInfo = req.getPathInfo();
+ File file = new File(config.getDir() + "/static/" + pathInfo);
+ if (!file.isFile()) {
+ throw new StatusServletExceptionImpl(404,
+ "The file could not be found");
+ }
+
+ // Manage cache headers: Last-Modified and If-Modified-Since
+ long ifModifiedSince = req.getDateHeader("If-Modified-Since");
+ long lastModified = file.lastModified();
+ if (ifModifiedSince >= (lastModified / 1000 * 1000)) {
+ resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+ return;
+ }
+ resp.setDateHeader("Last-Modified", lastModified);
+
+ // Set content type
+ FileNameMap fileNameMap = URLConnection.getFileNameMap();
+ String type = fileNameMap.getContentTypeFor(pathInfo);
+ resp.setContentType(type);
+
+ // Send contents
+ try {
+ InputStream is = new BufferedInputStream(new FileInputStream(file));
+ IOUtils.copy(is, resp.getOutputStream());
+ } catch (IOException e) {
+ logger.error("Error reading file", e);
+ throw new StatusServletExceptionImpl(500,
+ "Could transfer the resource");
+ }
+ }
+
+}
diff --git a/src/main/java/org/fao/unredd/portal/StatusServletException.java b/src/main/java/org/fao/unredd/portal/StatusServletException.java
new file mode 100644
index 0000000..9e7bdf2
--- /dev/null
+++ b/src/main/java/org/fao/unredd/portal/StatusServletException.java
@@ -0,0 +1,6 @@
+package org.fao.unredd.portal;
+
+public interface StatusServletException {
+
+ int getStatus();
+}
diff --git a/src/main/java/org/fao/unredd/portal/StatusServletExceptionImpl.java b/src/main/java/org/fao/unredd/portal/StatusServletExceptionImpl.java
new file mode 100644
index 0000000..54f2e09
--- /dev/null
+++ b/src/main/java/org/fao/unredd/portal/StatusServletExceptionImpl.java
@@ -0,0 +1,21 @@
+package org.fao.unredd.portal;
+
+import javax.servlet.ServletException;
+
+public class StatusServletExceptionImpl extends ServletException implements
+ StatusServletException {
+ private static final long serialVersionUID = 1L;
+
+ private int status;
+
+ public StatusServletExceptionImpl(int status, String msg) {
+ super(msg);
+ this.status = status;
+ }
+
+ @Override
+ public int getStatus() {
+ return status;
+ }
+
+}
diff --git a/src/main/webapp/WEB-INF/default_config/layers.json b/src/main/webapp/WEB-INF/default_config/layers.json
index 5398c82..be6cf79 100644
--- a/src/main/webapp/WEB-INF/default_config/layers.json
+++ b/src/main/webapp/WEB-INF/default_config/layers.json
@@ -1,101 +1,104 @@
{
- "layers": [
- {
- "id": "forestClassification",
- "label": "${facet_forest_classification}",
- "baseUrl": "http://demo1.geo-solutions.it/diss_geoserver/gwc/service/wms",
- "wmsName": "unredd:drc_forest_classification",
- "imageFormat": "image/png8",
- "visible": true,
- "legend": "forest_classification.png",
- "sourceLink": "http://osfac.net/facet.html",
- "sourceLabel": "FACET"
- },{
- "id": "forest_mask",
- "label": "${forest_mask}",
- "baseUrl": "http://demo1.geo-solutions.it/diss_geoserver/wms",
- "wmsName": "unredd:drc_forest_mask_mosaic",
- "wmsTime": "${time.drc_forest_mask_mosaic}",
- "imageFormat": "image/png8",
- "visible": true,
- "legend": "forest_mask.png",
- "sourceLink": "http://osfac.net/facet.html",
- "sourceLabel": "FACET"
- },{
- "id": "countryBoundaries",
- "baseUrl": "http://demo1.geo-solutions.it/diss_geoserver/gwc/service/wms",
- "wmsName": "unredd:drc_boundary",
- "imageFormat": "image/png8",
- "visible": true,
- "sourceLink": "http://www.wri.org/publication/interactive-forest-atlas-democratic-republic-of-congo",
- "sourceLabel": "WRI"
- },{
- "id": "provinces",
- "baseUrl": "http://demo1.geo-solutions.it/diss_geoserver/gwc/service/wms",
- "wmsName": "unredd:drc_provinces",
- "imageFormat": "image/png8",
- "visible": true,
- "sourceLink": "http://www.wri.org/publication/interactive-forest-atlas-democratic-republic-of-congo",
- "sourceLabel": "WRI",
- "queryable": true
- }
- ],
-
- "contexts": [
- {
- "id": "forestClassification",
- "active": true,
- "infoFile": "forest_classification_def.html",
- "label": "${facet_forest_classification}",
- "layers": ["forestClassification"]
- },{
- "id": "forest_mask",
- "label": "${forest_mask}",
- "layers": ["forest_mask"]
- },{
- "id": "countryBoundaries",
- "active": true,
- "label": "${country_boundaries}",
- "layers": ["countryBoundaries"],
- "inlineLegendUrl": "/diss_geoserver/wms?REQUEST=GetLegendGraphic&VERSION=1.0.0&FORMAT=image/png&WIDTH=20&HEIGHT=20&LAYER=unredd:drc_boundary&TRANSPARENT=true"
- },{
- "id": "provinces",
- "active": true,
- "infoFile": "provinces_def.html",
- "label": "${provinces}",
- "layers": ["provinces"],
- "inlineLegendUrl": "/diss_geoserver/wms?REQUEST=GetLegendGraphic&VERSION=1.0.0&FORMAT=image/png&WIDTH=20&HEIGHT=20&LAYER=unredd:drc_provinces&TRANSPARENT=true"
- }
- ],
-
- "contextGroups":
- {
- "items": [
- {
- "group": {
- "label": "${base_layers}",
- "items": [
- { "context": "forestClassification" }
- ]
- }
- },
- {
- "group": {
- "label": "${admin_areas}",
- "items": [
- { "context": "countryBoundaries" },
- { "context": "provinces" }
- ]
- }
- },
- {
- "group": {
- "label": "${land_cover_maps}",
- "items": [
- { "context": "forest_mask" }
- ]
- }
- }
- ]
- }
+ "wmsLayers" : [ {
+ "id" : "blue-marble",
+ "baseUrl" : "http://rdc-snsf.org/diss_geoserver/wms",
+ "wmsName" : "common:blue_marble",
+ "visible" : true
+ }, {
+ "id" : "forestClassification",
+ "label" : "${facet_forest_classification}",
+ "baseUrl" : "http://demo1.geo-solutions.it/diss_geoserver/gwc/service/wms",
+ "wmsName" : "unredd:drc_forest_classification",
+ "imageFormat" : "image/png8",
+ "visible" : true,
+ "legend" : "forest_classification.png",
+ "sourceLink" : "http://osfac.net/facet.html",
+ "sourceLabel" : "FACET"
+ }, {
+ "id" : "forest_mask",
+ "label" : "${forest_mask}",
+ "baseUrl" : "http://demo1.geo-solutions.it/diss_geoserver/wms",
+ "wmsName" : "unredd:drc_forest_mask_mosaic",
+ "imageFormat" : "image/png8",
+ "visible" : true,
+ "legend" : "forest_mask.png",
+ "sourceLink" : "http://osfac.net/facet.html",
+ "sourceLabel" : "FACET"
+ }, {
+ "id" : "countryBoundaries",
+ "baseUrl" : "http://demo1.geo-solutions.it/diss_geoserver/gwc/service/wms",
+ "wmsName" : "unredd:drc_boundary",
+ "imageFormat" : "image/png8",
+ "visible" : true,
+ "sourceLink" : "http://www.wri.org/publication/interactive-forest-atlas-democratic-republic-of-congo",
+ "sourceLabel" : "WRI"
+ }, {
+ "id" : "provinces",
+ "baseUrl" : "http://demo1.geo-solutions.it/diss_geoserver/gwc/service/wms",
+ "wmsName" : "unredd:drc_provinces",
+ "imageFormat" : "image/png8",
+ "visible" : true,
+ "sourceLink" : "http://www.wri.org/publication/interactive-forest-atlas-democratic-republic-of-congo",
+ "sourceLabel" : "WRI",
+ "queryable" : true,
+ "wmsTime" : "2007-03-01T00:00,2008-05-11T00:00"
+ } ],
+
+ "portalLayers" : [
+ {
+ "id" : "blue-marble",
+ "active" : true,
+ "label" : "Blue marble",
+ "layers" : [ "blue-marble" ]
+ },
+ {
+ "id" : "forestClassification",
+ "active" : true,
+ "infoFile" : "forest_classification_def.html",
+ "label" : "${facet_forest_classification}",
+ "layers" : [ "forestClassification" ]
+ },
+ {
+ "id" : "forest_mask",
+ "label" : "${forest_mask}",
+ "layers" : [ "forest_mask" ]
+ },
+ {
+ "id" : "countryBoundaries",
+ "active" : true,
+ "label" : "${country_boundaries}",
+ "layers" : [ "countryBoundaries" ],
+ "inlineLegendUrl" : "/diss_geoserver/wms?REQUEST=GetLegendGraphic&VERSION=1.0.0&FORMAT=image/png&WIDTH=20&HEIGHT=20&LAYER=unredd:drc_boundary&TRANSPARENT=true"
+ },
+ {
+ "id" : "provinces",
+ "active" : true,
+ "infoFile" : "provinces_def.html",
+ "label" : "${provinces}",
+ "layers" : [ "provinces" ],
+ "inlineLegendUrl" : "/diss_geoserver/wms?REQUEST=GetLegendGraphic&VERSION=1.0.0&FORMAT=image/png&WIDTH=20&HEIGHT=20&LAYER=unredd:drc_provinces&TRANSPARENT=true"
+ } ],
+
+ "groups" : [ {
+ "id" : "base",
+ "label" : "${base_layers}",
+ "infoFile": "base_layers.html",
+ "items" : [ {
+ "id" : "innerbase",
+ "label" : "General purpose",
+ "items": ["blue-marble"]
+ }, {
+ "id" : "innerforest",
+ "label" : "Forest classifications",
+ "items": [ "forestClassification" ]
+ } ]
+ }, {
+ "id" : "admin",
+ "label" : "${admin_areas}",
+ "items" : [ "countryBoundaries", "provinces" ]
+ }, {
+ "id" : "landcover",
+ "label" : "${land_cover_maps}",
+ "items" : [ "forest_mask" ]
+ } ]
}
diff --git a/src/main/webapp/WEB-INF/default_config/messages/messages.properties b/src/main/webapp/WEB-INF/default_config/messages/messages.properties
index 36b4d91..952b266 100644
--- a/src/main/webapp/WEB-INF/default_config/messages/messages.properties
+++ b/src/main/webapp/WEB-INF/default_config/messages/messages.properties
@@ -53,3 +53,6 @@ statistics_not_supported=Statistics are not supported in this version
submit=Submit
subtitle=Subtitle
title=NFMS Sample Portal
+en=English
+es=Spanish
+fr=French
diff --git a/src/main/webapp/WEB-INF/default_config/messages/messages_en.properties b/src/main/webapp/WEB-INF/default_config/messages/messages_en.properties
index 36b4d91..dd3cbc8 100644
--- a/src/main/webapp/WEB-INF/default_config/messages/messages_en.properties
+++ b/src/main/webapp/WEB-INF/default_config/messages/messages_en.properties
@@ -53,3 +53,7 @@ statistics_not_supported=Statistics are not supported in this version
submit=Submit
subtitle=Subtitle
title=NFMS Sample Portal
+en=English
+es=Spanish
+fr=French
+
diff --git a/src/main/webapp/WEB-INF/default_config/messages/messages_es.properties b/src/main/webapp/WEB-INF/default_config/messages/messages_es.properties
index 200c6a2..fdbaeaa 100644
--- a/src/main/webapp/WEB-INF/default_config/messages/messages_es.properties
+++ b/src/main/webapp/WEB-INF/default_config/messages/messages_es.properties
@@ -53,3 +53,6 @@ statistics_not_supported=Las estad\u00edsticas no est\u00e1n soportadas en esta
submit=Enviar
subtitle=Subt\u00edtulo
title=Ejemplo de portal NFMS
+en=Ingl\u00E9s
+es=Espa\u00f1ol
+fr=Franc\u00E9s
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/default_config/messages/messages_fr.properties b/src/main/webapp/WEB-INF/default_config/messages/messages_fr.properties
index 3e3234a..564ff62 100644
--- a/src/main/webapp/WEB-INF/default_config/messages/messages_fr.properties
+++ b/src/main/webapp/WEB-INF/default_config/messages/messages_fr.properties
@@ -53,3 +53,6 @@ statistics_not_supported=Les statistiques ne sont pas support\u00e9s dans cette
submit=Envoyer
subtitle=Soustitre
title=Exemple du Portail NFMS
+en=Anglais
+es=Espagnol
+fr=Fran\u00E7ais
diff --git a/src/main/webapp/WEB-INF/default_config/portal.properties b/src/main/webapp/WEB-INF/default_config/portal.properties
index 4c0d297..1be1a11 100644
--- a/src/main/webapp/WEB-INF/default_config/portal.properties
+++ b/src/main/webapp/WEB-INF/default_config/portal.properties
@@ -2,3 +2,4 @@ languages = {"en": "English", "fr": "Fran\u00e7ais", "es": "Espa\u00f1ol"}
recaptcha.publickey = 6Ld5ydQSAAAAAGtZJG67QkQM7Z13X6MGf72RtmDE
recaptcha.privatekey = 6Ld5ydQSAAAAAJW3To_tN6czS7C-HCnsBVhENfD9
layers.rootFolder=/tmp
+client.modules=layers,communication,iso8601,error-management,map,banner,toolbar,time-slider,layer-list,info-control,info-dialog,center,zoom-bar,layer-list-selector,active-layer-list
diff --git a/src/main/webapp/WEB-INF/jsp/index.jsp b/src/main/webapp/WEB-INF/jsp/index.jsp
deleted file mode 100644
index 77ff3a0..0000000
--- a/src/main/webapp/WEB-INF/jsp/index.jsp
+++ /dev/null
@@ -1,199 +0,0 @@
-<%@page import="java.util.Enumeration"%>
-<%@page import="java.util.Locale"%>
-<%@page import="java.util.ResourceBundle"%>
-<%@page import="net.tanesha.recaptcha.ReCaptchaImpl"%>
-<%@page session="true"%>
-<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
-<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
-<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
-<%@taglib prefix="pack" uri="http://packtag.sf.net"%>
-<%@page contentType="text/html" pageEncoding="UTF-8"%>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- /js/OpenLayers.unredd.js
- /js/jquery-1.7.1.js
- /js/jquery.mustache.js
- /js/jquery-ui-1.8.16.custom.min.js
- /js/jquery.fancybox.js
- /js/ol-extensions/PortalToolbar.js
- /js/unredd.js
- ${base}/static/custom.js
-
-
-
- /css/openlayers/style.css
- /css/jquery-ui-1.8.16.custom.css
- /css/jquery.fancybox.css
- /css/toolbar.css
- ${base}/static/unredd.css
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ${config.footer}
-
-
-
-
-
-
-
-
-
-
">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/webapp/WEB-INF/packtag.properties b/src/main/webapp/WEB-INF/packtag.properties
deleted file mode 100644
index 2b0aa19..0000000
--- a/src/main/webapp/WEB-INF/packtag.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-cache.servlet.combined.js.path=js
-cache.servlet.combined.style.path=css
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/unreddPortalApplicationContext.xml b/src/main/webapp/WEB-INF/unreddPortalApplicationContext.xml
deleted file mode 100644
index 99eef95..0000000
--- a/src/main/webapp/WEB-INF/unreddPortalApplicationContext.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
index dded3d3..294215d 100644
--- a/src/main/webapp/WEB-INF/web.xml
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -1,62 +1,100 @@
-
-
- org.springframework.web.context.ContextLoaderListener
-
-
- org.springframework.web.context.request.RequestContextListener
-
-
- contextConfigLocation
- /WEB-INF/unreddPortalApplicationContext.xml
-
+
+
+ org.fao.unredd.AppContextListener
+
-
- Spring MVC Dispatcher Servlet
- org.springframework.web.servlet.DispatcherServlet
-
- contextConfigLocation
- /WEB-INF/unreddPortalApplicationContext.xml
-
- 1
-
-
- Spring MVC Dispatcher Servlet
- /index.do
- *.json
- /static/*
- /feedback
-
+
+ lang-filter
+ org.fao.unredd.portal.LangFilter
+
+
+ lang-filter
+ /*
+
-
-
- proxyPropPath
- /proxy.properties
-
-
- HttpProxy
- it.geosolutions.httpproxy.HTTPProxy
-
-
- HttpProxy
- /proxy
-
-
-
- Needed for pack:tag. Returns the packed resources.
- PackServlet
- PackServlet
- net.sf.packtag.servlet.PackServlet
+
+
+ index-servlet
+ org.fao.unredd.portal.IndexHTMLServlet
- PackServlet
- *.pack
+ index-servlet
+ /index.html
+
+
+
+
+ static-content-servlet
+ org.fao.unredd.portal.StaticContentServlet
+
+
+ static-content-servlet
+ /static/*
+
+
+
+
+ config-servlet
+ org.fao.unredd.portal.ConfigServlet
+
+
+ config-servlet
+ /config.js
+
+
+
+
+ indicator-list-servlet
+ org.fao.unredd.portal.IndicatorListServlet
+
+
+ indicator-list-servlet
+ /indicators
+
+
+ indicator-data-servlet
+ org.fao.unredd.portal.IndicatorDataServlet
+
+
+ indicator-data-servlet
+ /indicator
+
+
+
+
+ error-servlet
+ org.fao.unredd.portal.ErrorServlet
+
+
+ error-servlet
+ /error
+
+
+ java.lang.Throwable
+ /error
+
+
+
+
+ proxyPropPath
+ /proxy.properties
+
+
+ HttpProxy
+ it.geosolutions.httpproxy.HTTPProxy
+
+
+ HttpProxy
+ /proxy
-
- 30
-
-
- index.jsp
-
+
+ 30
+
+
+ index.html
+
diff --git a/src/main/webapp/css/images/blank.gif b/src/main/webapp/css/images/blank.gif
deleted file mode 100644
index 35d42e8..0000000
Binary files a/src/main/webapp/css/images/blank.gif and /dev/null differ
diff --git a/src/main/webapp/css/images/fancybox_loading.gif b/src/main/webapp/css/images/fancybox_loading.gif
deleted file mode 100644
index 742131f..0000000
Binary files a/src/main/webapp/css/images/fancybox_loading.gif and /dev/null differ
diff --git a/src/main/webapp/css/images/fancybox_sprite.png b/src/main/webapp/css/images/fancybox_sprite.png
deleted file mode 100644
index 753021b..0000000
Binary files a/src/main/webapp/css/images/fancybox_sprite.png and /dev/null differ
diff --git a/src/main/webapp/css/images/silk/add.png b/src/main/webapp/css/images/silk/add.png
deleted file mode 100644
index 6332fef..0000000
Binary files a/src/main/webapp/css/images/silk/add.png and /dev/null differ
diff --git a/src/main/webapp/css/images/silk/down.png b/src/main/webapp/css/images/silk/down.png
deleted file mode 100644
index f3d54be..0000000
Binary files a/src/main/webapp/css/images/silk/down.png and /dev/null differ
diff --git a/src/main/webapp/css/images/silk/pencil.png b/src/main/webapp/css/images/silk/pencil.png
deleted file mode 100644
index 0bfecd5..0000000
Binary files a/src/main/webapp/css/images/silk/pencil.png and /dev/null differ
diff --git a/src/main/webapp/css/images/silk/readme.txt b/src/main/webapp/css/images/silk/readme.txt
deleted file mode 100644
index 400a64d..0000000
--- a/src/main/webapp/css/images/silk/readme.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-Silk icon set 1.3
-
-_________________________________________
-Mark James
-http://www.famfamfam.com/lab/icons/silk/
-_________________________________________
-
-This work is licensed under a
-Creative Commons Attribution 2.5 License.
-[ http://creativecommons.org/licenses/by/2.5/ ]
-
-This means you may use it for any purpose,
-and make any changes you like.
-All I ask is that you include a link back
-to this page in your credits.
-
-Are you using this icon set? Send me an email
-(including a link or picture if available) to
-mjames@gmail.com
-
-Any other questions about this icon set please
-contact mjames@gmail.com
\ No newline at end of file
diff --git a/src/main/webapp/css/images/ui-bg_flat_30_cccccc_40x100.png b/src/main/webapp/css/images/ui-bg_flat_30_cccccc_40x100.png
deleted file mode 100644
index 5473aff..0000000
Binary files a/src/main/webapp/css/images/ui-bg_flat_30_cccccc_40x100.png and /dev/null differ
diff --git a/src/main/webapp/css/images/ui-bg_flat_50_5c5c5c_40x100.png b/src/main/webapp/css/images/ui-bg_flat_50_5c5c5c_40x100.png
deleted file mode 100644
index 5950a8d..0000000
Binary files a/src/main/webapp/css/images/ui-bg_flat_50_5c5c5c_40x100.png and /dev/null differ
diff --git a/src/main/webapp/css/images/ui-bg_glass_40_ffc73d_1x400.png b/src/main/webapp/css/images/ui-bg_glass_40_ffc73d_1x400.png
deleted file mode 100644
index 35ec0d9..0000000
Binary files a/src/main/webapp/css/images/ui-bg_glass_40_ffc73d_1x400.png and /dev/null differ
diff --git a/src/main/webapp/css/images/ui-bg_highlight-soft_80_eeeeee_1x100.png b/src/main/webapp/css/images/ui-bg_highlight-soft_80_eeeeee_1x100.png
deleted file mode 100644
index e56eefd..0000000
Binary files a/src/main/webapp/css/images/ui-bg_highlight-soft_80_eeeeee_1x100.png and /dev/null differ
diff --git a/src/main/webapp/css/images/ui-icons_222222_256x240.png b/src/main/webapp/css/images/ui-icons_222222_256x240.png
deleted file mode 100644
index b273ff1..0000000
Binary files a/src/main/webapp/css/images/ui-icons_222222_256x240.png and /dev/null differ
diff --git a/src/main/webapp/css/images/ui-icons_4b8e0b_256x240.png b/src/main/webapp/css/images/ui-icons_4b8e0b_256x240.png
deleted file mode 100644
index 3bdb67b..0000000
Binary files a/src/main/webapp/css/images/ui-icons_4b8e0b_256x240.png and /dev/null differ
diff --git a/src/main/webapp/css/images/ui-icons_a83300_256x240.png b/src/main/webapp/css/images/ui-icons_a83300_256x240.png
deleted file mode 100644
index 020371b..0000000
Binary files a/src/main/webapp/css/images/ui-icons_a83300_256x240.png and /dev/null differ
diff --git a/src/main/webapp/css/images/ui-icons_cccccc_256x240.png b/src/main/webapp/css/images/ui-icons_cccccc_256x240.png
deleted file mode 100644
index 9254e05..0000000
Binary files a/src/main/webapp/css/images/ui-icons_cccccc_256x240.png and /dev/null differ
diff --git a/src/main/webapp/css/images/ui-icons_ffffff_256x240.png b/src/main/webapp/css/images/ui-icons_ffffff_256x240.png
deleted file mode 100644
index 42f8f99..0000000
Binary files a/src/main/webapp/css/images/ui-icons_ffffff_256x240.png and /dev/null differ
diff --git a/src/main/webapp/css/jquery-ui-1.8.16.custom.css b/src/main/webapp/css/jquery-ui-1.8.16.custom.css
deleted file mode 100644
index 1979099..0000000
--- a/src/main/webapp/css/jquery-ui-1.8.16.custom.css
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- * jQuery UI CSS Framework 1.8.16
- *
- * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Theming/API
- */
-
-/* Layout helpers
-----------------------------------*/
-.ui-helper-hidden { display: none; }
-.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
-.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
-.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
-.ui-helper-clearfix { display: inline-block; }
-/* required comment for clearfix to work in Opera \*/
-* html .ui-helper-clearfix { height:1%; }
-.ui-helper-clearfix { display:block; }
-/* end clearfix */
-.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
-
-
-/* Interaction Cues
-----------------------------------*/
-.ui-state-disabled { cursor: default !important; }
-
-
-/* Icons
-----------------------------------*/
-
-/* states and images */
-.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
-
-
-/* Misc visuals
-----------------------------------*/
-
-/* Overlays */
-.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
-
-
-/*
- * jQuery UI CSS Framework 1.8.16
- *
- * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Theming/API
- *
- * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,%20Arial,%20sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=6px&bgColorHeader=444444&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=44&borderColorHeader=333333&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=000000&bgTextureContent=01_flat.png&bgImgOpacityContent=25&borderColorContent=555555&fcContent=ffffff&iconColorContent=cccccc&bgColorDefault=222222&bgTextureDefault=03_highlight_soft.png&bgImgOpacityDefault=35&borderColorDefault=444444&fcDefault=eeeeee&iconColorDefault=cccccc&bgColorHover=003147&bgTextureHover=03_highlight_soft.png&bgImgOpacityHover=33&borderColorHover=0b93d5&fcHover=ffffff&iconColorHover=ffffff&bgColorActive=0972a5&bgTextureActive=04_highlight_hard.png&bgImgOpacityActive=20&borderColorActive=26b3f7&fcActive=ffffff&iconColorActive=222222&bgColorHighlight=eeeeee&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=80&borderColorHighlight=cccccc&fcHighlight=2e7db2&iconColorHighlight=4b8e0b&bgColorError=ffc73d&bgTextureError=02_glass.png&bgImgOpacityError=40&borderColorError=ffb73d&fcError=111111&iconColorError=a83300&bgColorOverlay=5c5c5c&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=50&opacityOverlay=80&bgColorShadow=cccccc&bgTextureShadow=01_flat.png&bgImgOpacityShadow=30&opacityShadow=60&thicknessShadow=7px&offsetTopShadow=-7px&offsetLeftShadow=-7px&cornerRadiusShadow=8px
- */
-
-
-/* Component containers
-----------------------------------*/
-.ui-widget { font-family: Verdana, Arial, sans-serif; font-size: 1.1em; }
-.ui-widget .ui-widget { font-size: 1em; }
-.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana, Arial, sans-serif; font-size: 1em; }
-.ui-widget-content { border: 1px solid #555555; background: #000000 url(images/ui-bg_flat_25_000000_40x100.png) 50% 50% repeat-x; color: #ffffff; }
-.ui-widget-content a { color: #ffffff; }
-.ui-widget-header { border: 1px solid #333333; background: #444444 url(images/ui-bg_highlight-soft_44_444444_1x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
-.ui-widget-header a { color: #ffffff; }
-
-/* Interaction states
-----------------------------------*/
-.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #444444; background: #222222 url(images/ui-bg_highlight-soft_35_222222_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #eeeeee; }
-.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #eeeeee; text-decoration: none; }
-.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #0b93d5; background: #003147 url(images/ui-bg_highlight-soft_33_003147_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; }
-.ui-state-hover a, .ui-state-hover a:hover { color: #ffffff; text-decoration: none; }
-.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #26b3f7; background: #0972a5 url(images/ui-bg_highlight-hard_20_0972a5_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; }
-.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #ffffff; text-decoration: none; }
-.ui-widget :active { outline: none; }
-
-/* Interaction Cues
-----------------------------------*/
-.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #cccccc; background: #eeeeee url(images/ui-bg_highlight-soft_80_eeeeee_1x100.png) 50% top repeat-x; color: #2e7db2; }
-.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #2e7db2; }
-.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #ffb73d; background: #ffc73d url(images/ui-bg_glass_40_ffc73d_1x400.png) 50% 50% repeat-x; color: #111111; }
-.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #111111; }
-.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #111111; }
-.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
-.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
-.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
-
-/* Icons
-----------------------------------*/
-
-/* states and images */
-.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_cccccc_256x240.png); }
-.ui-widget-content .ui-icon {background-image: url(images/ui-icons_cccccc_256x240.png); }
-.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
-.ui-state-default .ui-icon { background-image: url(images/ui-icons_cccccc_256x240.png); }
-.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
-.ui-state-active .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
-.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_4b8e0b_256x240.png); }
-.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_a83300_256x240.png); }
-
-/* positioning */
-.ui-icon-carat-1-n { background-position: 0 0; }
-.ui-icon-carat-1-ne { background-position: -16px 0; }
-.ui-icon-carat-1-e { background-position: -32px 0; }
-.ui-icon-carat-1-se { background-position: -48px 0; }
-.ui-icon-carat-1-s { background-position: -64px 0; }
-.ui-icon-carat-1-sw { background-position: -80px 0; }
-.ui-icon-carat-1-w { background-position: -96px 0; }
-.ui-icon-carat-1-nw { background-position: -112px 0; }
-.ui-icon-carat-2-n-s { background-position: -128px 0; }
-.ui-icon-carat-2-e-w { background-position: -144px 0; }
-.ui-icon-triangle-1-n { background-position: 0 -16px; }
-.ui-icon-triangle-1-ne { background-position: -16px -16px; }
-.ui-icon-triangle-1-e { background-position: -32px -16px; }
-.ui-icon-triangle-1-se { background-position: -48px -16px; }
-.ui-icon-triangle-1-s { background-position: -64px -16px; }
-.ui-icon-triangle-1-sw { background-position: -80px -16px; }
-.ui-icon-triangle-1-w { background-position: -96px -16px; }
-.ui-icon-triangle-1-nw { background-position: -112px -16px; }
-.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
-.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
-.ui-icon-arrow-1-n { background-position: 0 -32px; }
-.ui-icon-arrow-1-ne { background-position: -16px -32px; }
-.ui-icon-arrow-1-e { background-position: -32px -32px; }
-.ui-icon-arrow-1-se { background-position: -48px -32px; }
-.ui-icon-arrow-1-s { background-position: -64px -32px; }
-.ui-icon-arrow-1-sw { background-position: -80px -32px; }
-.ui-icon-arrow-1-w { background-position: -96px -32px; }
-.ui-icon-arrow-1-nw { background-position: -112px -32px; }
-.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
-.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
-.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
-.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
-.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
-.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
-.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
-.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
-.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
-.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
-.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
-.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
-.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
-.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
-.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
-.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
-.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
-.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
-.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
-.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
-.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
-.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
-.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
-.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
-.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
-.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
-.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
-.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
-.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
-.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
-.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
-.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
-.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
-.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
-.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
-.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
-.ui-icon-arrow-4 { background-position: 0 -80px; }
-.ui-icon-arrow-4-diag { background-position: -16px -80px; }
-.ui-icon-extlink { background-position: -32px -80px; }
-.ui-icon-newwin { background-position: -48px -80px; }
-.ui-icon-refresh { background-position: -64px -80px; }
-.ui-icon-shuffle { background-position: -80px -80px; }
-.ui-icon-transfer-e-w { background-position: -96px -80px; }
-.ui-icon-transferthick-e-w { background-position: -112px -80px; }
-.ui-icon-folder-collapsed { background-position: 0 -96px; }
-.ui-icon-folder-open { background-position: -16px -96px; }
-.ui-icon-document { background-position: -32px -96px; }
-.ui-icon-document-b { background-position: -48px -96px; }
-.ui-icon-note { background-position: -64px -96px; }
-.ui-icon-mail-closed { background-position: -80px -96px; }
-.ui-icon-mail-open { background-position: -96px -96px; }
-.ui-icon-suitcase { background-position: -112px -96px; }
-.ui-icon-comment { background-position: -128px -96px; }
-.ui-icon-person { background-position: -144px -96px; }
-.ui-icon-print { background-position: -160px -96px; }
-.ui-icon-trash { background-position: -176px -96px; }
-.ui-icon-locked { background-position: -192px -96px; }
-.ui-icon-unlocked { background-position: -208px -96px; }
-.ui-icon-bookmark { background-position: -224px -96px; }
-.ui-icon-tag { background-position: -240px -96px; }
-.ui-icon-home { background-position: 0 -112px; }
-.ui-icon-flag { background-position: -16px -112px; }
-.ui-icon-calendar { background-position: -32px -112px; }
-.ui-icon-cart { background-position: -48px -112px; }
-.ui-icon-pencil { background-position: -64px -112px; }
-.ui-icon-clock { background-position: -80px -112px; }
-.ui-icon-disk { background-position: -96px -112px; }
-.ui-icon-calculator { background-position: -112px -112px; }
-.ui-icon-zoomin { background-position: -128px -112px; }
-.ui-icon-zoomout { background-position: -144px -112px; }
-.ui-icon-search { background-position: -160px -112px; }
-.ui-icon-wrench { background-position: -176px -112px; }
-.ui-icon-gear { background-position: -192px -112px; }
-.ui-icon-heart { background-position: -208px -112px; }
-.ui-icon-star { background-position: -224px -112px; }
-.ui-icon-link { background-position: -240px -112px; }
-.ui-icon-cancel { background-position: 0 -128px; }
-.ui-icon-plus { background-position: -16px -128px; }
-.ui-icon-plusthick { background-position: -32px -128px; }
-.ui-icon-minus { background-position: -48px -128px; }
-.ui-icon-minusthick { background-position: -64px -128px; }
-.ui-icon-close { background-position: -80px -128px; }
-.ui-icon-closethick { background-position: -96px -128px; }
-.ui-icon-key { background-position: -112px -128px; }
-.ui-icon-lightbulb { background-position: -128px -128px; }
-.ui-icon-scissors { background-position: -144px -128px; }
-.ui-icon-clipboard { background-position: -160px -128px; }
-.ui-icon-copy { background-position: -176px -128px; }
-.ui-icon-contact { background-position: -192px -128px; }
-.ui-icon-image { background-position: -208px -128px; }
-.ui-icon-video { background-position: -224px -128px; }
-.ui-icon-script { background-position: -240px -128px; }
-.ui-icon-alert { background-position: 0 -144px; }
-.ui-icon-info { background-position: -16px -144px; }
-.ui-icon-notice { background-position: -32px -144px; }
-.ui-icon-help { background-position: -48px -144px; }
-.ui-icon-check { background-position: -64px -144px; }
-.ui-icon-bullet { background-position: -80px -144px; }
-.ui-icon-radio-off { background-position: -96px -144px; }
-.ui-icon-radio-on { background-position: -112px -144px; }
-.ui-icon-pin-w { background-position: -128px -144px; }
-.ui-icon-pin-s { background-position: -144px -144px; }
-.ui-icon-play { background-position: 0 -160px; }
-.ui-icon-pause { background-position: -16px -160px; }
-.ui-icon-seek-next { background-position: -32px -160px; }
-.ui-icon-seek-prev { background-position: -48px -160px; }
-.ui-icon-seek-end { background-position: -64px -160px; }
-.ui-icon-seek-start { background-position: -80px -160px; }
-/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
-.ui-icon-seek-first { background-position: -80px -160px; }
-.ui-icon-stop { background-position: -96px -160px; }
-.ui-icon-eject { background-position: -112px -160px; }
-.ui-icon-volume-off { background-position: -128px -160px; }
-.ui-icon-volume-on { background-position: -144px -160px; }
-.ui-icon-power { background-position: 0 -176px; }
-.ui-icon-signal-diag { background-position: -16px -176px; }
-.ui-icon-signal { background-position: -32px -176px; }
-.ui-icon-battery-0 { background-position: -48px -176px; }
-.ui-icon-battery-1 { background-position: -64px -176px; }
-.ui-icon-battery-2 { background-position: -80px -176px; }
-.ui-icon-battery-3 { background-position: -96px -176px; }
-.ui-icon-circle-plus { background-position: 0 -192px; }
-.ui-icon-circle-minus { background-position: -16px -192px; }
-.ui-icon-circle-close { background-position: -32px -192px; }
-.ui-icon-circle-triangle-e { background-position: -48px -192px; }
-.ui-icon-circle-triangle-s { background-position: -64px -192px; }
-.ui-icon-circle-triangle-w { background-position: -80px -192px; }
-.ui-icon-circle-triangle-n { background-position: -96px -192px; }
-.ui-icon-circle-arrow-e { background-position: -112px -192px; }
-.ui-icon-circle-arrow-s { background-position: -128px -192px; }
-.ui-icon-circle-arrow-w { background-position: -144px -192px; }
-.ui-icon-circle-arrow-n { background-position: -160px -192px; }
-.ui-icon-circle-zoomin { background-position: -176px -192px; }
-.ui-icon-circle-zoomout { background-position: -192px -192px; }
-.ui-icon-circle-check { background-position: -208px -192px; }
-.ui-icon-circlesmall-plus { background-position: 0 -208px; }
-.ui-icon-circlesmall-minus { background-position: -16px -208px; }
-.ui-icon-circlesmall-close { background-position: -32px -208px; }
-.ui-icon-squaresmall-plus { background-position: -48px -208px; }
-.ui-icon-squaresmall-minus { background-position: -64px -208px; }
-.ui-icon-squaresmall-close { background-position: -80px -208px; }
-.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
-.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
-.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
-.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
-.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
-.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
-
-
-/* Misc visuals
-----------------------------------*/
-
-/* Corner radius */
-.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; -khtml-border-top-left-radius: 6px; border-top-left-radius: 6px; }
-.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; -khtml-border-top-right-radius: 6px; border-top-right-radius: 6px; }
-.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; -khtml-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; }
-.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; -khtml-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; }
-
-/* Overlays */
-.ui-widget-overlay { background: #5c5c5c url(images/ui-bg_flat_50_5c5c5c_40x100.png) 50% 50% repeat-x; opacity: .80;filter:Alpha(Opacity=80); }
-.ui-widget-shadow { margin: -7px 0 0 -7px; padding: 7px; background: #cccccc url(images/ui-bg_flat_30_cccccc_40x100.png) 50% 50% repeat-x; opacity: .60;filter:Alpha(Opacity=60); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
- * jQuery UI Accordion 1.8.16
- *
- * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Accordion#theming
- */
-/* IE/Win - Fix animation bug - #4615 */
-.ui-accordion { width: 100%; }
-.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
-.ui-accordion .ui-accordion-li-fix { display: inline; }
-.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
-.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
-.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
-.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
-.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
-.ui-accordion .ui-accordion-content-active { display: block; }
-/*
- * jQuery UI Button 1.8.16
- *
- * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Button#theming
- */
-.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
-.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
-button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
-.ui-button-icons-only { width: 3.4em; }
-button.ui-button-icons-only { width: 3.7em; }
-
-/*button text element */
-.ui-button .ui-button-text { display: block; line-height: 1.4; }
-.ui-button-text-only .ui-button-text { padding: .4em 1em; }
-.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
-.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
-.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
-.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
-/* no icon support for input elements, provide padding by default */
-input.ui-button { padding: .4em 1em; }
-
-/*button icon element(s) */
-.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
-.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
-.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
-.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
-.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
-
-/*button sets*/
-.ui-buttonset { margin-right: 7px; }
-.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
-
-/* workarounds */
-button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
-/*
- * jQuery UI Dialog 1.8.16
- *
- * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Dialog#theming
- */
-.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
-.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
-.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
-.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
-.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
-.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
-.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
-.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
-.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
-.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
-.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
-.ui-draggable .ui-dialog-titlebar { cursor: move; }
-/*
- * jQuery UI Slider 1.8.16
- *
- * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Slider#theming
- */
-.ui-slider { position: relative; text-align: left; }
-.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
-.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
-
-.ui-slider-horizontal { height: .8em; }
-.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
-.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
-.ui-slider-horizontal .ui-slider-range-min { left: 0; }
-.ui-slider-horizontal .ui-slider-range-max { right: 0; }
-
-.ui-slider-vertical { width: .8em; height: 100px; }
-.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
-.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
-.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
-.ui-slider-vertical .ui-slider-range-max { top: 0; }
\ No newline at end of file
diff --git a/src/main/webapp/css/openlayers/google.css b/src/main/webapp/css/openlayers/google.css
deleted file mode 100644
index 3ee757c..0000000
--- a/src/main/webapp/css/openlayers/google.css
+++ /dev/null
@@ -1,17 +0,0 @@
-.olLayerGoogleCopyright {
- right: 3px;
- bottom: 2px;
- left: auto;
-}
-.olLayerGoogleV3.olLayerGoogleCopyright {
- bottom: 0px;
- right: 0px !important;
-}
-.olLayerGooglePoweredBy {
- left: 2px;
- bottom: 2px;
-}
-.olLayerGoogleV3.olLayerGooglePoweredBy {
- bottom: 0px !important;
-}
-
diff --git a/src/main/webapp/css/openlayers/google.tidy.css b/src/main/webapp/css/openlayers/google.tidy.css
deleted file mode 100644
index 4ba0cd8..0000000
--- a/src/main/webapp/css/openlayers/google.tidy.css
+++ /dev/null
@@ -1 +0,0 @@
-.olLayerGoogleCopyright{right:3px;bottom:2px;left:auto;}.olLayerGoogleV3.olLayerGoogleCopyright{bottom:0;right:0!important;}.olLayerGooglePoweredBy{left:2px;bottom:2px;}.olLayerGoogleV3.olLayerGooglePoweredBy{bottom:0!important;}
\ No newline at end of file
diff --git a/src/main/webapp/css/openlayers/style.mobile.tidy.css b/src/main/webapp/css/openlayers/style.mobile.tidy.css
deleted file mode 100644
index 10b8bff..0000000
--- a/src/main/webapp/css/openlayers/style.mobile.tidy.css
+++ /dev/null
@@ -1 +0,0 @@
-div.olControlZoom{position:absolute;top:8px;left:8px;background:rgba(255,255,255,0.4);border-radius:4px;padding:2px;}*{-webkit-tap-highlight-color:rgba(0,0,0,0);}div.olControlZoom a{display:block;color:#FFF;font-size:28px;font-family:sans-serif;font-weight:700;text-decoration:none;text-align:center;height:32px;width:32px;line-height:28px;text-shadow:0 0 3px rgba(0,0,0,0.8);background:rgba(0,60,136,0.5);filter:alpha(opacity=80);margin:1px;padding:0;}a.olControlZoomIn{border-radius:4px 4px 0 0;}a.olControlZoomOut{border-radius:0 0 4px 4px;}div.olControlZoom a:hover{background:rgba(0,60,136,0.7);filter:alpha(opacity=100);}.olLayerGrid .olTileImage{-webkit-transition:opacity .2s linear;-moz-transition:opacity .2s linear;-o-transition:opacity .2s linear;transition:opacity .2s linear;}@media only screen and max-width 600px{div.olControlZoom a:hover{background:rgba(0,60,136,0.5);}}@media -webkit-transform-3d{img.olTileImage{-webkit-transform:translate3d(0,0,0);}}
\ No newline at end of file
diff --git a/src/main/webapp/css/openlayers/style.tidy.css b/src/main/webapp/css/openlayers/style.tidy.css
deleted file mode 100644
index 0368055..0000000
--- a/src/main/webapp/css/openlayers/style.tidy.css
+++ /dev/null
@@ -1 +0,0 @@
-div.olMap{z-index:0;cursor:default;margin:0!important;padding:0!important;}div.olMapViewport{text-align:left;}.olLayerGoogleCopyright{left:2px;bottom:2px;}.olLayerGoogleV3.olLayerGoogleCopyright{right:auto!important;}.olLayerGooglePoweredBy{left:2px;bottom:15px;}.olLayerGoogleV3.olLayerGooglePoweredBy{bottom:15px!important;}.olControlAttribution{font-size:smaller;right:3px;bottom:4.5em;position:absolute;display:block;}.olControlScale{right:3px;bottom:3em;display:block;position:absolute;font-size:smaller;}.olControlScaleLine{display:block;position:absolute;left:10px;bottom:15px;font-size:xx-small;}.olControlScaleLineBottom{border:solid 2px #000;border-bottom:none;margin-top:-2px;text-align:center;}.olControlScaleLineTop{border:solid 2px #000;border-top:none;text-align:center;}.olControlPermalink{right:3px;bottom:1.5em;display:block;position:absolute;font-size:smaller;}div.olControlMousePosition{bottom:0;right:3px;display:block;position:absolute;font-family:Arial;font-size:smaller;}.olControlOverviewMapContainer{position:absolute;bottom:0;right:0;}.olControlOverviewMapElement{background-color:#00008B;-moz-border-radius:1em 0 0;padding:10px 18px 10px 10px;}.olControlOverviewMapMinimizeButton,.olControlOverviewMapMaximizeButton{height:18px;width:18px;right:0;bottom:80px;cursor:pointer;}.olControlOverviewMapExtentRectangle{overflow:hidden;background-image:url(img/blank.gif);cursor:move;border:2px dotted red;}.olControlOverviewMapRectReplacement{overflow:hidden;cursor:move;background-image:url(img/overview_replacement.gif);background-repeat:no-repeat;background-position:center;}.olLayerGeoRSSDescription{float:left;width:100%;overflow:auto;font-size:1em;}.olLayerGeoRSSClose{float:right;color:gray;font-size:1.2em;margin-right:6px;font-family:sans-serif;}.olLayerGeoRSSTitle{float:left;font-size:1.2em;}.olControlNavigationHistory{background-image:url(img/navigation_history.png);background-repeat:no-repeat;width:24px;height:24px;}.olControlNavigationHistoryPreviousItemActive{background-position:0 0;}.olControlNavigationHistoryPreviousItemInactive{background-position:0 -24px;}.olControlNavigationHistoryNextItemActive{background-position:-24px 0;}.olControlNavigationHistoryNextItemInactive{background-position:-24px -24px;}div.olControlSaveFeaturesItemActive{background-image:url(img/save_features_on.png);background-repeat:no-repeat;background-position:0 1px;}div.olControlSaveFeaturesItemInactive{background-image:url(img/save_features_off.png);background-repeat:no-repeat;background-position:0 1px;}.olHandlerBoxZoomBox{border:2px solid red;position:absolute;background-color:#FFF;opacity:.5;font-size:1px;filter:alpha(opacity=50);}.olHandlerBoxSelectFeature{border:2px solid blue;position:absolute;background-color:#FFF;opacity:.5;font-size:1px;filter:alpha(opacity=50);}.olControlPanPanel{top:10px;left:5px;}.olControlPanPanel div{background-image:url(img/pan-panel.png);height:18px;width:18px;cursor:pointer;position:absolute;}.olControlPanPanel .olControlPanNorthItemInactive{top:0;left:9px;background-position:0 0;}.olControlPanPanel .olControlPanSouthItemInactive{top:36px;left:9px;background-position:18px 0;}.olControlPanPanel .olControlPanWestItemInactive{position:absolute;top:18px;left:0;background-position:0 18px;}.olControlPanPanel .olControlPanEastItemInactive{top:18px;left:18px;background-position:18px 18px;}.olControlZoomPanel{top:71px;left:14px;}.olControlZoomPanel div{background-image:url(img/zoom-panel.png);position:absolute;height:18px;width:18px;cursor:pointer;}.olControlZoomPanel .olControlZoomInItemInactive{top:0;left:0;background-position:0 0;}.olControlZoomPanel .olControlZoomToMaxExtentItemInactive{top:18px;left:0;background-position:0 -18px;}.olControlZoomPanel .olControlZoomOutItemInactive{top:36px;left:0;background-position:0 18px;}.olControlPanZoomBar div{font-size:1px;}.olPopupCloseBox{background:url(img/close.gif) no-repeat;cursor:pointer;}.olImageLoadError{background-color:#FFC0CB;opacity:.5;filter:alpha(opacity=50);}.olCursorWait{cursor:wait;}.olDrawBox{cursor:crosshair;}.olControlDragFeatureActive.olControlDragFeatureOver.olDragDown{cursor:0;}.olControlLayerSwitcher{position:absolute;top:25px;right:0;width:20em;font-family:sans-serif;font-weight:700;margin-top:3px;margin-left:3px;margin-bottom:3px;font-size:smaller;color:#FFF;background-color:transparent;}.olControlLayerSwitcher .layersDiv{background-color:#00008B;padding:5px 10px;}.olControlLayerSwitcher .layersDiv .baseLbl,.olControlLayerSwitcher .layersDiv .dataLbl{margin-top:3px;margin-left:3px;margin-bottom:3px;}.olControlLayerSwitcher .layersDiv .baseLayersDiv,.olControlLayerSwitcher .layersDiv .dataLayersDiv{padding-left:10px;}.olControlLayerSwitcher .maximizeDiv,.olControlLayerSwitcher .minimizeDiv{width:18px;height:18px;top:5px;right:0;cursor:pointer;}.olBingAttribution{color:#DDD;}span.olGoogleAttribution a{color:#77C;}.olControlNavToolbar,.olControlEditingToolbar{margin:5px 5px 0 0;}.olControlNavToolbar div,.olControlEditingToolbar div{background-image:url(img/editing_tool_bar.png);background-repeat:no-repeat;width:24px;height:22px;cursor:pointer;margin:0 0 5px 5px;}.olControlEditingToolbar{right:0;top:0;}.olControlNavToolbar{top:295px;left:9px;}.olControlEditingToolbar div{float:right;}.olControlNavToolbar .olControlNavigationItemInactive,.olControlEditingToolbar .olControlNavigationItemInactive{background-position:-103px -1px;}.olControlNavToolbar .olControlNavigationItemActive,.olControlEditingToolbar .olControlNavigationItemActive{background-position:-103px -24px;}.olControlNavToolbar .olControlZoomBoxItemInactive{background-position:-128px -1px;}.olControlNavToolbar .olControlZoomBoxItemActive{background-position:-128px -24px;}.olControlEditingToolbar .olControlDrawFeaturePointItemInactive{background-position:-77px -1px;}.olControlEditingToolbar .olControlDrawFeaturePointItemActive{background-position:-77px -24px;}.olControlEditingToolbar .olControlDrawFeaturePathItemInactive{background-position:-51px -1px;}.olControlEditingToolbar .olControlDrawFeaturePathItemActive{background-position:-51px -24px;}.olControlEditingToolbar .olControlDrawFeaturePolygonItemInactive{background-position:-26px -1px;}.olControlEditingToolbar .olControlDrawFeaturePolygonItemActive{background-position:-26px -24px;}div.olControlZoom{position:absolute;top:8px;left:8px;background:rgba(255,255,255,0.4);border-radius:4px;padding:2px;}div.olControlZoom a{display:block;color:#FFF;font-size:18px;font-family:'Lucida Grande', Verdana, Geneva, Lucida, Arial, Helvetica, sans-serif;font-weight:700;text-decoration:none;text-align:center;height:22px;width:22px;line-height:19px;background:rgba(0,60,136,0.5);filter:alpha(opacity=80);margin:1px;padding:0;}div.olControlZoom a:hover{background:rgba(0,60,136,0.7);filter:alpha(opacity=100);}a.olControlZoomIn{border-radius:4px 4px 0 0;}a.olControlZoomOut{border-radius:0 0 4px 4px;}.olLayerGrid .olTileImage{-webkit-transition:opacity .2s linear;-moz-transition:opacity .2s linear;-o-transition:opacity .2s linear;transition:opacity .2s linear;}div.olLayerDiv,.olControlNoSelect{-khtml-user-select:none;-moz-user-select:none;}.olPopupContent,.olFramedCloudPopupContent{overflow:auto;padding:5px;}.olDragDown,.olControlDragFeatureOver{cursor:move;}.olBingAttribution.road,.olGoogleAttribution{color:#333;}.olGoogleAttribution.hybrid,.olGoogleAttribution.satellite,span.olGoogleAttribution.hybrid a,span.olGoogleAttribution.satellite a{color:#EEE;}@media only screen and max-width 600px{div.olControlZoom a:hover{background:rgba(0,60,136,0.5);}}
\ No newline at end of file
diff --git a/src/main/webapp/css/toolbar.css b/src/main/webapp/css/toolbar.css
deleted file mode 100644
index ea2d1f0..0000000
--- a/src/main/webapp/css/toolbar.css
+++ /dev/null
@@ -1,42 +0,0 @@
-.olControlPortalToolbar div {
- float: left;
- background-repeat: no-repeat;
- margin: 2px;
- width: 20px;
- height: 20px;
- background-position: center;
- cursor: pointer;
- -moz-border-radius: 5px;
- border-radius: 5px;
-}
-
-.olControlPortalToolbarAddItemActive, .olControlPortalToolbarAddItemInactive {
- background-image: url("images/silk/add.png");
- background-color: #555555;
- border-color: #dddddd;
-}
-
-.olControlPortalToolbarEditItemActive, .olControlPortalToolbarEditItemInactive {
- background-image: url("images/silk/pencil.png");
-}
-
-.olControlPortalToolbarGetItemActive, .olControlPortalToolbarGetItemInactive {
- background-image: url("images/silk/down.png");
-}
-
-.olControlPortalToolbarAddItemInactive, .olControlPortalToolbarEditItemInactive, .olControlPortalToolbarGetItemInactive {
- background-color: #445566;
- border: 1px solid #444444;
- display: ;
-}
-
-.olControlPortalToolbarAddItemActive, .olControlPortalToolbarEditItemActive, .olControlPortalToolbarGetItemActive {
- background-color: #3388cc;
- border: 1px solid #26b3f7;
- display: ;
-}
-
-.olControlPortalToolbarGetItemHidden {
- background-color: #ff0000;
- display: none;
-}
diff --git a/src/main/webapp/images/Printer.png b/src/main/webapp/images/Printer.png
deleted file mode 100644
index c6250ee..0000000
Binary files a/src/main/webapp/images/Printer.png and /dev/null differ
diff --git a/src/main/webapp/images/ajax-loader.gif b/src/main/webapp/images/ajax-loader.gif
deleted file mode 100644
index c69e937..0000000
Binary files a/src/main/webapp/images/ajax-loader.gif and /dev/null differ
diff --git a/src/main/webapp/images/pdficon_small.gif b/src/main/webapp/images/pdficon_small.gif
deleted file mode 100644
index bb5edca..0000000
Binary files a/src/main/webapp/images/pdficon_small.gif and /dev/null differ
diff --git a/src/main/webapp/WEB-INF/jsp/messages.jsp b/src/main/webapp/index.html
similarity index 50%
rename from src/main/webapp/WEB-INF/jsp/messages.jsp
rename to src/main/webapp/index.html
index f781f2e..c9ca63a 100644
--- a/src/main/webapp/WEB-INF/jsp/messages.jsp
+++ b/src/main/webapp/index.html
@@ -1,4 +1,5 @@
-<%--
+
+
+
+
+
+
+
+
+
+#foreach( $styleSheet in $styleSheets )
+
+#end
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp
deleted file mode 100644
index f3bc151..0000000
--- a/src/main/webapp/index.jsp
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/main/webapp/index_wikimapia.html b/src/main/webapp/index_wikimapia.html
deleted file mode 100644
index d0da2fb..0000000
--- a/src/main/webapp/index_wikimapia.html
+++ /dev/null
@@ -1,153 +0,0 @@
-
-
-
-
-
- Paraguay National Forest Monitoring Portal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Capas
- Capas Seleccionadas
-
-
-
- Legend
-
-
-
-
-
-
-
-
-
-
-
- Please click on the map to post feedback
- Please click on the map to post feedback
-
-
-
-
-
diff --git a/src/main/webapp/js/OpenLayers.unredd.js b/src/main/webapp/js/OpenLayers.unredd.js
deleted file mode 100644
index f1961ab..0000000
--- a/src/main/webapp/js/OpenLayers.unredd.js
+++ /dev/null
@@ -1,37176 +0,0 @@
-/*
-
- OpenLayers.js -- OpenLayers Map Viewer Library
-
- Copyright (c) 2006-2012 by OpenLayers Contributors
- Published under the 2-clause BSD license.
- See http://openlayers.org/dev/license.txt for the full text of the license, and http://openlayers.org/dev/authors.txt for full list of contributors.
-
- Includes compressed code under the following licenses:
-
- (For uncompressed versions of the code used, please see the
- OpenLayers Github repository: )
-
-*/
-
-/**
- * Contains XMLHttpRequest.js
- * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- */
-
-/**
- * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
- * Copyright (c) 2006, Yahoo! Inc.
- * All rights reserved.
- *
- * Redistribution and use of this software in source and binary forms, with or
- * without modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * * Neither the name of Yahoo! Inc. nor the names of its contributors may be
- * used to endorse or promote products derived from this software without
- * specific prior written permission of Yahoo! Inc.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-/* ======================================================================
- OpenLayers/SingleFile.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-var OpenLayers = {
- /**
- * Constant: VERSION_NUMBER
- */
- VERSION_NUMBER: "Release 2.12",
-
- /**
- * Constant: singleFile
- * TODO: remove this in 3.0 when we stop supporting build profiles that
- * include OpenLayers.js
- */
- singleFile: true,
-
- /**
- * Method: _getScriptLocation
- * Return the path to this script. This is also implemented in
- * OpenLayers.js
- *
- * Returns:
- * {String} Path to this script
- */
- _getScriptLocation: (function() {
- var r = new RegExp("(^|(.*?\\/))(OpenLayers[^\\/]*?\\.js)(\\?|$)"),
- s = document.getElementsByTagName('script'),
- src, m, l = "";
- for(var i=0, len=s.length; i
- *
- * (end code)
- *
- * Please remember that when your OpenLayers script is not named
- * "OpenLayers.js" you will have to make sure that the default theme is
- * loaded into the page by including an appropriate -tag,
- * e.g.:
- *
- * (code)
- *
- * (end code)
- */
- ImgPath : ''
-};
-/* ======================================================================
- OpenLayers/BaseTypes/Class.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/SingleFile.js
- */
-
-/**
- * Constructor: OpenLayers.Class
- * Base class used to construct all other classes. Includes support for
- * multiple inheritance.
- *
- * This constructor is new in OpenLayers 2.5. At OpenLayers 3.0, the old
- * syntax for creating classes and dealing with inheritance
- * will be removed.
- *
- * To create a new OpenLayers-style class, use the following syntax:
- * (code)
- * var MyClass = OpenLayers.Class(prototype);
- * (end)
- *
- * To create a new OpenLayers-style class with multiple inheritance, use the
- * following syntax:
- * (code)
- * var MyClass = OpenLayers.Class(Class1, Class2, prototype);
- * (end)
- *
- * Note that instanceof reflection will only reveal Class1 as superclass.
- *
- */
-OpenLayers.Class = function() {
- var len = arguments.length;
- var P = arguments[0];
- var F = arguments[len-1];
-
- var C = typeof F.initialize == "function" ?
- F.initialize :
- function(){ P.prototype.initialize.apply(this, arguments); };
-
- if (len > 1) {
- var newArgs = [C, P].concat(
- Array.prototype.slice.call(arguments).slice(1, len-1), F);
- OpenLayers.inherit.apply(null, newArgs);
- } else {
- C.prototype = F;
- }
- return C;
-};
-
-/**
- * Function: OpenLayers.inherit
- *
- * Parameters:
- * C - {Object} the class that inherits
- * P - {Object} the superclass to inherit from
- *
- * In addition to the mandatory C and P parameters, an arbitrary number of
- * objects can be passed, which will extend C.
- */
-OpenLayers.inherit = function(C, P) {
- var F = function() {};
- F.prototype = P.prototype;
- C.prototype = new F;
- var i, l, o;
- for(i=2, l=arguments.length; i replacement = context[a];
- // 1 -> replacement = context[a][b];
- // 2 -> replacement = context[a][b][c];
- var subs = match.split(/\.+/);
- for (var i=0; i< subs.length; i++) {
- if (i == 0) {
- replacement = context;
- }
-
- replacement = replacement[subs[i]];
- }
-
- if(typeof replacement == "function") {
- replacement = args ?
- replacement.apply(null, args) :
- replacement();
- }
-
- // If replacement is undefined, return the string 'undefined'.
- // This is a workaround for a bugs in browsers not properly
- // dealing with non-participating groups in regular expressions:
- // http://blog.stevenlevithan.com/archives/npcg-javascript
- if (typeof replacement == 'undefined') {
- return 'undefined';
- } else {
- return replacement;
- }
- };
-
- return template.replace(OpenLayers.String.tokenRegEx, replacer);
- },
-
- /**
- * Property: tokenRegEx
- * Used to find tokens in a string.
- * Examples: ${a}, ${a.b.c}, ${a-b}, ${5}
- */
- tokenRegEx: /\$\{([\w.]+?)\}/g,
-
- /**
- * Property: numberRegEx
- * Used to test strings as numbers.
- */
- numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,
-
- /**
- * APIFunction: isNumeric
- * Determine whether a string contains only a numeric value.
- *
- * Examples:
- * (code)
- * OpenLayers.String.isNumeric("6.02e23") // true
- * OpenLayers.String.isNumeric("12 dozen") // false
- * OpenLayers.String.isNumeric("4") // true
- * OpenLayers.String.isNumeric(" 4 ") // false
- * (end)
- *
- * Returns:
- * {Boolean} String contains only a number.
- */
- isNumeric: function(value) {
- return OpenLayers.String.numberRegEx.test(value);
- },
-
- /**
- * APIFunction: numericIf
- * Converts a string that appears to be a numeric value into a number.
- *
- * Parameters:
- * value - {String}
- *
- * Returns:
- * {Number|String} a Number if the passed value is a number, a String
- * otherwise.
- */
- numericIf: function(value) {
- return OpenLayers.String.isNumeric(value) ? parseFloat(value) : value;
- }
-
-};
-
-/**
- * Namespace: OpenLayers.Number
- * Contains convenience functions for manipulating numbers.
- */
-OpenLayers.Number = {
-
- /**
- * Property: decimalSeparator
- * Decimal separator to use when formatting numbers.
- */
- decimalSeparator: ".",
-
- /**
- * Property: thousandsSeparator
- * Thousands separator to use when formatting numbers.
- */
- thousandsSeparator: ",",
-
- /**
- * APIFunction: limitSigDigs
- * Limit the number of significant digits on a float.
- *
- * Parameters:
- * num - {Float}
- * sig - {Integer}
- *
- * Returns:
- * {Float} The number, rounded to the specified number of significant
- * digits.
- */
- limitSigDigs: function(num, sig) {
- var fig = 0;
- if (sig > 0) {
- fig = parseFloat(num.toPrecision(sig));
- }
- return fig;
- },
-
- /**
- * APIFunction: format
- * Formats a number for output.
- *
- * Parameters:
- * num - {Float}
- * dec - {Integer} Number of decimal places to round to.
- * Defaults to 0. Set to null to leave decimal places unchanged.
- * tsep - {String} Thousands separator.
- * Default is ",".
- * dsep - {String} Decimal separator.
- * Default is ".".
- *
- * Returns:
- * {String} A string representing the formatted number.
- */
- format: function(num, dec, tsep, dsep) {
- dec = (typeof dec != "undefined") ? dec : 0;
- tsep = (typeof tsep != "undefined") ? tsep :
- OpenLayers.Number.thousandsSeparator;
- dsep = (typeof dsep != "undefined") ? dsep :
- OpenLayers.Number.decimalSeparator;
-
- if (dec != null) {
- num = parseFloat(num.toFixed(dec));
- }
-
- var parts = num.toString().split(".");
- if (parts.length == 1 && dec == null) {
- // integer where we do not want to touch the decimals
- dec = 0;
- }
-
- var integer = parts[0];
- if (tsep) {
- var thousands = /(-?[0-9]+)([0-9]{3})/;
- while(thousands.test(integer)) {
- integer = integer.replace(thousands, "$1" + tsep + "$2");
- }
- }
-
- var str;
- if (dec == 0) {
- str = integer;
- } else {
- var rem = parts.length > 1 ? parts[1] : "0";
- if (dec != null) {
- rem = rem + new Array(dec - rem.length + 1).join("0");
- }
- str = integer + dsep + rem;
- }
- return str;
- }
-};
-
-/**
- * Namespace: OpenLayers.Function
- * Contains convenience functions for function manipulation.
- */
-OpenLayers.Function = {
- /**
- * APIFunction: bind
- * Bind a function to an object. Method to easily create closures with
- * 'this' altered.
- *
- * Parameters:
- * func - {Function} Input function.
- * object - {Object} The object to bind to the input function (as this).
- *
- * Returns:
- * {Function} A closure with 'this' set to the passed in object.
- */
- bind: function(func, object) {
- // create a reference to all arguments past the second one
- var args = Array.prototype.slice.apply(arguments, [2]);
- return function() {
- // Push on any additional arguments from the actual function call.
- // These will come after those sent to the bind call.
- var newArgs = args.concat(
- Array.prototype.slice.apply(arguments, [0])
- );
- return func.apply(object, newArgs);
- };
- },
-
- /**
- * APIFunction: bindAsEventListener
- * Bind a function to an object, and configure it to receive the event
- * object as first parameter when called.
- *
- * Parameters:
- * func - {Function} Input function to serve as an event listener.
- * object - {Object} A reference to this.
- *
- * Returns:
- * {Function}
- */
- bindAsEventListener: function(func, object) {
- return function(event) {
- return func.call(object, event || window.event);
- };
- },
-
- /**
- * APIFunction: False
- * A simple function to that just does "return false". We use this to
- * avoid attaching anonymous functions to DOM event handlers, which
- * causes "issues" on IE<8.
- *
- * Usage:
- * document.onclick = OpenLayers.Function.False;
- *
- * Returns:
- * {Boolean}
- */
- False : function() {
- return false;
- },
-
- /**
- * APIFunction: True
- * A simple function to that just does "return true". We use this to
- * avoid attaching anonymous functions to DOM event handlers, which
- * causes "issues" on IE<8.
- *
- * Usage:
- * document.onclick = OpenLayers.Function.True;
- *
- * Returns:
- * {Boolean}
- */
- True : function() {
- return true;
- },
-
- /**
- * APIFunction: Void
- * A reusable function that returns ``undefined``.
- *
- * Returns:
- * {undefined}
- */
- Void: function() {}
-
-};
-
-/**
- * Namespace: OpenLayers.Array
- * Contains convenience functions for array manipulation.
- */
-OpenLayers.Array = {
-
- /**
- * APIMethod: filter
- * Filter an array. Provides the functionality of the
- * Array.prototype.filter extension to the ECMA-262 standard. Where
- * available, Array.prototype.filter will be used.
- *
- * Based on well known example from http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/filter
- *
- * Parameters:
- * array - {Array} The array to be filtered. This array is not mutated.
- * Elements added to this array by the callback will not be visited.
- * callback - {Function} A function that is called for each element in
- * the array. If this function returns true, the element will be
- * included in the return. The function will be called with three
- * arguments: the element in the array, the index of that element, and
- * the array itself. If the optional caller parameter is specified
- * the callback will be called with this set to caller.
- * caller - {Object} Optional object to be set as this when the callback
- * is called.
- *
- * Returns:
- * {Array} An array of elements from the passed in array for which the
- * callback returns true.
- */
- filter: function(array, callback, caller) {
- var selected = [];
- if (Array.prototype.filter) {
- selected = array.filter(callback, caller);
- } else {
- var len = array.length;
- if (typeof callback != "function") {
- throw new TypeError();
- }
- for(var i=0; i} A cached center location. This should not be
- * accessed directly. Use instead.
- */
- centerLonLat: null,
-
- /**
- * Constructor: OpenLayers.Bounds
- * Construct a new bounds object. Coordinates can either be passed as four
- * arguments, or as a single argument.
- *
- * Parameters (four arguments):
- * left - {Number} The left bounds of the box. Note that for width
- * calculations, this is assumed to be less than the right value.
- * bottom - {Number} The bottom bounds of the box. Note that for height
- * calculations, this is assumed to be more than the top value.
- * right - {Number} The right bounds.
- * top - {Number} The top bounds.
- *
- * Parameters (single argument):
- * bounds - {Array(Number)} [left, bottom, right, top]
- */
- initialize: function(left, bottom, right, top) {
- if (OpenLayers.Util.isArray(left)) {
- top = left[3];
- right = left[2];
- bottom = left[1];
- left = left[0];
- }
- if (left != null) {
- this.left = OpenLayers.Util.toFloat(left);
- }
- if (bottom != null) {
- this.bottom = OpenLayers.Util.toFloat(bottom);
- }
- if (right != null) {
- this.right = OpenLayers.Util.toFloat(right);
- }
- if (top != null) {
- this.top = OpenLayers.Util.toFloat(top);
- }
- },
-
- /**
- * Method: clone
- * Create a cloned instance of this bounds.
- *
- * Returns:
- * {} A fresh copy of the bounds
- */
- clone:function() {
- return new OpenLayers.Bounds(this.left, this.bottom,
- this.right, this.top);
- },
-
- /**
- * Method: equals
- * Test a two bounds for equivalence.
- *
- * Parameters:
- * bounds - {}
- *
- * Returns:
- * {Boolean} The passed-in bounds object has the same left,
- * right, top, bottom components as this. Note that if bounds
- * passed in is null, returns false.
- */
- equals:function(bounds) {
- var equals = false;
- if (bounds != null) {
- equals = ((this.left == bounds.left) &&
- (this.right == bounds.right) &&
- (this.top == bounds.top) &&
- (this.bottom == bounds.bottom));
- }
- return equals;
- },
-
- /**
- * APIMethod: toString
- *
- * Returns:
- * {String} String representation of bounds object.
- */
- toString:function() {
- return [this.left, this.bottom, this.right, this.top].join(",");
- },
-
- /**
- * APIMethod: toArray
- *
- * Parameters:
- * reverseAxisOrder - {Boolean} Should we reverse the axis order?
- *
- * Returns:
- * {Array} array of left, bottom, right, top
- */
- toArray: function(reverseAxisOrder) {
- if (reverseAxisOrder === true) {
- return [this.bottom, this.left, this.top, this.right];
- } else {
- return [this.left, this.bottom, this.right, this.top];
- }
- },
-
- /**
- * APIMethod: toBBOX
- *
- * Parameters:
- * decimal - {Integer} How many significant digits in the bbox coords?
- * Default is 6
- * reverseAxisOrder - {Boolean} Should we reverse the axis order?
- *
- * Returns:
- * {String} Simple String representation of bounds object.
- * (e.g. "5,42,10,45" )
- */
- toBBOX:function(decimal, reverseAxisOrder) {
- if (decimal== null) {
- decimal = 6;
- }
- var mult = Math.pow(10, decimal);
- var xmin = Math.round(this.left * mult) / mult;
- var ymin = Math.round(this.bottom * mult) / mult;
- var xmax = Math.round(this.right * mult) / mult;
- var ymax = Math.round(this.top * mult) / mult;
- if (reverseAxisOrder === true) {
- return ymin + "," + xmin + "," + ymax + "," + xmax;
- } else {
- return xmin + "," + ymin + "," + xmax + "," + ymax;
- }
- },
-
- /**
- * APIMethod: toGeometry
- * Create a new polygon geometry based on this bounds.
- *
- * Returns:
- * {} A new polygon with the coordinates
- * of this bounds.
- */
- toGeometry: function() {
- return new OpenLayers.Geometry.Polygon([
- new OpenLayers.Geometry.LinearRing([
- new OpenLayers.Geometry.Point(this.left, this.bottom),
- new OpenLayers.Geometry.Point(this.right, this.bottom),
- new OpenLayers.Geometry.Point(this.right, this.top),
- new OpenLayers.Geometry.Point(this.left, this.top)
- ])
- ]);
- },
-
- /**
- * APIMethod: getWidth
- *
- * Returns:
- * {Float} The width of the bounds
- */
- getWidth:function() {
- return (this.right - this.left);
- },
-
- /**
- * APIMethod: getHeight
- *
- * Returns:
- * {Float} The height of the bounds (top minus bottom).
- */
- getHeight:function() {
- return (this.top - this.bottom);
- },
-
- /**
- * APIMethod: getSize
- *
- * Returns:
- * {} The size of the box.
- */
- getSize:function() {
- return new OpenLayers.Size(this.getWidth(), this.getHeight());
- },
-
- /**
- * APIMethod: getCenterPixel
- *
- * Returns:
- * {} The center of the bounds in pixel space.
- */
- getCenterPixel:function() {
- return new OpenLayers.Pixel( (this.left + this.right) / 2,
- (this.bottom + this.top) / 2);
- },
-
- /**
- * APIMethod: getCenterLonLat
- *
- * Returns:
- * {} The center of the bounds in map space.
- */
- getCenterLonLat:function() {
- if(!this.centerLonLat) {
- this.centerLonLat = new OpenLayers.LonLat(
- (this.left + this.right) / 2, (this.bottom + this.top) / 2
- );
- }
- return this.centerLonLat;
- },
-
- /**
- * APIMethod: scale
- * Scales the bounds around a pixel or lonlat. Note that the new
- * bounds may return non-integer properties, even if a pixel
- * is passed.
- *
- * Parameters:
- * ratio - {Float}
- * origin - { or }
- * Default is center.
- *
- * Returns:
- * {} A new bounds that is scaled by ratio
- * from origin.
- */
- scale: function(ratio, origin){
- if(origin == null){
- origin = this.getCenterLonLat();
- }
-
- var origx,origy;
-
- // get origin coordinates
- if(origin.CLASS_NAME == "OpenLayers.LonLat"){
- origx = origin.lon;
- origy = origin.lat;
- } else {
- origx = origin.x;
- origy = origin.y;
- }
-
- var left = (this.left - origx) * ratio + origx;
- var bottom = (this.bottom - origy) * ratio + origy;
- var right = (this.right - origx) * ratio + origx;
- var top = (this.top - origy) * ratio + origy;
-
- return new OpenLayers.Bounds(left, bottom, right, top);
- },
-
- /**
- * APIMethod: add
- *
- * Parameters:
- * x - {Float}
- * y - {Float}
- *
- * Returns:
- * {} A new bounds whose coordinates are the same as
- * this, but shifted by the passed-in x and y values.
- */
- add:function(x, y) {
- if ( (x == null) || (y == null) ) {
- throw new TypeError('Bounds.add cannot receive null values');
- }
- return new OpenLayers.Bounds(this.left + x, this.bottom + y,
- this.right + x, this.top + y);
- },
-
- /**
- * APIMethod: extend
- * Extend the bounds to include the point, lonlat, or bounds specified.
- * Note, this function assumes that left < right and bottom < top.
- *
- * Parameters:
- * object - {Object} Can be LonLat, Point, or Bounds
- */
- extend:function(object) {
- var bounds = null;
- if (object) {
- // clear cached center location
- switch(object.CLASS_NAME) {
- case "OpenLayers.LonLat":
- bounds = new OpenLayers.Bounds(object.lon, object.lat,
- object.lon, object.lat);
- break;
- case "OpenLayers.Geometry.Point":
- bounds = new OpenLayers.Bounds(object.x, object.y,
- object.x, object.y);
- break;
-
- case "OpenLayers.Bounds":
- bounds = object;
- break;
- }
-
- if (bounds) {
- this.centerLonLat = null;
- if ( (this.left == null) || (bounds.left < this.left)) {
- this.left = bounds.left;
- }
- if ( (this.bottom == null) || (bounds.bottom < this.bottom) ) {
- this.bottom = bounds.bottom;
- }
- if ( (this.right == null) || (bounds.right > this.right) ) {
- this.right = bounds.right;
- }
- if ( (this.top == null) || (bounds.top > this.top) ) {
- this.top = bounds.top;
- }
- }
- }
- },
-
- /**
- * APIMethod: containsLonLat
- *
- * Parameters:
- * ll - {|Object} OpenLayers.LonLat or an
- * object with a 'lon' and 'lat' properties.
- * options - {Object} Optional parameters
- *
- * Acceptable options:
- * inclusive - {Boolean} Whether or not to include the border.
- * Default is true.
- * worldBounds - {} If a worldBounds is provided, the
- * ll will be considered as contained if it exceeds the world bounds,
- * but can be wrapped around the dateline so it is contained by this
- * bounds.
- *
- * Returns:
- * {Boolean} The passed-in lonlat is within this bounds.
- */
- containsLonLat: function(ll, options) {
- if (typeof options === "boolean") {
- options = {inclusive: options};
- }
- options = options || {};
- var contains = this.contains(ll.lon, ll.lat, options.inclusive),
- worldBounds = options.worldBounds;
- if (worldBounds && !contains) {
- var worldWidth = worldBounds.getWidth();
- var worldCenterX = (worldBounds.left + worldBounds.right) / 2;
- var worldsAway = Math.round((ll.lon - worldCenterX) / worldWidth);
- contains = this.containsLonLat({
- lon: ll.lon - worldsAway * worldWidth,
- lat: ll.lat
- }, {inclusive: options.inclusive});
- }
- return contains;
- },
-
- /**
- * APIMethod: containsPixel
- *
- * Parameters:
- * px - {}
- * inclusive - {Boolean} Whether or not to include the border. Default is
- * true.
- *
- * Returns:
- * {Boolean} The passed-in pixel is within this bounds.
- */
- containsPixel:function(px, inclusive) {
- return this.contains(px.x, px.y, inclusive);
- },
-
- /**
- * APIMethod: contains
- *
- * Parameters:
- * x - {Float}
- * y - {Float}
- * inclusive - {Boolean} Whether or not to include the border. Default is
- * true.
- *
- * Returns:
- * {Boolean} Whether or not the passed-in coordinates are within this
- * bounds.
- */
- contains:function(x, y, inclusive) {
- //set default
- if (inclusive == null) {
- inclusive = true;
- }
-
- if (x == null || y == null) {
- return false;
- }
-
- x = OpenLayers.Util.toFloat(x);
- y = OpenLayers.Util.toFloat(y);
-
- var contains = false;
- if (inclusive) {
- contains = ((x >= this.left) && (x <= this.right) &&
- (y >= this.bottom) && (y <= this.top));
- } else {
- contains = ((x > this.left) && (x < this.right) &&
- (y > this.bottom) && (y < this.top));
- }
- return contains;
- },
-
- /**
- * APIMethod: intersectsBounds
- * Determine whether the target bounds intersects this bounds. Bounds are
- * considered intersecting if any of their edges intersect or if one
- * bounds contains the other.
- *
- * Parameters:
- * bounds - {} The target bounds.
- * options - {Object} Optional parameters.
- *
- * Acceptable options:
- * inclusive - {Boolean} Treat coincident borders as intersecting. Default
- * is true. If false, bounds that do not overlap but only touch at the
- * border will not be considered as intersecting.
- * worldBounds - {} If a worldBounds is provided, two
- * bounds will be considered as intersecting if they intersect when
- * shifted to within the world bounds. This applies only to bounds that
- * cross or are completely outside the world bounds.
- *
- * Returns:
- * {Boolean} The passed-in bounds object intersects this bounds.
- */
- intersectsBounds:function(bounds, options) {
- if (typeof options === "boolean") {
- options = {inclusive: options};
- }
- options = options || {};
- if (options.worldBounds) {
- var self = this.wrapDateLine(options.worldBounds);
- bounds = bounds.wrapDateLine(options.worldBounds);
- } else {
- self = this;
- }
- if (options.inclusive == null) {
- options.inclusive = true;
- }
- var intersects = false;
- var mightTouch = (
- self.left == bounds.right ||
- self.right == bounds.left ||
- self.top == bounds.bottom ||
- self.bottom == bounds.top
- );
-
- // if the two bounds only touch at an edge, and inclusive is false,
- // then the bounds don't *really* intersect.
- if (options.inclusive || !mightTouch) {
- // otherwise, if one of the boundaries even partially contains another,
- // inclusive of the edges, then they do intersect.
- var inBottom = (
- ((bounds.bottom >= self.bottom) && (bounds.bottom <= self.top)) ||
- ((self.bottom >= bounds.bottom) && (self.bottom <= bounds.top))
- );
- var inTop = (
- ((bounds.top >= self.bottom) && (bounds.top <= self.top)) ||
- ((self.top > bounds.bottom) && (self.top < bounds.top))
- );
- var inLeft = (
- ((bounds.left >= self.left) && (bounds.left <= self.right)) ||
- ((self.left >= bounds.left) && (self.left <= bounds.right))
- );
- var inRight = (
- ((bounds.right >= self.left) && (bounds.right <= self.right)) ||
- ((self.right >= bounds.left) && (self.right <= bounds.right))
- );
- intersects = ((inBottom || inTop) && (inLeft || inRight));
- }
- // document me
- if (options.worldBounds && !intersects) {
- var world = options.worldBounds;
- var width = world.getWidth();
- var selfCrosses = !world.containsBounds(self);
- var boundsCrosses = !world.containsBounds(bounds);
- if (selfCrosses && !boundsCrosses) {
- bounds = bounds.add(-width, 0);
- intersects = self.intersectsBounds(bounds, {inclusive: options.inclusive});
- } else if (boundsCrosses && !selfCrosses) {
- self = self.add(-width, 0);
- intersects = bounds.intersectsBounds(self, {inclusive: options.inclusive});
- }
- }
- return intersects;
- },
-
- /**
- * APIMethod: containsBounds
- * Determine whether the target bounds is contained within this bounds.
- *
- * bounds - {} The target bounds.
- * partial - {Boolean} If any of the target corners is within this bounds
- * consider the bounds contained. Default is false. If false, the
- * entire target bounds must be contained within this bounds.
- * inclusive - {Boolean} Treat shared edges as contained. Default is
- * true.
- *
- * Returns:
- * {Boolean} The passed-in bounds object is contained within this bounds.
- */
- containsBounds:function(bounds, partial, inclusive) {
- if (partial == null) {
- partial = false;
- }
- if (inclusive == null) {
- inclusive = true;
- }
- var bottomLeft = this.contains(bounds.left, bounds.bottom, inclusive);
- var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
- var topLeft = this.contains(bounds.left, bounds.top, inclusive);
- var topRight = this.contains(bounds.right, bounds.top, inclusive);
-
- return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
- : (bottomLeft && bottomRight && topLeft && topRight);
- },
-
- /**
- * APIMethod: determineQuadrant
- *
- * Parameters:
- * lonlat - {}
- *
- * Returns:
- * {String} The quadrant ("br" "tr" "tl" "bl") of the bounds in which the
- * coordinate lies.
- */
- determineQuadrant: function(lonlat) {
-
- var quadrant = "";
- var center = this.getCenterLonLat();
-
- quadrant += (lonlat.lat < center.lat) ? "b" : "t";
- quadrant += (lonlat.lon < center.lon) ? "l" : "r";
-
- return quadrant;
- },
-
- /**
- * APIMethod: transform
- * Transform the Bounds object from source to dest.
- *
- * Parameters:
- * source - {} Source projection.
- * dest - {} Destination projection.
- *
- * Returns:
- * {} Itself, for use in chaining operations.
- */
- transform: function(source, dest) {
- // clear cached center location
- this.centerLonLat = null;
- var ll = OpenLayers.Projection.transform(
- {'x': this.left, 'y': this.bottom}, source, dest);
- var lr = OpenLayers.Projection.transform(
- {'x': this.right, 'y': this.bottom}, source, dest);
- var ul = OpenLayers.Projection.transform(
- {'x': this.left, 'y': this.top}, source, dest);
- var ur = OpenLayers.Projection.transform(
- {'x': this.right, 'y': this.top}, source, dest);
- this.left = Math.min(ll.x, ul.x);
- this.bottom = Math.min(ll.y, lr.y);
- this.right = Math.max(lr.x, ur.x);
- this.top = Math.max(ul.y, ur.y);
- return this;
- },
-
- /**
- * APIMethod: wrapDateLine
- *
- * Parameters:
- * maxExtent - {}
- * options - {Object} Some possible options are:
- *
- * Allowed Options:
- * leftTolerance - {float} Allow for a margin of error
- * with the 'left' value of this
- * bound.
- * Default is 0.
- * rightTolerance - {float} Allow for a margin of error
- * with the 'right' value of
- * this bound.
- * Default is 0.
- *
- * Returns:
- * {} A copy of this bounds, but wrapped around the
- * "dateline" (as specified by the borders of
- * maxExtent). Note that this function only returns
- * a different bounds value if this bounds is
- * *entirely* outside of the maxExtent. If this
- * bounds straddles the dateline (is part in/part
- * out of maxExtent), the returned bounds will always
- * cross the left edge of the given maxExtent.
- *.
- */
- wrapDateLine: function(maxExtent, options) {
- options = options || {};
-
- var leftTolerance = options.leftTolerance || 0;
- var rightTolerance = options.rightTolerance || 0;
-
- var newBounds = this.clone();
-
- if (maxExtent) {
- var width = maxExtent.getWidth();
-
- //shift right?
- while (newBounds.left < maxExtent.left &&
- newBounds.right - rightTolerance <= maxExtent.left ) {
- newBounds = newBounds.add(width, 0);
- }
-
- //shift left?
- while (newBounds.left + leftTolerance >= maxExtent.right &&
- newBounds.right > maxExtent.right ) {
- newBounds = newBounds.add(-width, 0);
- }
-
- // crosses right only? force left
- var newLeft = newBounds.left + leftTolerance;
- if (newLeft < maxExtent.right && newLeft > maxExtent.left &&
- newBounds.right - rightTolerance > maxExtent.right) {
- newBounds = newBounds.add(-width, 0);
- }
- }
-
- return newBounds;
- },
-
- CLASS_NAME: "OpenLayers.Bounds"
-});
-
-/**
- * APIFunction: fromString
- * Alternative constructor that builds a new OpenLayers.Bounds from a
- * parameter string
- *
- * Parameters:
- * str - {String}Comma-separated bounds string. (e.g. "5,42,10,45" )
- * reverseAxisOrder - {Boolean} Does the string use reverse axis order?
- *
- * Returns:
- * {} New bounds object built from the
- * passed-in String.
- */
-OpenLayers.Bounds.fromString = function(str, reverseAxisOrder) {
- var bounds = str.split(",");
- return OpenLayers.Bounds.fromArray(bounds, reverseAxisOrder);
-};
-
-/**
- * APIFunction: fromArray
- * Alternative constructor that builds a new OpenLayers.Bounds
- * from an array
- *
- * Parameters:
- * bbox - {Array(Float)} Array of bounds values (e.g. [5,42,10,45] )
- * reverseAxisOrder - {Boolean} Does the array use reverse axis order?
- *
- * Returns:
- * {} New bounds object built from the passed-in Array.
- */
-OpenLayers.Bounds.fromArray = function(bbox, reverseAxisOrder) {
- return reverseAxisOrder === true ?
- new OpenLayers.Bounds(bbox[1], bbox[0], bbox[3], bbox[2]) :
- new OpenLayers.Bounds(bbox[0], bbox[1], bbox[2], bbox[3]);
-};
-
-/**
- * APIFunction: fromSize
- * Alternative constructor that builds a new OpenLayers.Bounds
- * from a size
- *
- * Parameters:
- * size - {|Object} OpenLayers.Size or an object with
- * a 'w' and 'h' properties.
- *
- * Returns:
- * {} New bounds object built from the passed-in size.
- */
-OpenLayers.Bounds.fromSize = function(size) {
- return new OpenLayers.Bounds(0,
- size.h,
- size.w,
- 0);
-};
-
-/**
- * Function: oppositeQuadrant
- * Get the opposite quadrant for a given quadrant string.
- *
- * Parameters:
- * quadrant - {String} two character quadrant shortstring
- *
- * Returns:
- * {String} The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if
- * you pass in "bl" it returns "tr", if you pass in "br" it
- * returns "tl", etc.
- */
-OpenLayers.Bounds.oppositeQuadrant = function(quadrant) {
- var opp = "";
-
- opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
- opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
-
- return opp;
-};
-/* ======================================================================
- OpenLayers/BaseTypes/Element.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Util.js
- * @requires OpenLayers/BaseTypes.js
- */
-
-/**
- * Namespace: OpenLayers.Element
- */
-OpenLayers.Element = {
-
- /**
- * APIFunction: visible
- *
- * Parameters:
- * element - {DOMElement}
- *
- * Returns:
- * {Boolean} Is the element visible?
- */
- visible: function(element) {
- return OpenLayers.Util.getElement(element).style.display != 'none';
- },
-
- /**
- * APIFunction: toggle
- * Toggle the visibility of element(s) passed in
- *
- * Parameters:
- * element - {DOMElement} Actually user can pass any number of elements
- */
- toggle: function() {
- for (var i=0, len=arguments.length; i"lon=5,lat=42")
- */
- toString:function() {
- return ("lon=" + this.lon + ",lat=" + this.lat);
- },
-
- /**
- * APIMethod: toShortString
- *
- * Returns:
- * {String} Shortened String representation of OpenLayers.LonLat object.
- * (e.g. "5, 42" )
- */
- toShortString:function() {
- return (this.lon + ", " + this.lat);
- },
-
- /**
- * APIMethod: clone
- *
- * Returns:
- * {} New OpenLayers.LonLat object with the same lon
- * and lat values
- */
- clone:function() {
- return new OpenLayers.LonLat(this.lon, this.lat);
- },
-
- /**
- * APIMethod: add
- *
- * Parameters:
- * lon - {Float}
- * lat - {Float}
- *
- * Returns:
- * {} A new OpenLayers.LonLat object with the lon and
- * lat passed-in added to this's.
- */
- add:function(lon, lat) {
- if ( (lon == null) || (lat == null) ) {
- throw new TypeError('LonLat.add cannot receive null values');
- }
- return new OpenLayers.LonLat(this.lon + OpenLayers.Util.toFloat(lon),
- this.lat + OpenLayers.Util.toFloat(lat));
- },
-
- /**
- * APIMethod: equals
- *
- * Parameters:
- * ll - {}
- *
- * Returns:
- * {Boolean} Boolean value indicating whether the passed-in
- * object has the same lon and lat
- * components as this.
- * Note: if ll passed in is null, returns false
- */
- equals:function(ll) {
- var equals = false;
- if (ll != null) {
- equals = ((this.lon == ll.lon && this.lat == ll.lat) ||
- (isNaN(this.lon) && isNaN(this.lat) && isNaN(ll.lon) && isNaN(ll.lat)));
- }
- return equals;
- },
-
- /**
- * APIMethod: transform
- * Transform the LonLat object from source to dest. This transformation is
- * *in place*: if you want a *new* lonlat, use .clone() first.
- *
- * Parameters:
- * source - {} Source projection.
- * dest - {} Destination projection.
- *
- * Returns:
- * {} Itself, for use in chaining operations.
- */
- transform: function(source, dest) {
- var point = OpenLayers.Projection.transform(
- {'x': this.lon, 'y': this.lat}, source, dest);
- this.lon = point.x;
- this.lat = point.y;
- return this;
- },
-
- /**
- * APIMethod: wrapDateLine
- *
- * Parameters:
- * maxExtent - {}
- *
- * Returns:
- * {} A copy of this lonlat, but wrapped around the
- * "dateline" (as specified by the borders of
- * maxExtent)
- */
- wrapDateLine: function(maxExtent) {
-
- var newLonLat = this.clone();
-
- if (maxExtent) {
- //shift right?
- while (newLonLat.lon < maxExtent.left) {
- newLonLat.lon += maxExtent.getWidth();
- }
-
- //shift left?
- while (newLonLat.lon > maxExtent.right) {
- newLonLat.lon -= maxExtent.getWidth();
- }
- }
-
- return newLonLat;
- },
-
- CLASS_NAME: "OpenLayers.LonLat"
-});
-
-/**
- * Function: fromString
- * Alternative constructor that builds a new from a
- * parameter string
- *
- * Parameters:
- * str - {String} Comma-separated Lon,Lat coordinate string.
- * (e.g. "5,40" )
- *
- * Returns:
- * {} New object built from the
- * passed-in String.
- */
-OpenLayers.LonLat.fromString = function(str) {
- var pair = str.split(",");
- return new OpenLayers.LonLat(pair[0], pair[1]);
-};
-
-/**
- * Function: fromArray
- * Alternative constructor that builds a new from an
- * array of two numbers that represent lon- and lat-values.
- *
- * Parameters:
- * arr - {Array(Float)} Array of lon/lat values (e.g. [5,-42])
- *
- * Returns:
- * {} New object built from the
- * passed-in array.
- */
-OpenLayers.LonLat.fromArray = function(arr) {
- var gotArr = OpenLayers.Util.isArray(arr),
- lon = gotArr && arr[0],
- lat = gotArr && arr[1];
- return new OpenLayers.LonLat(lon, lat);
-};
-/* ======================================================================
- OpenLayers/BaseTypes/Pixel.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/BaseTypes/Class.js
- */
-
-/**
- * Class: OpenLayers.Pixel
- * This class represents a screen coordinate, in x and y coordinates
- */
-OpenLayers.Pixel = OpenLayers.Class({
-
- /**
- * APIProperty: x
- * {Number} The x coordinate
- */
- x: 0.0,
-
- /**
- * APIProperty: y
- * {Number} The y coordinate
- */
- y: 0.0,
-
- /**
- * Constructor: OpenLayers.Pixel
- * Create a new OpenLayers.Pixel instance
- *
- * Parameters:
- * x - {Number} The x coordinate
- * y - {Number} The y coordinate
- *
- * Returns:
- * An instance of OpenLayers.Pixel
- */
- initialize: function(x, y) {
- this.x = parseFloat(x);
- this.y = parseFloat(y);
- },
-
- /**
- * Method: toString
- * Cast this object into a string
- *
- * Returns:
- * {String} The string representation of Pixel. ex: "x=200.4,y=242.2"
- */
- toString:function() {
- return ("x=" + this.x + ",y=" + this.y);
- },
-
- /**
- * APIMethod: clone
- * Return a clone of this pixel object
- *
- * Returns:
- * {} A clone pixel
- */
- clone:function() {
- return new OpenLayers.Pixel(this.x, this.y);
- },
-
- /**
- * APIMethod: equals
- * Determine whether one pixel is equivalent to another
- *
- * Parameters:
- * px - {|Object} An OpenLayers.Pixel or an object with
- * a 'x' and 'y' properties.
- *
- * Returns:
- * {Boolean} The point passed in as parameter is equal to this. Note that
- * if px passed in is null, returns false.
- */
- equals:function(px) {
- var equals = false;
- if (px != null) {
- equals = ((this.x == px.x && this.y == px.y) ||
- (isNaN(this.x) && isNaN(this.y) && isNaN(px.x) && isNaN(px.y)));
- }
- return equals;
- },
-
- /**
- * APIMethod: distanceTo
- * Returns the distance to the pixel point passed in as a parameter.
- *
- * Parameters:
- * px - {}
- *
- * Returns:
- * {Float} The pixel point passed in as parameter to calculate the
- * distance to.
- */
- distanceTo:function(px) {
- return Math.sqrt(
- Math.pow(this.x - px.x, 2) +
- Math.pow(this.y - px.y, 2)
- );
- },
-
- /**
- * APIMethod: add
- *
- * Parameters:
- * x - {Integer}
- * y - {Integer}
- *
- * Returns:
- * {} A new Pixel with this pixel's x&y augmented by the
- * values passed in.
- */
- add:function(x, y) {
- if ( (x == null) || (y == null) ) {
- throw new TypeError('Pixel.add cannot receive null values');
- }
- return new OpenLayers.Pixel(this.x + x, this.y + y);
- },
-
- /**
- * APIMethod: offset
- *
- * Parameters
- * px - {|Object} An OpenLayers.Pixel or an object with
- * a 'x' and 'y' properties.
- *
- * Returns:
- * {} A new Pixel with this pixel's x&y augmented by the
- * x&y values of the pixel passed in.
- */
- offset:function(px) {
- var newPx = this.clone();
- if (px) {
- newPx = this.add(px.x, px.y);
- }
- return newPx;
- },
-
- CLASS_NAME: "OpenLayers.Pixel"
-});
-/* ======================================================================
- OpenLayers/BaseTypes/Size.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/BaseTypes/Class.js
- */
-
-/**
- * Class: OpenLayers.Size
- * Instances of this class represent a width/height pair
- */
-OpenLayers.Size = OpenLayers.Class({
-
- /**
- * APIProperty: w
- * {Number} width
- */
- w: 0.0,
-
- /**
- * APIProperty: h
- * {Number} height
- */
- h: 0.0,
-
-
- /**
- * Constructor: OpenLayers.Size
- * Create an instance of OpenLayers.Size
- *
- * Parameters:
- * w - {Number} width
- * h - {Number} height
- */
- initialize: function(w, h) {
- this.w = parseFloat(w);
- this.h = parseFloat(h);
- },
-
- /**
- * Method: toString
- * Return the string representation of a size object
- *
- * Returns:
- * {String} The string representation of OpenLayers.Size object.
- * (e.g. "w=55,h=66" )
- */
- toString:function() {
- return ("w=" + this.w + ",h=" + this.h);
- },
-
- /**
- * APIMethod: clone
- * Create a clone of this size object
- *
- * Returns:
- * {} A new OpenLayers.Size object with the same w and h
- * values
- */
- clone:function() {
- return new OpenLayers.Size(this.w, this.h);
- },
-
- /**
- *
- * APIMethod: equals
- * Determine where this size is equal to another
- *
- * Parameters:
- * sz - {|Object} An OpenLayers.Size or an object with
- * a 'w' and 'h' properties.
- *
- * Returns:
- * {Boolean} The passed in size has the same h and w properties as this one.
- * Note that if sz passed in is null, returns false.
- */
- equals:function(sz) {
- var equals = false;
- if (sz != null) {
- equals = ((this.w == sz.w && this.h == sz.h) ||
- (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
- }
- return equals;
- },
-
- CLASS_NAME: "OpenLayers.Size"
-});
-/* ======================================================================
- OpenLayers/Console.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/BaseTypes/Class.js
- */
-
-/**
- * Namespace: OpenLayers.Console
- * The OpenLayers.Console namespace is used for debugging and error logging.
- * If the Firebug Lite (../Firebug/firebug.js) is included before this script,
- * calls to OpenLayers.Console methods will get redirected to window.console.
- * This makes use of the Firebug extension where available and allows for
- * cross-browser debugging Firebug style.
- *
- * Note:
- * Note that behavior will differ with the Firebug extention and Firebug Lite.
- * Most notably, the Firebug Lite console does not currently allow for
- * hyperlinks to code or for clicking on object to explore their properties.
- *
- */
-OpenLayers.Console = {
- /**
- * Create empty functions for all console methods. The real value of these
- * properties will be set if Firebug Lite (../Firebug/firebug.js script) is
- * included. We explicitly require the Firebug Lite script to trigger
- * functionality of the OpenLayers.Console methods.
- */
-
- /**
- * APIFunction: log
- * Log an object in the console. The Firebug Lite console logs string
- * representation of objects. Given multiple arguments, they will
- * be cast to strings and logged with a space delimiter. If the first
- * argument is a string with printf-like formatting, subsequent arguments
- * will be used in string substitution. Any additional arguments (beyond
- * the number substituted in a format string) will be appended in a space-
- * delimited line.
- *
- * Parameters:
- * object - {Object}
- */
- log: function() {},
-
- /**
- * APIFunction: debug
- * Writes a message to the console, including a hyperlink to the line
- * where it was called.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- debug: function() {},
-
- /**
- * APIFunction: info
- * Writes a message to the console with the visual "info" icon and color
- * coding and a hyperlink to the line where it was called.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- info: function() {},
-
- /**
- * APIFunction: warn
- * Writes a message to the console with the visual "warning" icon and
- * color coding and a hyperlink to the line where it was called.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- warn: function() {},
-
- /**
- * APIFunction: error
- * Writes a message to the console with the visual "error" icon and color
- * coding and a hyperlink to the line where it was called.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- error: function() {},
-
- /**
- * APIFunction: userError
- * A single interface for showing error messages to the user. The default
- * behavior is a Javascript alert, though this can be overridden by
- * reassigning OpenLayers.Console.userError to a different function.
- *
- * Expects a single error message
- *
- * Parameters:
- * error - {Object}
- */
- userError: function(error) {
- alert(error);
- },
-
- /**
- * APIFunction: assert
- * Tests that an expression is true. If not, it will write a message to
- * the console and throw an exception.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- assert: function() {},
-
- /**
- * APIFunction: dir
- * Prints an interactive listing of all properties of the object. This
- * looks identical to the view that you would see in the DOM tab.
- *
- * Parameters:
- * object - {Object}
- */
- dir: function() {},
-
- /**
- * APIFunction: dirxml
- * Prints the XML source tree of an HTML or XML element. This looks
- * identical to the view that you would see in the HTML tab. You can click
- * on any node to inspect it in the HTML tab.
- *
- * Parameters:
- * object - {Object}
- */
- dirxml: function() {},
-
- /**
- * APIFunction: trace
- * Prints an interactive stack trace of JavaScript execution at the point
- * where it is called. The stack trace details the functions on the stack,
- * as well as the values that were passed as arguments to each function.
- * You can click each function to take you to its source in the Script tab,
- * and click each argument value to inspect it in the DOM or HTML tabs.
- *
- */
- trace: function() {},
-
- /**
- * APIFunction: group
- * Writes a message to the console and opens a nested block to indent all
- * future messages sent to the console. Call OpenLayers.Console.groupEnd()
- * to close the block.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- group: function() {},
-
- /**
- * APIFunction: groupEnd
- * Closes the most recently opened block created by a call to
- * OpenLayers.Console.group
- */
- groupEnd: function() {},
-
- /**
- * APIFunction: time
- * Creates a new timer under the given name. Call
- * OpenLayers.Console.timeEnd(name)
- * with the same name to stop the timer and print the time elapsed.
- *
- * Parameters:
- * name - {String}
- */
- time: function() {},
-
- /**
- * APIFunction: timeEnd
- * Stops a timer created by a call to OpenLayers.Console.time(name) and
- * writes the time elapsed.
- *
- * Parameters:
- * name - {String}
- */
- timeEnd: function() {},
-
- /**
- * APIFunction: profile
- * Turns on the JavaScript profiler. The optional argument title would
- * contain the text to be printed in the header of the profile report.
- *
- * This function is not currently implemented in Firebug Lite.
- *
- * Parameters:
- * title - {String} Optional title for the profiler
- */
- profile: function() {},
-
- /**
- * APIFunction: profileEnd
- * Turns off the JavaScript profiler and prints its report.
- *
- * This function is not currently implemented in Firebug Lite.
- */
- profileEnd: function() {},
-
- /**
- * APIFunction: count
- * Writes the number of times that the line of code where count was called
- * was executed. The optional argument title will print a message in
- * addition to the number of the count.
- *
- * This function is not currently implemented in Firebug Lite.
- *
- * Parameters:
- * title - {String} Optional title to be printed with count
- */
- count: function() {},
-
- CLASS_NAME: "OpenLayers.Console"
-};
-
-/**
- * Execute an anonymous function to extend the OpenLayers.Console namespace
- * if the firebug.js script is included. This closure is used so that the
- * "scripts" and "i" variables don't pollute the global namespace.
- */
-(function() {
- /**
- * If Firebug Lite is included (before this script), re-route all
- * OpenLayers.Console calls to the console object.
- */
- var scripts = document.getElementsByTagName("script");
- for(var i=0, len=scripts.length; i method to set this value and the method to
- * retrieve it.
- */
- code: null,
-
- /**
- * APIProperty: defaultCode
- * {String} Default language to use when a specific language can't be
- * found. Default is "en".
- */
- defaultCode: "en",
-
- /**
- * APIFunction: getCode
- * Get the current language code.
- *
- * Returns:
- * {String} The current language code.
- */
- getCode: function() {
- if(!OpenLayers.Lang.code) {
- OpenLayers.Lang.setCode();
- }
- return OpenLayers.Lang.code;
- },
-
- /**
- * APIFunction: setCode
- * Set the language code for string translation. This code is used by
- * the method.
- *
- * Parameters:
- * code - {String} These codes follow the IETF recommendations at
- * http://www.ietf.org/rfc/rfc3066.txt. If no value is set, the
- * browser's language setting will be tested. If no
- * dictionary exists for the code, the
- * will be used.
- */
- setCode: function(code) {
- var lang;
- if(!code) {
- code = (OpenLayers.BROWSER_NAME == "msie") ?
- navigator.userLanguage : navigator.language;
- }
- var parts = code.split('-');
- parts[0] = parts[0].toLowerCase();
- if(typeof OpenLayers.Lang[parts[0]] == "object") {
- lang = parts[0];
- }
-
- // check for regional extensions
- if(parts[1]) {
- var testLang = parts[0] + '-' + parts[1].toUpperCase();
- if(typeof OpenLayers.Lang[testLang] == "object") {
- lang = testLang;
- }
- }
- if(!lang) {
- OpenLayers.Console.warn(
- 'Failed to find OpenLayers.Lang.' + parts.join("-") +
- ' dictionary, falling back to default language'
- );
- lang = OpenLayers.Lang.defaultCode;
- }
-
- OpenLayers.Lang.code = lang;
- },
-
- /**
- * APIMethod: translate
- * Looks up a key from a dictionary based on the current language string.
- * The value of will be used to determine the appropriate
- * dictionary. Dictionaries are stored in .
- *
- * Parameters:
- * key - {String} The key for an i18n string value in the dictionary.
- * context - {Object} Optional context to be used with
- * .
- *
- * Returns:
- * {String} A internationalized string.
- */
- translate: function(key, context) {
- var dictionary = OpenLayers.Lang[OpenLayers.Lang.getCode()];
- var message = dictionary && dictionary[key];
- if(!message) {
- // Message not found, fall back to message key
- message = key;
- }
- if(context) {
- message = OpenLayers.String.format(message, context);
- }
- return message;
- }
-
-};
-
-
-/**
- * APIMethod: OpenLayers.i18n
- * Alias for . Looks up a key from a dictionary
- * based on the current language string. The value of
- * will be used to determine the appropriate
- * dictionary. Dictionaries are stored in .
- *
- * Parameters:
- * key - {String} The key for an i18n string value in the dictionary.
- * context - {Object} Optional context to be used with
- * .
- *
- * Returns:
- * {String} A internationalized string.
- */
-OpenLayers.i18n = OpenLayers.Lang.translate;
-/* ======================================================================
- OpenLayers/Util.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/BaseTypes.js
- * @requires OpenLayers/BaseTypes/Bounds.js
- * @requires OpenLayers/BaseTypes/Element.js
- * @requires OpenLayers/BaseTypes/LonLat.js
- * @requires OpenLayers/BaseTypes/Pixel.js
- * @requires OpenLayers/BaseTypes/Size.js
- * @requires OpenLayers/Lang.js
- */
-
-/**
- * Namespace: Util
- */
-OpenLayers.Util = OpenLayers.Util || {};
-
-/**
- * Function: getElement
- * This is the old $() from prototype
- *
- * Parameters:
- * e - {String or DOMElement or Window}
- *
- * Returns:
- * {Array(DOMElement) or DOMElement}
- */
-OpenLayers.Util.getElement = function() {
- var elements = [];
-
- for (var i=0, len=arguments.length; i= 0; i--) {
- if(array[i] == item) {
- array.splice(i,1);
- //break;more than once??
- }
- }
- return array;
-};
-
-/**
- * Function: indexOf
- * Seems to exist already in FF, but not in MOZ.
- *
- * Parameters:
- * array - {Array}
- * obj - {*}
- *
- * Returns:
- * {Integer} The index at, which the first object was found in the array.
- * If not found, returns -1.
- */
-OpenLayers.Util.indexOf = function(array, obj) {
- // use the build-in function if available.
- if (typeof array.indexOf == "function") {
- return array.indexOf(obj);
- } else {
- for (var i = 0, len = array.length; i < len; i++) {
- if (array[i] == obj) {
- return i;
- }
- }
- return -1;
- }
-};
-
-
-
-/**
- * Function: modifyDOMElement
- *
- * Modifies many properties of a DOM element all at once. Passing in
- * null to an individual parameter will avoid setting the attribute.
- *
- * Parameters:
- * element - {DOMElement} DOM element to modify.
- * id - {String} The element id attribute to set.
- * px - {|Object} The element left and top position,
- * OpenLayers.Pixel or an object with
- * a 'x' and 'y' properties.
- * sz - {|Object} The element width and height,
- * OpenLayers.Size or an object with a
- * 'w' and 'h' properties.
- * position - {String} The position attribute. eg: absolute,
- * relative, etc.
- * border - {String} The style.border attribute. eg:
- * solid black 2px
- * overflow - {String} The style.overview attribute.
- * opacity - {Float} Fractional value (0.0 - 1.0)
- */
-OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position,
- border, overflow, opacity) {
-
- if (id) {
- element.id = id;
- }
- if (px) {
- element.style.left = px.x + "px";
- element.style.top = px.y + "px";
- }
- if (sz) {
- element.style.width = sz.w + "px";
- element.style.height = sz.h + "px";
- }
- if (position) {
- element.style.position = position;
- }
- if (border) {
- element.style.border = border;
- }
- if (overflow) {
- element.style.overflow = overflow;
- }
- if (parseFloat(opacity) >= 0.0 && parseFloat(opacity) < 1.0) {
- element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
- element.style.opacity = opacity;
- } else if (parseFloat(opacity) == 1.0) {
- element.style.filter = '';
- element.style.opacity = '';
- }
-};
-
-/**
- * Function: createDiv
- * Creates a new div and optionally set some standard attributes.
- * Null may be passed to each parameter if you do not wish to
- * set a particular attribute.
- * Note - zIndex is NOT set on the resulting div.
- *
- * Parameters:
- * id - {String} An identifier for this element. If no id is
- * passed an identifier will be created
- * automatically.
- * px - {|Object} The element left and top position,
- * OpenLayers.Pixel or an object with
- * a 'x' and 'y' properties.
- * sz - {|Object} The element width and height,
- * OpenLayers.Size or an object with a
- * 'w' and 'h' properties.
- * imgURL - {String} A url pointing to an image to use as a
- * background image.
- * position - {String} The style.position value. eg: absolute,
- * relative etc.
- * border - {String} The the style.border value.
- * eg: 2px solid black
- * overflow - {String} The style.overflow value. Eg. hidden
- * opacity - {Float} Fractional value (0.0 - 1.0)
- *
- * Returns:
- * {DOMElement} A DOM Div created with the specified attributes.
- */
-OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position,
- border, overflow, opacity) {
-
- var dom = document.createElement('div');
-
- if (imgURL) {
- dom.style.backgroundImage = 'url(' + imgURL + ')';
- }
-
- //set generic properties
- if (!id) {
- id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
- }
- if (!position) {
- position = "absolute";
- }
- OpenLayers.Util.modifyDOMElement(dom, id, px, sz, position,
- border, overflow, opacity);
-
- return dom;
-};
-
-/**
- * Function: createImage
- * Creates an img element with specific attribute values.
- *
- * Parameters:
- * id - {String} The id field for the img. If none assigned one will be
- * automatically generated.
- * px - {|Object} The element left and top position,
- * OpenLayers.Pixel or an object with
- * a 'x' and 'y' properties.
- * sz - {|Object} The element width and height,
- * OpenLayers.Size or an object with a
- * 'w' and 'h' properties.
- * imgURL - {String} The url to use as the image source.
- * position - {String} The style.position value.
- * border - {String} The border to place around the image.
- * opacity - {Float} Fractional value (0.0 - 1.0)
- * delayDisplay - {Boolean} If true waits until the image has been
- * loaded.
- *
- * Returns:
- * {DOMElement} A DOM Image created with the specified attributes.
- */
-OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border,
- opacity, delayDisplay) {
-
- var image = document.createElement("img");
-
- //set generic properties
- if (!id) {
- id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
- }
- if (!position) {
- position = "relative";
- }
- OpenLayers.Util.modifyDOMElement(image, id, px, sz, position,
- border, null, opacity);
-
- if (delayDisplay) {
- image.style.display = "none";
- function display() {
- image.style.display = "";
- OpenLayers.Event.stopObservingElement(image);
- }
- OpenLayers.Event.observe(image, "load", display);
- OpenLayers.Event.observe(image, "error", display);
- }
-
- //set special properties
- image.style.alt = id;
- image.galleryImg = "no";
- if (imgURL) {
- image.src = imgURL;
- }
-
- return image;
-};
-
-/**
- * Property: IMAGE_RELOAD_ATTEMPTS
- * {Integer} How many times should we try to reload an image before giving up?
- * Default is 0
- */
-OpenLayers.IMAGE_RELOAD_ATTEMPTS = 0;
-
-/**
- * Property: alphaHackNeeded
- * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
- */
-OpenLayers.Util.alphaHackNeeded = null;
-
-/**
- * Function: alphaHack
- * Checks whether it's necessary (and possible) to use the png alpha
- * hack which allows alpha transparency for png images under Internet
- * Explorer.
- *
- * Returns:
- * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
- */
-OpenLayers.Util.alphaHack = function() {
- if (OpenLayers.Util.alphaHackNeeded == null) {
- var arVersion = navigator.appVersion.split("MSIE");
- var version = parseFloat(arVersion[1]);
- var filter = false;
-
- // IEs4Lin dies when trying to access document.body.filters, because
- // the property is there, but requires a DLL that can't be provided. This
- // means that we need to wrap this in a try/catch so that this can
- // continue.
-
- try {
- filter = !!(document.body.filters);
- } catch (e) {}
-
- OpenLayers.Util.alphaHackNeeded = (filter &&
- (version >= 5.5) && (version < 7));
- }
- return OpenLayers.Util.alphaHackNeeded;
-};
-
-/**
- * Function: modifyAlphaImageDiv
- *
- * Parameters:
- * div - {DOMElement} Div containing Alpha-adjusted Image
- * id - {String}
- * px - {|Object} OpenLayers.Pixel or an object with
- * a 'x' and 'y' properties.
- * sz - {|Object} OpenLayers.Size or an object with
- * a 'w' and 'h' properties.
- * imgURL - {String}
- * position - {String}
- * border - {String}
- * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
- * opacity - {Float} Fractional value (0.0 - 1.0)
- */
-OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL,
- position, border, sizing,
- opacity) {
-
- OpenLayers.Util.modifyDOMElement(div, id, px, sz, position,
- null, null, opacity);
-
- var img = div.childNodes[0];
-
- if (imgURL) {
- img.src = imgURL;
- }
- OpenLayers.Util.modifyDOMElement(img, div.id + "_innerImage", null, sz,
- "relative", border);
-
- if (OpenLayers.Util.alphaHack()) {
- if(div.style.display != "none") {
- div.style.display = "inline-block";
- }
- if (sizing == null) {
- sizing = "scale";
- }
-
- div.style.filter = "progid:DXImageTransform.Microsoft" +
- ".AlphaImageLoader(src='" + img.src + "', " +
- "sizingMethod='" + sizing + "')";
- if (parseFloat(div.style.opacity) >= 0.0 &&
- parseFloat(div.style.opacity) < 1.0) {
- div.style.filter += " alpha(opacity=" + div.style.opacity * 100 + ")";
- }
-
- img.style.filter = "alpha(opacity=0)";
- }
-};
-
-/**
- * Function: createAlphaImageDiv
- *
- * Parameters:
- * id - {String}
- * px - {|Object} OpenLayers.Pixel or an object with
- * a 'x' and 'y' properties.
- * sz - {|Object} OpenLayers.Size or an object with
- * a 'w' and 'h' properties.
- * imgURL - {String}
- * position - {String}
- * border - {String}
- * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
- * opacity - {Float} Fractional value (0.0 - 1.0)
- * delayDisplay - {Boolean} If true waits until the image has been
- * loaded.
- *
- * Returns:
- * {DOMElement} A DOM Div created with a DOM Image inside it. If the hack is
- * needed for transparency in IE, it is added.
- */
-OpenLayers.Util.createAlphaImageDiv = function(id, px, sz, imgURL,
- position, border, sizing,
- opacity, delayDisplay) {
-
- var div = OpenLayers.Util.createDiv();
- var img = OpenLayers.Util.createImage(null, null, null, null, null, null,
- null, delayDisplay);
- img.className = "olAlphaImg";
- div.appendChild(img);
-
- OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, position,
- border, sizing, opacity);
-
- return div;
-};
-
-
-/**
- * Function: upperCaseObject
- * Creates a new hashtable and copies over all the keys from the
- * passed-in object, but storing them under an uppercased
- * version of the key at which they were stored.
- *
- * Parameters:
- * object - {Object}
- *
- * Returns:
- * {Object} A new Object with all the same keys but uppercased
- */
-OpenLayers.Util.upperCaseObject = function (object) {
- var uObject = {};
- for (var key in object) {
- uObject[key.toUpperCase()] = object[key];
- }
- return uObject;
-};
-
-/**
- * Function: applyDefaults
- * Takes an object and copies any properties that don't exist from
- * another properties, by analogy with OpenLayers.Util.extend() from
- * Prototype.js.
- *
- * Parameters:
- * to - {Object} The destination object.
- * from - {Object} The source object. Any properties of this object that
- * are undefined in the to object will be set on the to object.
- *
- * Returns:
- * {Object} A reference to the to object. Note that the to argument is modified
- * in place and returned by this function.
- */
-OpenLayers.Util.applyDefaults = function (to, from) {
- to = to || {};
- /*
- * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
- * prototype object" when calling hawOwnProperty if the source object is an
- * instance of window.Event.
- */
- var fromIsEvt = typeof window.Event == "function"
- && from instanceof window.Event;
-
- for (var key in from) {
- if (to[key] === undefined ||
- (!fromIsEvt && from.hasOwnProperty
- && from.hasOwnProperty(key) && !to.hasOwnProperty(key))) {
- to[key] = from[key];
- }
- }
- /**
- * IE doesn't include the toString property when iterating over an object's
- * properties with the for(property in object) syntax. Explicitly check if
- * the source has its own toString property.
- */
- if(!fromIsEvt && from && from.hasOwnProperty
- && from.hasOwnProperty('toString') && !to.hasOwnProperty('toString')) {
- to.toString = from.toString;
- }
-
- return to;
-};
-
-/**
- * Function: getParameterString
- *
- * Parameters:
- * params - {Object}
- *
- * Returns:
- * {String} A concatenation of the properties of an object in
- * http parameter notation.
- * (ex. "key1=value1&key2=value2&key3=value3" )
- * If a parameter is actually a list, that parameter will then
- * be set to a comma-seperated list of values (foo,bar) instead
- * of being URL escaped (foo%3Abar).
- */
-OpenLayers.Util.getParameterString = function(params) {
- var paramsArray = [];
-
- for (var key in params) {
- var value = params[key];
- if ((value != null) && (typeof value != 'function')) {
- var encodedValue;
- if (typeof value == 'object' && value.constructor == Array) {
- /* value is an array; encode items and separate with "," */
- var encodedItemArray = [];
- var item;
- for (var itemIndex=0, len=value.length; itemIndex} (or any object with both .lat, .lon properties)
- * p2 - {} (or any object with both .lat, .lon properties)
- *
- * Returns:
- * {Float} The distance (in km) between the two input points as measured on an
- * ellipsoid. Note that the input point objects must be in geographic
- * coordinates (decimal degrees) and the return distance is in kilometers.
- */
-OpenLayers.Util.distVincenty = function(p1, p2) {
- var ct = OpenLayers.Util.VincentyConstants;
- var a = ct.a, b = ct.b, f = ct.f;
-
- var L = OpenLayers.Util.rad(p2.lon - p1.lon);
- var U1 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p1.lat)));
- var U2 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p2.lat)));
- var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
- var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
- var lambda = L, lambdaP = 2*Math.PI;
- var iterLimit = 20;
- while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
- var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
- var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
- (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
- if (sinSigma==0) {
- return 0; // co-incident points
- }
- var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
- var sigma = Math.atan2(sinSigma, cosSigma);
- var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
- var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
- var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
- var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
- lambdaP = lambda;
- lambda = L + (1-C) * f * Math.sin(alpha) *
- (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
- }
- if (iterLimit==0) {
- return NaN; // formula failed to converge
- }
- var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
- var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
- var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
- var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
- B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
- var s = b*A*(sigma-deltaSigma);
- var d = s.toFixed(3)/1000; // round to 1mm precision
- return d;
-};
-
-/**
- * APIFunction: destinationVincenty
- * Calculate destination point given start point lat/long (numeric degrees),
- * bearing (numeric degrees) & distance (in m).
- * Adapted from Chris Veness work, see
- * http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html
- *
- * Parameters:
- * lonlat - {} (or any object with both .lat, .lon
- * properties) The start point.
- * brng - {Float} The bearing (degrees).
- * dist - {Float} The ground distance (meters).
- *
- * Returns:
- * {} The destination point.
- */
-OpenLayers.Util.destinationVincenty = function(lonlat, brng, dist) {
- var u = OpenLayers.Util;
- var ct = u.VincentyConstants;
- var a = ct.a, b = ct.b, f = ct.f;
-
- var lon1 = lonlat.lon;
- var lat1 = lonlat.lat;
-
- var s = dist;
- var alpha1 = u.rad(brng);
- var sinAlpha1 = Math.sin(alpha1);
- var cosAlpha1 = Math.cos(alpha1);
-
- var tanU1 = (1-f) * Math.tan(u.rad(lat1));
- var cosU1 = 1 / Math.sqrt((1 + tanU1*tanU1)), sinU1 = tanU1*cosU1;
- var sigma1 = Math.atan2(tanU1, cosAlpha1);
- var sinAlpha = cosU1 * sinAlpha1;
- var cosSqAlpha = 1 - sinAlpha*sinAlpha;
- var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
- var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
- var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
-
- var sigma = s / (b*A), sigmaP = 2*Math.PI;
- while (Math.abs(sigma-sigmaP) > 1e-12) {
- var cos2SigmaM = Math.cos(2*sigma1 + sigma);
- var sinSigma = Math.sin(sigma);
- var cosSigma = Math.cos(sigma);
- var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
- B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
- sigmaP = sigma;
- sigma = s / (b*A) + deltaSigma;
- }
-
- var tmp = sinU1*sinSigma - cosU1*cosSigma*cosAlpha1;
- var lat2 = Math.atan2(sinU1*cosSigma + cosU1*sinSigma*cosAlpha1,
- (1-f)*Math.sqrt(sinAlpha*sinAlpha + tmp*tmp));
- var lambda = Math.atan2(sinSigma*sinAlpha1, cosU1*cosSigma - sinU1*sinSigma*cosAlpha1);
- var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
- var L = lambda - (1-C) * f * sinAlpha *
- (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
-
- var revAz = Math.atan2(sinAlpha, -tmp); // final bearing
-
- return new OpenLayers.LonLat(lon1+u.deg(L), u.deg(lat2));
-};
-
-/**
- * Function: getParameters
- * Parse the parameters from a URL or from the current page itself into a
- * JavaScript Object. Note that parameter values with commas are separated
- * out into an Array.
- *
- * Parameters:
- * url - {String} Optional url used to extract the query string.
- * If url is null or is not supplied, query string is taken
- * from the page location.
- *
- * Returns:
- * {Object} An object of key/value pairs from the query string.
- */
-OpenLayers.Util.getParameters = function(url) {
- // if no url specified, take it from the location bar
- url = (url === null || url === undefined) ? window.location.href : url;
-
- //parse out parameters portion of url string
- var paramsString = "";
- if (OpenLayers.String.contains(url, '?')) {
- var start = url.indexOf('?') + 1;
- var end = OpenLayers.String.contains(url, "#") ?
- url.indexOf('#') : url.length;
- paramsString = url.substring(start, end);
- }
-
- var parameters = {};
- var pairs = paramsString.split(/[&;]/);
- for(var i=0, len=pairs.length; i 1.0) ? (1.0 / scale)
- : scale;
- return normScale;
-};
-
-/**
- * Function: getResolutionFromScale
- *
- * Parameters:
- * scale - {Float}
- * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
- * Default is degrees
- *
- * Returns:
- * {Float} The corresponding resolution given passed-in scale and unit
- * parameters. If the given scale is falsey, the returned resolution will
- * be undefined.
- */
-OpenLayers.Util.getResolutionFromScale = function (scale, units) {
- var resolution;
- if (scale) {
- if (units == null) {
- units = "degrees";
- }
- var normScale = OpenLayers.Util.normalizeScale(scale);
- resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units]
- * OpenLayers.DOTS_PER_INCH);
- }
- return resolution;
-};
-
-/**
- * Function: getScaleFromResolution
- *
- * Parameters:
- * resolution - {Float}
- * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
- * Default is degrees
- *
- * Returns:
- * {Float} The corresponding scale given passed-in resolution and unit
- * parameters.
- */
-OpenLayers.Util.getScaleFromResolution = function (resolution, units) {
-
- if (units == null) {
- units = "degrees";
- }
-
- var scale = resolution * OpenLayers.INCHES_PER_UNIT[units] *
- OpenLayers.DOTS_PER_INCH;
- return scale;
-};
-
-/**
- * Function: pagePosition
- * Calculates the position of an element on the page (see
- * http://code.google.com/p/doctype/wiki/ArticlePageOffset)
- *
- * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
- * Copyright (c) 2006, Yahoo! Inc.
- * All rights reserved.
- *
- * Redistribution and use of this software in source and binary forms, with or
- * without modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * * Neither the name of Yahoo! Inc. nor the names of its contributors may be
- * used to endorse or promote products derived from this software without
- * specific prior written permission of Yahoo! Inc.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * Parameters:
- * forElement - {DOMElement}
- *
- * Returns:
- * {Array} two item array, Left value then Top value.
- */
-OpenLayers.Util.pagePosition = function(forElement) {
- // NOTE: If element is hidden (display none or disconnected or any the
- // ancestors are hidden) we get (0,0) by default but we still do the
- // accumulation of scroll position.
-
- var pos = [0, 0];
- var viewportElement = OpenLayers.Util.getViewportElement();
- if (!forElement || forElement == window || forElement == viewportElement) {
- // viewport is always at 0,0 as that defined the coordinate system for
- // this function - this avoids special case checks in the code below
- return pos;
- }
-
- // Gecko browsers normally use getBoxObjectFor to calculate the position.
- // When invoked for an element with an implicit absolute position though it
- // can be off by one. Therefore the recursive implementation is used in
- // those (relatively rare) cases.
- var BUGGY_GECKO_BOX_OBJECT =
- OpenLayers.IS_GECKO && document.getBoxObjectFor &&
- OpenLayers.Element.getStyle(forElement, 'position') == 'absolute' &&
- (forElement.style.top == '' || forElement.style.left == '');
-
- var parent = null;
- var box;
-
- if (forElement.getBoundingClientRect) { // IE
- box = forElement.getBoundingClientRect();
- var scrollTop = viewportElement.scrollTop;
- var scrollLeft = viewportElement.scrollLeft;
-
- pos[0] = box.left + scrollLeft;
- pos[1] = box.top + scrollTop;
-
- } else if (document.getBoxObjectFor && !BUGGY_GECKO_BOX_OBJECT) { // gecko
- // Gecko ignores the scroll values for ancestors, up to 1.9. See:
- // https://bugzilla.mozilla.org/show_bug.cgi?id=328881 and
- // https://bugzilla.mozilla.org/show_bug.cgi?id=330619
-
- box = document.getBoxObjectFor(forElement);
- var vpBox = document.getBoxObjectFor(viewportElement);
- pos[0] = box.screenX - vpBox.screenX;
- pos[1] = box.screenY - vpBox.screenY;
-
- } else { // safari/opera
- pos[0] = forElement.offsetLeft;
- pos[1] = forElement.offsetTop;
- parent = forElement.offsetParent;
- if (parent != forElement) {
- while (parent) {
- pos[0] += parent.offsetLeft;
- pos[1] += parent.offsetTop;
- parent = parent.offsetParent;
- }
- }
-
- var browser = OpenLayers.BROWSER_NAME;
-
- // opera & (safari absolute) incorrectly account for body offsetTop
- if (browser == "opera" || (browser == "safari" &&
- OpenLayers.Element.getStyle(forElement, 'position') == 'absolute')) {
- pos[1] -= document.body.offsetTop;
- }
-
- // accumulate the scroll positions for everything but the body element
- parent = forElement.offsetParent;
- while (parent && parent != document.body) {
- pos[0] -= parent.scrollLeft;
- // see https://bugs.opera.com/show_bug.cgi?id=249965
- if (browser != "opera" || parent.tagName != 'TR') {
- pos[1] -= parent.scrollTop;
- }
- parent = parent.offsetParent;
- }
- }
-
- return pos;
-};
-
-/**
- * Function: getViewportElement
- * Returns die viewport element of the document. The viewport element is
- * usually document.documentElement, except in IE,where it is either
- * document.body or document.documentElement, depending on the document's
- * compatibility mode (see
- * http://code.google.com/p/doctype/wiki/ArticleClientViewportElement)
- *
- * Returns:
- * {DOMElement}
- */
-OpenLayers.Util.getViewportElement = function() {
- var viewportElement = arguments.callee.viewportElement;
- if (viewportElement == undefined) {
- viewportElement = (OpenLayers.BROWSER_NAME == "msie" &&
- document.compatMode != 'CSS1Compat') ? document.body :
- document.documentElement;
- arguments.callee.viewportElement = viewportElement;
- }
- return viewportElement;
-};
-
-/**
- * Function: isEquivalentUrl
- * Test two URLs for equivalence.
- *
- * Setting 'ignoreCase' allows for case-independent comparison.
- *
- * Comparison is based on:
- * - Protocol
- * - Host (evaluated without the port)
- * - Port (set 'ignorePort80' to ignore "80" values)
- * - Hash ( set 'ignoreHash' to disable)
- * - Pathname (for relative <-> absolute comparison)
- * - Arguments (so they can be out of order)
- *
- * Parameters:
- * url1 - {String}
- * url2 - {String}
- * options - {Object} Allows for customization of comparison:
- * 'ignoreCase' - Default is True
- * 'ignorePort80' - Default is True
- * 'ignoreHash' - Default is True
- *
- * Returns:
- * {Boolean} Whether or not the two URLs are equivalent
- */
-OpenLayers.Util.isEquivalentUrl = function(url1, url2, options) {
- options = options || {};
-
- OpenLayers.Util.applyDefaults(options, {
- ignoreCase: true,
- ignorePort80: true,
- ignoreHash: true
- });
-
- var urlObj1 = OpenLayers.Util.createUrlObject(url1, options);
- var urlObj2 = OpenLayers.Util.createUrlObject(url2, options);
-
- //compare all keys except for "args" (treated below)
- for(var key in urlObj1) {
- if(key !== "args") {
- if(urlObj1[key] != urlObj2[key]) {
- return false;
- }
- }
- }
-
- // compare search args - irrespective of order
- for(var key in urlObj1.args) {
- if(urlObj1.args[key] != urlObj2.args[key]) {
- return false;
- }
- delete urlObj2.args[key];
- }
- // urlObj2 shouldn't have any args left
- for(var key in urlObj2.args) {
- return false;
- }
-
- return true;
-};
-
-/**
- * Function: createUrlObject
- *
- * Parameters:
- * url - {String}
- * options - {Object} A hash of options.
- *
- * Valid options:
- * ignoreCase - {Boolean} lowercase url,
- * ignorePort80 - {Boolean} don't include explicit port if port is 80,
- * ignoreHash - {Boolean} Don't include part of url after the hash (#).
- *
- * Returns:
- * {Object} An object with separate url, a, port, host, and args parsed out
- * and ready for comparison
- */
-OpenLayers.Util.createUrlObject = function(url, options) {
- options = options || {};
-
- // deal with relative urls first
- if(!(/^\w+:\/\//).test(url)) {
- var loc = window.location;
- var port = loc.port ? ":" + loc.port : "";
- var fullUrl = loc.protocol + "//" + loc.host.split(":").shift() + port;
- if(url.indexOf("/") === 0) {
- // full pathname
- url = fullUrl + url;
- } else {
- // relative to current path
- var parts = loc.pathname.split("/");
- parts.pop();
- url = fullUrl + parts.join("/") + "/" + url;
- }
- }
-
- if (options.ignoreCase) {
- url = url.toLowerCase();
- }
-
- var a = document.createElement('a');
- a.href = url;
-
- var urlObject = {};
-
- //host (without port)
- urlObject.host = a.host.split(":").shift();
-
- //protocol
- urlObject.protocol = a.protocol;
-
- //port (get uniform browser behavior with port 80 here)
- if(options.ignorePort80) {
- urlObject.port = (a.port == "80" || a.port == "0") ? "" : a.port;
- } else {
- urlObject.port = (a.port == "" || a.port == "0") ? "80" : a.port;
- }
-
- //hash
- urlObject.hash = (options.ignoreHash || a.hash === "#") ? "" : a.hash;
-
- //args
- var queryString = a.search;
- if (!queryString) {
- var qMark = url.indexOf("?");
- queryString = (qMark != -1) ? url.substr(qMark) : "";
- }
- urlObject.args = OpenLayers.Util.getParameters(queryString);
-
- // pathname
- //
- // This is a workaround for Internet Explorer where
- // window.location.pathname has a leading "/", but
- // a.pathname has no leading "/".
- urlObject.pathname = (a.pathname.charAt(0) == "/") ? a.pathname : "/" + a.pathname;
-
- return urlObject;
-};
-
-/**
- * Function: removeTail
- * Takes a url and removes everything after the ? and #
- *
- * Parameters:
- * url - {String} The url to process
- *
- * Returns:
- * {String} The string with all queryString and Hash removed
- */
-OpenLayers.Util.removeTail = function(url) {
- var head = null;
-
- var qMark = url.indexOf("?");
- var hashMark = url.indexOf("#");
-
- if (qMark == -1) {
- head = (hashMark != -1) ? url.substr(0,hashMark) : url;
- } else {
- head = (hashMark != -1) ? url.substr(0,Math.min(qMark, hashMark))
- : url.substr(0, qMark);
- }
- return head;
-};
-
-/**
- * Constant: IS_GECKO
- * {Boolean} True if the userAgent reports the browser to use the Gecko engine
- */
-OpenLayers.IS_GECKO = (function() {
- var ua = navigator.userAgent.toLowerCase();
- return ua.indexOf("webkit") == -1 && ua.indexOf("gecko") != -1;
-})();
-
-/**
- * Constant: CANVAS_SUPPORTED
- * {Boolean} True if canvas 2d is supported.
- */
-OpenLayers.CANVAS_SUPPORTED = (function() {
- var elem = document.createElement('canvas');
- return !!(elem.getContext && elem.getContext('2d'));
-})();
-
-/**
- * Constant: BROWSER_NAME
- * {String}
- * A substring of the navigator.userAgent property. Depending on the userAgent
- * property, this will be the empty string or one of the following:
- * * "opera" -- Opera
- * * "msie" -- Internet Explorer
- * * "safari" -- Safari
- * * "firefox" -- Firefox
- * * "mozilla" -- Mozilla
- */
-OpenLayers.BROWSER_NAME = (function() {
- var name = "";
- var ua = navigator.userAgent.toLowerCase();
- if (ua.indexOf("opera") != -1) {
- name = "opera";
- } else if (ua.indexOf("msie") != -1) {
- name = "msie";
- } else if (ua.indexOf("safari") != -1) {
- name = "safari";
- } else if (ua.indexOf("mozilla") != -1) {
- if (ua.indexOf("firefox") != -1) {
- name = "firefox";
- } else {
- name = "mozilla";
- }
- }
- return name;
-})();
-
-/**
- * Function: getBrowserName
- *
- * Returns:
- * {String} A string which specifies which is the current
- * browser in which we are running.
- *
- * Currently-supported browser detection and codes:
- * * 'opera' -- Opera
- * * 'msie' -- Internet Explorer
- * * 'safari' -- Safari
- * * 'firefox' -- Firefox
- * * 'mozilla' -- Mozilla
- *
- * If we are unable to property identify the browser, we
- * return an empty string.
- */
-OpenLayers.Util.getBrowserName = function() {
- return OpenLayers.BROWSER_NAME;
-};
-
-/**
- * Method: getRenderedDimensions
- * Renders the contentHTML offscreen to determine actual dimensions for
- * popup sizing. As we need layout to determine dimensions the content
- * is rendered -9999px to the left and absolute to ensure the
- * scrollbars do not flicker
- *
- * Parameters:
- * contentHTML
- * size - {} If either the 'w' or 'h' properties is
- * specified, we fix that dimension of the div to be measured. This is
- * useful in the case where we have a limit in one dimension and must
- * therefore meaure the flow in the other dimension.
- * options - {Object}
- *
- * Allowed Options:
- * displayClass - {String} Optional parameter. A CSS class name(s) string
- * to provide the CSS context of the rendered content.
- * containerElement - {DOMElement} Optional parameter. Insert the HTML to
- * this node instead of the body root when calculating dimensions.
- *
- * Returns:
- * {}
- */
-OpenLayers.Util.getRenderedDimensions = function(contentHTML, size, options) {
-
- var w, h;
-
- // create temp container div with restricted size
- var container = document.createElement("div");
- container.style.visibility = "hidden";
-
- var containerElement = (options && options.containerElement)
- ? options.containerElement : document.body;
-
- // Opera and IE7 can't handle a node with position:aboslute if it inherits
- // position:absolute from a parent.
- var parentHasPositionAbsolute = false;
- var superContainer = null;
- var parent = containerElement;
- while (parent && parent.tagName.toLowerCase()!="body") {
- var parentPosition = OpenLayers.Element.getStyle(parent, "position");
- if(parentPosition == "absolute") {
- parentHasPositionAbsolute = true;
- break;
- } else if (parentPosition && parentPosition != "static") {
- break;
- }
- parent = parent.parentNode;
- }
- if(parentHasPositionAbsolute && (containerElement.clientHeight === 0 ||
- containerElement.clientWidth === 0) ){
- superContainer = document.createElement("div");
- superContainer.style.visibility = "hidden";
- superContainer.style.position = "absolute";
- superContainer.style.overflow = "visible";
- superContainer.style.width = document.body.clientWidth + "px";
- superContainer.style.height = document.body.clientHeight + "px";
- superContainer.appendChild(container);
- }
- container.style.position = "absolute";
-
- //fix a dimension, if specified.
- if (size) {
- if (size.w) {
- w = size.w;
- container.style.width = w + "px";
- } else if (size.h) {
- h = size.h;
- container.style.height = h + "px";
- }
- }
-
- //add css classes, if specified
- if (options && options.displayClass) {
- container.className = options.displayClass;
- }
-
- // create temp content div and assign content
- var content = document.createElement("div");
- content.innerHTML = contentHTML;
-
- // we need overflow visible when calculating the size
- content.style.overflow = "visible";
- if (content.childNodes) {
- for (var i=0, l=content.childNodes.length; i= 60) {
- coordinateseconds -= 60;
- coordinateminutes += 1;
- if( coordinateminutes >= 60) {
- coordinateminutes -= 60;
- coordinatedegrees += 1;
- }
- }
-
- if( coordinatedegrees < 10 ) {
- coordinatedegrees = "0" + coordinatedegrees;
- }
- var str = coordinatedegrees + "\u00B0";
-
- if (dmsOption.indexOf('dm') >= 0) {
- if( coordinateminutes < 10 ) {
- coordinateminutes = "0" + coordinateminutes;
- }
- str += coordinateminutes + "'";
-
- if (dmsOption.indexOf('dms') >= 0) {
- if( coordinateseconds < 10 ) {
- coordinateseconds = "0" + coordinateseconds;
- }
- str += coordinateseconds + '"';
- }
- }
-
- if (axis == "lon") {
- str += coordinate < 0 ? OpenLayers.i18n("W") : OpenLayers.i18n("E");
- } else {
- str += coordinate < 0 ? OpenLayers.i18n("S") : OpenLayers.i18n("N");
- }
- return str;
-};
-
-/* ======================================================================
- OpenLayers/Feature.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-
-/**
- * @requires OpenLayers/BaseTypes/Class.js
- * @requires OpenLayers/Util.js
- */
-
-/**
- * Class: OpenLayers.Feature
- * Features are combinations of geography and attributes. The OpenLayers.Feature
- * class specifically combines a marker and a lonlat.
- */
-OpenLayers.Feature = OpenLayers.Class({
-
- /**
- * Property: layer
- * {}
- */
- layer: null,
-
- /**
- * Property: id
- * {String}
- */
- id: null,
-
- /**
- * Property: lonlat
- * {}
- */
- lonlat: null,
-
- /**
- * Property: data
- * {Object}
- */
- data: null,
-
- /**
- * Property: marker
- * {}
- */
- marker: null,
-
- /**
- * APIProperty: popupClass
- * {} The class which will be used to instantiate
- * a new Popup. Default is .
- */
- popupClass: null,
-
- /**
- * Property: popup
- * {}
- */
- popup: null,
-
- /**
- * Constructor: OpenLayers.Feature
- * Constructor for features.
- *
- * Parameters:
- * layer - {}
- * lonlat - {}
- * data - {Object}
- *
- * Returns:
- * {}
- */
- initialize: function(layer, lonlat, data) {
- this.layer = layer;
- this.lonlat = lonlat;
- this.data = (data != null) ? data : {};
- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
- },
-
- /**
- * Method: destroy
- * nullify references to prevent circular references and memory leaks
- */
- destroy: function() {
-
- //remove the popup from the map
- if ((this.layer != null) && (this.layer.map != null)) {
- if (this.popup != null) {
- this.layer.map.removePopup(this.popup);
- }
- }
- // remove the marker from the layer
- if (this.layer != null && this.marker != null) {
- this.layer.removeMarker(this.marker);
- }
-
- this.layer = null;
- this.id = null;
- this.lonlat = null;
- this.data = null;
- if (this.marker != null) {
- this.destroyMarker(this.marker);
- this.marker = null;
- }
- if (this.popup != null) {
- this.destroyPopup(this.popup);
- this.popup = null;
- }
- },
-
- /**
- * Method: onScreen
- *
- * Returns:
- * {Boolean} Whether or not the feature is currently visible on screen
- * (based on its 'lonlat' property)
- */
- onScreen:function() {
-
- var onScreen = false;
- if ((this.layer != null) && (this.layer.map != null)) {
- var screenBounds = this.layer.map.getExtent();
- onScreen = screenBounds.containsLonLat(this.lonlat);
- }
- return onScreen;
- },
-
-
- /**
- * Method: createMarker
- * Based on the data associated with the Feature, create and return a marker object.
- *
- * Returns:
- * {} A Marker Object created from the 'lonlat' and 'icon' properties
- * set in this.data. If no 'lonlat' is set, returns null. If no
- * 'icon' is set, OpenLayers.Marker() will load the default image.
- *
- * Note - this.marker is set to return value
- *
- */
- createMarker: function() {
-
- if (this.lonlat != null) {
- this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
- }
- return this.marker;
- },
-
- /**
- * Method: destroyMarker
- * Destroys marker.
- * If user overrides the createMarker() function, s/he should be able
- * to also specify an alternative function for destroying it
- */
- destroyMarker: function() {
- this.marker.destroy();
- },
-
- /**
- * Method: createPopup
- * Creates a popup object created from the 'lonlat', 'popupSize',
- * and 'popupContentHTML' properties set in this.data. It uses
- * this.marker.icon as default anchor.
- *
- * If no 'lonlat' is set, returns null.
- * If no this.marker has been created, no anchor is sent.
- *
- * Note - the returned popup object is 'owned' by the feature, so you
- * cannot use the popup's destroy method to discard the popup.
- * Instead, you must use the feature's destroyPopup
- *
- * Note - this.popup is set to return value
- *
- * Parameters:
- * closeBox - {Boolean} create popup with closebox or not
- *
- * Returns:
- * {} Returns the created popup, which is also set
- * as 'popup' property of this feature. Will be of whatever type
- * specified by this feature's 'popupClass' property, but must be
- * of type .
- *
- */
- createPopup: function(closeBox) {
-
- if (this.lonlat != null) {
- if (!this.popup) {
- var anchor = (this.marker) ? this.marker.icon : null;
- var popupClass = this.popupClass ?
- this.popupClass : OpenLayers.Popup.Anchored;
- this.popup = new popupClass(this.id + "_popup",
- this.lonlat,
- this.data.popupSize,
- this.data.popupContentHTML,
- anchor,
- closeBox);
- }
- if (this.data.overflow != null) {
- this.popup.contentDiv.style.overflow = this.data.overflow;
- }
-
- this.popup.feature = this;
- }
- return this.popup;
- },
-
-
- /**
- * Method: destroyPopup
- * Destroys the popup created via createPopup.
- *
- * As with the marker, if user overrides the createPopup() function, s/he
- * should also be able to override the destruction
- */
- destroyPopup: function() {
- if (this.popup) {
- this.popup.feature = null;
- this.popup.destroy();
- this.popup = null;
- }
- },
-
- CLASS_NAME: "OpenLayers.Feature"
-});
-/* ======================================================================
- OpenLayers/Feature/Vector.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-// TRASH THIS
-OpenLayers.State = {
- /** states */
- UNKNOWN: 'Unknown',
- INSERT: 'Insert',
- UPDATE: 'Update',
- DELETE: 'Delete'
-};
-
-/**
- * @requires OpenLayers/Feature.js
- * @requires OpenLayers/Util.js
- */
-
-/**
- * Class: OpenLayers.Feature.Vector
- * Vector features use the OpenLayers.Geometry classes as geometry description.
- * They have an 'attributes' property, which is the data object, and a 'style'
- * property, the default values of which are defined in the
- * objects.
- *
- * Inherits from:
- * -
- */
-OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
-
- /**
- * Property: fid
- * {String}
- */
- fid: null,
-
- /**
- * APIProperty: geometry
- * {}
- */
- geometry: null,
-
- /**
- * APIProperty: attributes
- * {Object} This object holds arbitrary, serializable properties that
- * describe the feature.
- */
- attributes: null,
-
- /**
- * Property: bounds
- * {} The box bounding that feature's geometry, that
- * property can be set by an object when
- * deserializing the feature, so in most cases it represents an
- * information set by the server.
- */
- bounds: null,
-
- /**
- * Property: state
- * {String}
- */
- state: null,
-
- /**
- * APIProperty: style
- * {Object}
- */
- style: null,
-
- /**
- * APIProperty: url
- * {String} If this property is set it will be taken into account by
- * {} when upadting or deleting the feature.
- */
- url: null,
-
- /**
- * Property: renderIntent
- * {String} rendering intent currently being used
- */
- renderIntent: "default",
-
- /**
- * APIProperty: modified
- * {Object} An object with the originals of the geometry and attributes of
- * the feature, if they were changed. Currently this property is only read
- * by , and written by
- * , which sets the geometry property.
- * Applications can set the originals of modified attributes in the
- * attributes property. Note that applications have to check if this
- * object and the attributes property is already created before using it.
- * After a change made with ModifyFeature, this object could look like
- *
- * (code)
- * {
- * geometry: >Object
- * }
- * (end)
- *
- * When an application has made changes to feature attributes, it could
- * have set the attributes to something like this:
- *
- * (code)
- * {
- * attributes: {
- * myAttribute: "original"
- * }
- * }
- * (end)
- *
- * Note that only checks for truthy values in
- * *modified.geometry* and the attribute names in *modified.attributes*,
- * but it is recommended to set the original values (and not just true) as
- * attribute value, so applications could use this information to undo
- * changes.
- */
- modified: null,
-
- /**
- * Constructor: OpenLayers.Feature.Vector
- * Create a vector feature.
- *
- * Parameters:
- * geometry - {} The geometry that this feature
- * represents.
- * attributes - {Object} An optional object that will be mapped to the
- * property.
- * style - {Object} An optional style object.
- */
- initialize: function(geometry, attributes, style) {
- OpenLayers.Feature.prototype.initialize.apply(this,
- [null, null, attributes]);
- this.lonlat = null;
- this.geometry = geometry ? geometry : null;
- this.state = null;
- this.attributes = {};
- if (attributes) {
- this.attributes = OpenLayers.Util.extend(this.attributes,
- attributes);
- }
- this.style = style ? style : null;
- },
-
- /**
- * Method: destroy
- * nullify references to prevent circular references and memory leaks
- */
- destroy: function() {
- if (this.layer) {
- this.layer.removeFeatures(this);
- this.layer = null;
- }
-
- this.geometry = null;
- this.modified = null;
- OpenLayers.Feature.prototype.destroy.apply(this, arguments);
- },
-
- /**
- * Method: clone
- * Create a clone of this vector feature. Does not set any non-standard
- * properties.
- *
- * Returns:
- * {} An exact clone of this vector feature.
- */
- clone: function () {
- return new OpenLayers.Feature.Vector(
- this.geometry ? this.geometry.clone() : null,
- this.attributes,
- this.style);
- },
-
- /**
- * Method: onScreen
- * Determine whether the feature is within the map viewport. This method
- * tests for an intersection between the geometry and the viewport
- * bounds. If a more effecient but less precise geometry bounds
- * intersection is desired, call the method with the boundsOnly
- * parameter true.
- *
- * Parameters:
- * boundsOnly - {Boolean} Only test whether a feature's bounds intersects
- * the viewport bounds. Default is false. If false, the feature's
- * geometry must intersect the viewport for onScreen to return true.
- *
- * Returns:
- * {Boolean} The feature is currently visible on screen (optionally
- * based on its bounds if boundsOnly is true).
- */
- onScreen:function(boundsOnly) {
- var onScreen = false;
- if(this.layer && this.layer.map) {
- var screenBounds = this.layer.map.getExtent();
- if(boundsOnly) {
- var featureBounds = this.geometry.getBounds();
- onScreen = screenBounds.intersectsBounds(featureBounds);
- } else {
- var screenPoly = screenBounds.toGeometry();
- onScreen = screenPoly.intersects(this.geometry);
- }
- }
- return onScreen;
- },
-
- /**
- * Method: getVisibility
- * Determine whether the feature is displayed or not. It may not displayed
- * because:
- * - its style display property is set to 'none',
- * - it doesn't belong to any layer,
- * - the styleMap creates a symbolizer with display property set to 'none'
- * for it,
- * - the layer which it belongs to is not visible.
- *
- * Returns:
- * {Boolean} The feature is currently displayed.
- */
- getVisibility: function() {
- return !(this.style && this.style.display == 'none' ||
- !this.layer ||
- this.layer && this.layer.styleMap &&
- this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' ||
- this.layer && !this.layer.getVisibility());
- },
-
- /**
- * Method: createMarker
- * HACK - we need to decide if all vector features should be able to
- * create markers
- *
- * Returns:
- * {} For now just returns null
- */
- createMarker: function() {
- return null;
- },
-
- /**
- * Method: destroyMarker
- * HACK - we need to decide if all vector features should be able to
- * delete markers
- *
- * If user overrides the createMarker() function, s/he should be able
- * to also specify an alternative function for destroying it
- */
- destroyMarker: function() {
- // pass
- },
-
- /**
- * Method: createPopup
- * HACK - we need to decide if all vector features should be able to
- * create popups
- *
- * Returns:
- * {} For now just returns null
- */
- createPopup: function() {
- return null;
- },
-
- /**
- * Method: atPoint
- * Determins whether the feature intersects with the specified location.
- *
- * Parameters:
- * lonlat - {|Object} OpenLayers.LonLat or an
- * object with a 'lon' and 'lat' properties.
- * toleranceLon - {float} Optional tolerance in Geometric Coords
- * toleranceLat - {float} Optional tolerance in Geographic Coords
- *
- * Returns:
- * {Boolean} Whether or not the feature is at the specified location
- */
- atPoint: function(lonlat, toleranceLon, toleranceLat) {
- var atPoint = false;
- if(this.geometry) {
- atPoint = this.geometry.atPoint(lonlat, toleranceLon,
- toleranceLat);
- }
- return atPoint;
- },
-
- /**
- * Method: destroyPopup
- * HACK - we need to decide if all vector features should be able to
- * delete popups
- */
- destroyPopup: function() {
- // pass
- },
-
- /**
- * Method: move
- * Moves the feature and redraws it at its new location
- *
- * Parameters:
- * location - { or } the
- * location to which to move the feature.
- */
- move: function(location) {
-
- if(!this.layer || !this.geometry.move){
- //do nothing if no layer or immoveable geometry
- return undefined;
- }
-
- var pixel;
- if (location.CLASS_NAME == "OpenLayers.LonLat") {
- pixel = this.layer.getViewPortPxFromLonLat(location);
- } else {
- pixel = location;
- }
-
- var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());
- var res = this.layer.map.getResolution();
- this.geometry.move(res * (pixel.x - lastPixel.x),
- res * (lastPixel.y - pixel.y));
- this.layer.drawFeature(this);
- return lastPixel;
- },
-
- /**
- * Method: toState
- * Sets the new state
- *
- * Parameters:
- * state - {String}
- */
- toState: function(state) {
- if (state == OpenLayers.State.UPDATE) {
- switch (this.state) {
- case OpenLayers.State.UNKNOWN:
- case OpenLayers.State.DELETE:
- this.state = state;
- break;
- case OpenLayers.State.UPDATE:
- case OpenLayers.State.INSERT:
- break;
- }
- } else if (state == OpenLayers.State.INSERT) {
- switch (this.state) {
- case OpenLayers.State.UNKNOWN:
- break;
- default:
- this.state = state;
- break;
- }
- } else if (state == OpenLayers.State.DELETE) {
- switch (this.state) {
- case OpenLayers.State.INSERT:
- // the feature should be destroyed
- break;
- case OpenLayers.State.DELETE:
- break;
- case OpenLayers.State.UNKNOWN:
- case OpenLayers.State.UPDATE:
- this.state = state;
- break;
- }
- } else if (state == OpenLayers.State.UNKNOWN) {
- this.state = state;
- }
- },
-
- CLASS_NAME: "OpenLayers.Feature.Vector"
-});
-
-
-/**
- * Constant: OpenLayers.Feature.Vector.style
- * OpenLayers features can have a number of style attributes. The 'default'
- * style will typically be used if no other style is specified. These
- * styles correspond for the most part, to the styling properties defined
- * by the SVG standard.
- * Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties
- * Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties
- *
- * Symbolizer properties:
- * fill - {Boolean} Set to false if no fill is desired.
- * fillColor - {String} Hex fill color. Default is "#ee9900".
- * fillOpacity - {Number} Fill opacity (0-1). Default is 0.4
- * stroke - {Boolean} Set to false if no stroke is desired.
- * strokeColor - {String} Hex stroke color. Default is "#ee9900".
- * strokeOpacity - {Number} Stroke opacity (0-1). Default is 1.
- * strokeWidth - {Number} Pixel stroke width. Default is 1.
- * strokeLinecap - {String} Stroke cap type. Default is "round". [butt | round | square]
- * strokeDashstyle - {String} Stroke dash style. Default is "solid". [dot | dash | dashdot | longdash | longdashdot | solid]
- * graphic - {Boolean} Set to false if no graphic is desired.
- * pointRadius - {Number} Pixel point radius. Default is 6.
- * pointerEvents - {String} Default is "visiblePainted".
- * cursor - {String} Default is "".
- * externalGraphic - {String} Url to an external graphic that will be used for rendering points.
- * graphicWidth - {Number} Pixel width for sizing an external graphic.
- * graphicHeight - {Number} Pixel height for sizing an external graphic.
- * graphicOpacity - {Number} Opacity (0-1) for an external graphic.
- * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic.
- * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic.
- * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset).
- * graphicZIndex - {Number} The integer z-index value to use in rendering.
- * graphicName - {String} Named graphic to use when rendering points. Supported values include "circle" (default),
- * "square", "star", "x", "cross", "triangle".
- * graphicTitle - {String} Tooltip for an external graphic.
- * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.
- * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.
- * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.
- * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.
- * backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used.
- * backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used.
- * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either
- * fillText or mozDrawText to be available.
- * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string
- * composed of two characters. The first character is for the horizontal alignment, the second for the vertical
- * alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical
- * alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". Default is "cm".
- * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.
- * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.
- * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.
- * Default is false.
- * labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers.
- * labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the canvas & SVG renderers.
- * fontColor - {String} The font color for the label, to be provided like CSS.
- * fontOpacity - {Number} Opacity (0-1) for the label
- * fontFamily - {String} The font family for the label, to be provided like in CSS.
- * fontSize - {String} The font size for the label, to be provided like in CSS.
- * fontStyle - {String} The font style for the label, to be provided like in CSS.
- * fontWeight - {String} The font weight for the label, to be provided like in CSS.
- * display - {String} Symbolizers will have no effect if display is set to "none". All other values have no effect.
- */
-OpenLayers.Feature.Vector.style = {
- 'default': {
- fillColor: "#ee9900",
- fillOpacity: 0.4,
- hoverFillColor: "white",
- hoverFillOpacity: 0.8,
- strokeColor: "#ee9900",
- strokeOpacity: 1,
- strokeWidth: 1,
- strokeLinecap: "round",
- strokeDashstyle: "solid",
- hoverStrokeColor: "red",
- hoverStrokeOpacity: 1,
- hoverStrokeWidth: 0.2,
- pointRadius: 6,
- hoverPointRadius: 1,
- hoverPointUnit: "%",
- pointerEvents: "visiblePainted",
- cursor: "inherit",
- fontColor: "#000000",
- labelAlign: "cm",
- labelOutlineColor: "white",
- labelOutlineWidth: 3
- },
- 'select': {
- fillColor: "blue",
- fillOpacity: 0.4,
- hoverFillColor: "white",
- hoverFillOpacity: 0.8,
- strokeColor: "blue",
- strokeOpacity: 1,
- strokeWidth: 2,
- strokeLinecap: "round",
- strokeDashstyle: "solid",
- hoverStrokeColor: "red",
- hoverStrokeOpacity: 1,
- hoverStrokeWidth: 0.2,
- pointRadius: 6,
- hoverPointRadius: 1,
- hoverPointUnit: "%",
- pointerEvents: "visiblePainted",
- cursor: "pointer",
- fontColor: "#000000",
- labelAlign: "cm",
- labelOutlineColor: "white",
- labelOutlineWidth: 3
-
- },
- 'temporary': {
- fillColor: "#66cccc",
- fillOpacity: 0.2,
- hoverFillColor: "white",
- hoverFillOpacity: 0.8,
- strokeColor: "#66cccc",
- strokeOpacity: 1,
- strokeLinecap: "round",
- strokeWidth: 2,
- strokeDashstyle: "solid",
- hoverStrokeColor: "red",
- hoverStrokeOpacity: 1,
- hoverStrokeWidth: 0.2,
- pointRadius: 6,
- hoverPointRadius: 1,
- hoverPointUnit: "%",
- pointerEvents: "visiblePainted",
- cursor: "inherit",
- fontColor: "#000000",
- labelAlign: "cm",
- labelOutlineColor: "white",
- labelOutlineWidth: 3
-
- },
- 'delete': {
- display: "none"
- }
-};
-/* ======================================================================
- OpenLayers/Style.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-
-/**
- * @requires OpenLayers/BaseTypes/Class.js
- * @requires OpenLayers/Util.js
- * @requires OpenLayers/Feature/Vector.js
- */
-
-/**
- * Class: OpenLayers.Style
- * This class represents a UserStyle obtained
- * from a SLD, containing styling rules.
- */
-OpenLayers.Style = OpenLayers.Class({
-
- /**
- * Property: id
- * {String} A unique id for this session.
- */
- id: null,
-
- /**
- * APIProperty: name
- * {String}
- */
- name: null,
-
- /**
- * Property: title
- * {String} Title of this style (set if included in SLD)
- */
- title: null,
-
- /**
- * Property: description
- * {String} Description of this style (set if abstract is included in SLD)
- */
- description: null,
-
- /**
- * APIProperty: layerName
- * {} name of the layer that this style belongs to, usually
- * according to the NamedLayer attribute of an SLD document.
- */
- layerName: null,
-
- /**
- * APIProperty: isDefault
- * {Boolean}
- */
- isDefault: false,
-
- /**
- * Property: rules
- * {Array()}
- */
- rules: null,
-
- /**
- * Property: context
- * {Object} An optional object with properties that symbolizers' property
- * values should be evaluated against. If no context is specified,
- * feature.attributes will be used
- */
- context: null,
-
- /**
- * Property: defaultStyle
- * {Object} hash of style properties to use as default for merging
- * rule-based style symbolizers onto. If no rules are defined,
- * createSymbolizer will return this style. If is set to
- * true, the defaultStyle will only be taken into account if there are
- * rules defined.
- */
- defaultStyle: null,
-
- /**
- * Property: defaultsPerSymbolizer
- * {Boolean} If set to true, the will extend the symbolizer
- * of every rule. Properties of the will also be used to set
- * missing symbolizer properties if the symbolizer has stroke, fill or
- * graphic set to true. Default is false.
- */
- defaultsPerSymbolizer: false,
-
- /**
- * Property: propertyStyles
- * {Hash of Boolean} cache of style properties that need to be parsed for
- * propertyNames. Property names are keys, values won't be used.
- */
- propertyStyles: null,
-
-
- /**
- * Constructor: OpenLayers.Style
- * Creates a UserStyle.
- *
- * Parameters:
- * style - {Object} Optional hash of style properties that will be
- * used as default style for this style object. This style
- * applies if no rules are specified. Symbolizers defined in
- * rules will extend this default style.
- * options - {Object} An optional object with properties to set on the
- * style.
- *
- * Valid options:
- * rules - {Array()} List of rules to be added to the
- * style.
- *
- * Returns:
- * {}
- */
- initialize: function(style, options) {
-
- OpenLayers.Util.extend(this, options);
- this.rules = [];
- if(options && options.rules) {
- this.addRules(options.rules);
- }
-
- // use the default style from OpenLayers.Feature.Vector if no style
- // was given in the constructor
- this.setDefaultStyle(style ||
- OpenLayers.Feature.Vector.style["default"]);
-
- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
- },
-
- /**
- * APIMethod: destroy
- * nullify references to prevent circular references and memory leaks
- */
- destroy: function() {
- for (var i=0, len=this.rules.length; i} feature to evaluate rules for
- *
- * Returns:
- * {Object} symbolizer hash
- */
- createSymbolizer: function(feature) {
- var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(
- OpenLayers.Util.extend({}, this.defaultStyle), feature);
-
- var rules = this.rules;
-
- var rule, context;
- var elseRules = [];
- var appliedRules = false;
- for(var i=0, len=rules.length; i 0) {
- appliedRules = true;
- for(var i=0, len=elseRules.length; i 0 && appliedRules == false) {
- style.display = "none";
- }
-
- if (style.label != null && typeof style.label !== "string") {
- style.label = String(style.label);
- }
-
- return style;
- },
-
- /**
- * Method: applySymbolizer
- *
- * Parameters:
- * rule - {}
- * style - {Object}
- * feature - {}
- *
- * Returns:
- * {Object} A style with new symbolizer applied.
- */
- applySymbolizer: function(rule, style, feature) {
- var symbolizerPrefix = feature.geometry ?
- this.getSymbolizerPrefix(feature.geometry) :
- OpenLayers.Style.SYMBOLIZER_PREFIXES[0];
-
- var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;
-
- if(this.defaultsPerSymbolizer === true) {
- var defaults = this.defaultStyle;
- OpenLayers.Util.applyDefaults(symbolizer, {
- pointRadius: defaults.pointRadius
- });
- if(symbolizer.stroke === true || symbolizer.graphic === true) {
- OpenLayers.Util.applyDefaults(symbolizer, {
- strokeWidth: defaults.strokeWidth,
- strokeColor: defaults.strokeColor,
- strokeOpacity: defaults.strokeOpacity,
- strokeDashstyle: defaults.strokeDashstyle,
- strokeLinecap: defaults.strokeLinecap
- });
- }
- if(symbolizer.fill === true || symbolizer.graphic === true) {
- OpenLayers.Util.applyDefaults(symbolizer, {
- fillColor: defaults.fillColor,
- fillOpacity: defaults.fillOpacity
- });
- }
- if(symbolizer.graphic === true) {
- OpenLayers.Util.applyDefaults(symbolizer, {
- pointRadius: this.defaultStyle.pointRadius,
- externalGraphic: this.defaultStyle.externalGraphic,
- graphicName: this.defaultStyle.graphicName,
- graphicOpacity: this.defaultStyle.graphicOpacity,
- graphicWidth: this.defaultStyle.graphicWidth,
- graphicHeight: this.defaultStyle.graphicHeight,
- graphicXOffset: this.defaultStyle.graphicXOffset,
- graphicYOffset: this.defaultStyle.graphicYOffset
- });
- }
- }
-
- // merge the style with the current style
- return this.createLiterals(
- OpenLayers.Util.extend(style, symbolizer), feature);
- },
-
- /**
- * Method: createLiterals
- * creates literals for all style properties that have an entry in
- * .
- *
- * Parameters:
- * style - {Object} style to create literals for. Will be modified
- * inline.
- * feature - {Object}
- *
- * Returns:
- * {Object} the modified style
- */
- createLiterals: function(style, feature) {
- var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);
- OpenLayers.Util.extend(context, this.context);
-
- for (var i in this.propertyStyles) {
- style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);
- }
- return style;
- },
-
- /**
- * Method: findPropertyStyles
- * Looks into all rules for this style and the defaultStyle to collect
- * all the style hash property names containing ${...} strings that have
- * to be replaced using the createLiteral method before returning them.
- *
- * Returns:
- * {Object} hash of property names that need createLiteral parsing. The
- * name of the property is the key, and the value is true;
- */
- findPropertyStyles: function() {
- var propertyStyles = {};
-
- // check the default style
- var style = this.defaultStyle;
- this.addPropertyStyles(propertyStyles, style);
-
- // walk through all rules to check for properties in their symbolizer
- var rules = this.rules;
- var symbolizer, value;
- for (var i=0, len=rules.length; i)}
- */
- addRules: function(rules) {
- Array.prototype.push.apply(this.rules, rules);
- this.propertyStyles = this.findPropertyStyles();
- },
-
- /**
- * APIMethod: setDefaultStyle
- * Sets the default style for this style object.
- *
- * Parameters:
- * style - {Object} Hash of style properties
- */
- setDefaultStyle: function(style) {
- this.defaultStyle = style;
- this.propertyStyles = this.findPropertyStyles();
- },
-
- /**
- * Method: getSymbolizerPrefix
- * Returns the correct symbolizer prefix according to the
- * geometry type of the passed geometry
- *
- * Parameters:
- * geometry - {}
- *
- * Returns:
- * {String} key of the according symbolizer
- */
- getSymbolizerPrefix: function(geometry) {
- var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;
- for (var i=0, len=prefixes.length; i} Clone of this style.
- */
- clone: function() {
- var options = OpenLayers.Util.extend({}, this);
- // clone rules
- if(this.rules) {
- options.rules = [];
- for(var i=0, len=this.rules.length; i} optional feature to pass to
- * for evaluating functions in the
- * context.
- * property - {String} optional, name of the property for which the literal is
- * being created for evaluating functions in the context.
- *
- * Returns:
- * {String} the parsed value. In the example of the value parameter above, the
- * result would be "foo valueOfBar", assuming that the passed feature has an
- * attribute named "bar" with the value "valueOfBar".
- */
-OpenLayers.Style.createLiteral = function(value, context, feature, property) {
- if (typeof value == "string" && value.indexOf("${") != -1) {
- value = OpenLayers.String.format(value, context, [feature, property]);
- value = (isNaN(value) || !value) ? value : parseFloat(value);
- }
- return value;
-};
-
-/**
- * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES
- * {Array} prefixes of the sld symbolizers. These are the
- * same as the main geometry types
- */
-OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',
- 'Raster'];
-/* ======================================================================
- OpenLayers/Renderer.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/BaseTypes/Class.js
- */
-
-/**
- * Class: OpenLayers.Renderer
- * This is the base class for all renderers.
- *
- * This is based on a merger code written by Paul Spencer and Bertil Chapuis.
- * It is largely composed of virtual functions that are to be implemented
- * in technology-specific subclasses, but there is some generic code too.
- *
- * The functions that *are* implemented here merely deal with the maintenance
- * of the size and extent variables, as well as the cached 'resolution'
- * value.
- *
- * A note to the user that all subclasses should use getResolution() instead
- * of directly accessing this.resolution in order to correctly use the
- * cacheing system.
- *
- */
-OpenLayers.Renderer = OpenLayers.Class({
-
- /**
- * Property: container
- * {DOMElement}
- */
- container: null,
-
- /**
- * Property: root
- * {DOMElement}
- */
- root: null,
-
- /**
- * Property: extent
- * {}
- */
- extent: null,
-
- /**
- * Property: locked
- * {Boolean} If the renderer is currently in a state where many things
- * are changing, the 'locked' property is set to true. This means
- * that renderers can expect at least one more drawFeature event to be
- * called with the 'locked' property set to 'true': In some renderers,
- * this might make sense to use as a 'only update local information'
- * flag.
- */
- locked: false,
-
- /**
- * Property: size
- * {}
- */
- size: null,
-
- /**
- * Property: resolution
- * {Float} cache of current map resolution
- */
- resolution: null,
-
- /**
- * Property: map
- * {} Reference to the map -- this is set in Vector's setMap()
- */
- map: null,
-
- /**
- * Property: featureDx
- * {Number} Feature offset in x direction. Will be calculated for and
- * applied to the current feature while rendering (see
- * ).
- */
- featureDx: 0,
-
- /**
- * Constructor: OpenLayers.Renderer
- *
- * Parameters:
- * containerID - {}
- * options - {Object} options for this renderer. See sublcasses for
- * supported options.
- */
- initialize: function(containerID, options) {
- this.container = OpenLayers.Util.getElement(containerID);
- OpenLayers.Util.extend(this, options);
- },
-
- /**
- * APIMethod: destroy
- */
- destroy: function() {
- this.container = null;
- this.extent = null;
- this.size = null;
- this.resolution = null;
- this.map = null;
- },
-
- /**
- * APIMethod: supported
- * This should be overridden by specific subclasses
- *
- * Returns:
- * {Boolean} Whether or not the browser supports the renderer class
- */
- supported: function() {
- return false;
- },
-
- /**
- * Method: setExtent
- * Set the visible part of the layer.
- *
- * Resolution has probably changed, so we nullify the resolution
- * cache (this.resolution) -- this way it will be re-computed when
- * next it is needed.
- * We nullify the resolution cache (this.resolution) if resolutionChanged
- * is set to true - this way it will be re-computed on the next
- * getResolution() request.
- *
- * Parameters:
- * extent - {}
- * resolutionChanged - {Boolean}
- *
- * Returns:
- * {Boolean} true to notify the layer that the new extent does not exceed
- * the coordinate range, and the features will not need to be redrawn.
- * False otherwise.
- */
- setExtent: function(extent, resolutionChanged) {
- this.extent = extent.clone();
- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
- var ratio = extent.getWidth() / this.map.getExtent().getWidth(),
- extent = extent.scale(1 / ratio);
- this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio);
- }
- if (resolutionChanged) {
- this.resolution = null;
- }
- return true;
- },
-
- /**
- * Method: setSize
- * Sets the size of the drawing surface.
- *
- * Resolution has probably changed, so we nullify the resolution
- * cache (this.resolution) -- this way it will be re-computed when
- * next it is needed.
- *
- * Parameters:
- * size - {}
- */
- setSize: function(size) {
- this.size = size.clone();
- this.resolution = null;
- },
-
- /**
- * Method: getResolution
- * Uses cached copy of resolution if available to minimize computing
- *
- * Returns:
- * {Float} The current map's resolution
- */
- getResolution: function() {
- this.resolution = this.resolution || this.map.getResolution();
- return this.resolution;
- },
-
- /**
- * Method: drawFeature
- * Draw the feature. The optional style argument can be used
- * to override the feature's own style. This method should only
- * be called from layer.drawFeature().
- *
- * Parameters:
- * feature - {}
- * style - {}
- *
- * Returns:
- * {Boolean} true if the feature has been drawn completely, false if not,
- * undefined if the feature had no geometry
- */
- drawFeature: function(feature, style) {
- if(style == null) {
- style = feature.style;
- }
- if (feature.geometry) {
- var bounds = feature.geometry.getBounds();
- if(bounds) {
- var worldBounds;
- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
- worldBounds = this.map.getMaxExtent();
- }
- if (!bounds.intersectsBounds(this.extent, {worldBounds: worldBounds})) {
- style = {display: "none"};
- } else {
- this.calculateFeatureDx(bounds, worldBounds);
- }
- var rendered = this.drawGeometry(feature.geometry, style, feature.id);
- if(style.display != "none" && style.label && rendered !== false) {
-
- var location = feature.geometry.getCentroid();
- if(style.labelXOffset || style.labelYOffset) {
- var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
- var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
- var res = this.getResolution();
- location.move(xOffset*res, yOffset*res);
- }
- this.drawText(feature.id, style, location);
- } else {
- this.removeText(feature.id);
- }
- return rendered;
- }
- }
- },
-
- /**
- * Method: calculateFeatureDx
- * {Number} Calculates the feature offset in x direction. Looking at the
- * center of the feature bounds and the renderer extent, we calculate how
- * many world widths the two are away from each other. This distance is
- * used to shift the feature as close as possible to the center of the
- * current enderer extent, which ensures that the feature is visible in the
- * current viewport.
- *
- * Parameters:
- * bounds - {} Bounds of the feature
- * worldBounds - {} Bounds of the world
- */
- calculateFeatureDx: function(bounds, worldBounds) {
- this.featureDx = 0;
- if (worldBounds) {
- var worldWidth = worldBounds.getWidth(),
- rendererCenterX = (this.extent.left + this.extent.right) / 2,
- featureCenterX = (bounds.left + bounds.right) / 2,
- worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);
- this.featureDx = worldsAway * worldWidth;
- }
- },
-
- /**
- * Method: drawGeometry
- *
- * Draw a geometry. This should only be called from the renderer itself.
- * Use layer.drawFeature() from outside the renderer.
- * virtual function
- *
- * Parameters:
- * geometry - {}
- * style - {Object}
- * featureId - {}
- */
- drawGeometry: function(geometry, style, featureId) {},
-
- /**
- * Method: drawText
- * Function for drawing text labels.
- * This method is only called by the renderer itself.
- *
- * Parameters:
- * featureId - {String}
- * style -
- * location - {}
- */
- drawText: function(featureId, style, location) {},
-
- /**
- * Method: removeText
- * Function for removing text labels.
- * This method is only called by the renderer itself.
- *
- * Parameters:
- * featureId - {String}
- */
- removeText: function(featureId) {},
-
- /**
- * Method: clear
- * Clear all vectors from the renderer.
- * virtual function.
- */
- clear: function() {},
-
- /**
- * Method: getFeatureIdFromEvent
- * Returns a feature id from an event on the renderer.
- * How this happens is specific to the renderer. This should be
- * called from layer.getFeatureFromEvent().
- * Virtual function.
- *
- * Parameters:
- * evt - {}
- *
- * Returns:
- * {String} A feature id or undefined.
- */
- getFeatureIdFromEvent: function(evt) {},
-
- /**
- * Method: eraseFeatures
- * This is called by the layer to erase features
- *
- * Parameters:
- * features - {Array()}
- */
- eraseFeatures: function(features) {
- if(!(OpenLayers.Util.isArray(features))) {
- features = [features];
- }
- for(var i=0, len=features.length; i}
- * featureId - {String}
- */
- eraseGeometry: function(geometry, featureId) {},
-
- /**
- * Method: moveRoot
- * moves this renderer's root to a (different) renderer.
- * To be implemented by subclasses that require a common renderer root for
- * feature selection.
- *
- * Parameters:
- * renderer - {} target renderer for the moved root
- */
- moveRoot: function(renderer) {},
-
- /**
- * Method: getRenderLayerId
- * Gets the layer that this renderer's output appears on. If moveRoot was
- * used, this will be different from the id of the layer containing the
- * features rendered by this renderer.
- *
- * Returns:
- * {String} the id of the output layer.
- */
- getRenderLayerId: function() {
- return this.container.id;
- },
-
- /**
- * Method: applyDefaultSymbolizer
- *
- * Parameters:
- * symbolizer - {Object}
- *
- * Returns:
- * {Object}
- */
- applyDefaultSymbolizer: function(symbolizer) {
- var result = OpenLayers.Util.extend({},
- OpenLayers.Renderer.defaultSymbolizer);
- if(symbolizer.stroke === false) {
- delete result.strokeWidth;
- delete result.strokeColor;
- }
- if(symbolizer.fill === false) {
- delete result.fillColor;
- }
- OpenLayers.Util.extend(result, symbolizer);
- return result;
- },
-
- CLASS_NAME: "OpenLayers.Renderer"
-});
-
-/**
- * Constant: OpenLayers.Renderer.defaultSymbolizer
- * {Object} Properties from this symbolizer will be applied to symbolizers
- * with missing properties. This can also be used to set a global
- * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the
- * following code before rendering any vector features:
- * (code)
- * OpenLayers.Renderer.defaultSymbolizer = {
- * fillColor: "#808080",
- * fillOpacity: 1,
- * strokeColor: "#000000",
- * strokeOpacity: 1,
- * strokeWidth: 1,
- * pointRadius: 3,
- * graphicName: "square"
- * };
- * (end)
- */
-OpenLayers.Renderer.defaultSymbolizer = {
- fillColor: "#000000",
- strokeColor: "#000000",
- strokeWidth: 2,
- fillOpacity: 1,
- strokeOpacity: 1,
- pointRadius: 0,
- labelAlign: 'cm'
-};
-
-
-
-/**
- * Constant: OpenLayers.Renderer.symbol
- * Coordinate arrays for well known (named) symbols.
- */
-OpenLayers.Renderer.symbol = {
- "star": [350,75, 379,161, 469,161, 397,215, 423,301, 350,250, 277,301,
- 303,215, 231,161, 321,161, 350,75],
- "cross": [4,0, 6,0, 6,4, 10,4, 10,6, 6,6, 6,10, 4,10, 4,6, 0,6, 0,4, 4,4,
- 4,0],
- "x": [0,0, 25,0, 50,35, 75,0, 100,0, 65,50, 100,100, 75,100, 50,65, 25,100, 0,100, 35,50, 0,0],
- "square": [0,0, 0,1, 1,1, 1,0, 0,0],
- "triangle": [0,10, 10,10, 5,0, 0,10]
-};
-/* ======================================================================
- OpenLayers/Renderer/Elements.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Renderer.js
- */
-
-/**
- * Class: OpenLayers.ElementsIndexer
- * This class takes care of figuring out which order elements should be
- * placed in the DOM based on given indexing methods.
- */
-OpenLayers.ElementsIndexer = OpenLayers.Class({
-
- /**
- * Property: maxZIndex
- * {Integer} This is the largest-most z-index value for a node
- * contained within the indexer.
- */
- maxZIndex: null,
-
- /**
- * Property: order
- * {Array} This is an array of node id's stored in the
- * order that they should show up on screen. Id's higher up in the
- * array (higher array index) represent nodes with higher z-indeces.
- */
- order: null,
-
- /**
- * Property: indices
- * {Object} This is a hash that maps node ids to their z-index value
- * stored in the indexer. This is done to make finding a nodes z-index
- * value O(1).
- */
- indices: null,
-
- /**
- * Property: compare
- * {Function} This is the function used to determine placement of
- * of a new node within the indexer. If null, this defaults to to
- * the Z_ORDER_DRAWING_ORDER comparison method.
- */
- compare: null,
-
- /**
- * APIMethod: initialize
- * Create a new indexer with
- *
- * Parameters:
- * yOrdering - {Boolean} Whether to use y-ordering.
- */
- initialize: function(yOrdering) {
-
- this.compare = yOrdering ?
- OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :
- OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;
-
- this.clear();
- },
-
- /**
- * APIMethod: insert
- * Insert a new node into the indexer. In order to find the correct
- * positioning for the node to be inserted, this method uses a binary
- * search. This makes inserting O(log(n)).
- *
- * Parameters:
- * newNode - {DOMElement} The new node to be inserted.
- *
- * Returns
- * {DOMElement} the node before which we should insert our newNode, or
- * null if newNode can just be appended.
- */
- insert: function(newNode) {
- // If the node is known to the indexer, remove it so we can
- // recalculate where it should go.
- if (this.exists(newNode)) {
- this.remove(newNode);
- }
-
- var nodeId = newNode.id;
-
- this.determineZIndex(newNode);
-
- var leftIndex = -1;
- var rightIndex = this.order.length;
- var middle;
-
- while (rightIndex - leftIndex > 1) {
- middle = parseInt((leftIndex + rightIndex) / 2);
-
- var placement = this.compare(this, newNode,
- OpenLayers.Util.getElement(this.order[middle]));
-
- if (placement > 0) {
- leftIndex = middle;
- } else {
- rightIndex = middle;
- }
- }
-
- this.order.splice(rightIndex, 0, nodeId);
- this.indices[nodeId] = this.getZIndex(newNode);
-
- // If the new node should be before another in the index
- // order, return the node before which we have to insert the new one;
- // else, return null to indicate that the new node can be appended.
- return this.getNextElement(rightIndex);
- },
-
- /**
- * APIMethod: remove
- *
- * Parameters:
- * node - {DOMElement} The node to be removed.
- */
- remove: function(node) {
- var nodeId = node.id;
- var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);
- if (arrayIndex >= 0) {
- // Remove it from the order array, as well as deleting the node
- // from the indeces hash.
- this.order.splice(arrayIndex, 1);
- delete this.indices[nodeId];
-
- // Reset the maxium z-index based on the last item in the
- // order array.
- if (this.order.length > 0) {
- var lastId = this.order[this.order.length - 1];
- this.maxZIndex = this.indices[lastId];
- } else {
- this.maxZIndex = 0;
- }
- }
- },
-
- /**
- * APIMethod: clear
- */
- clear: function() {
- this.order = [];
- this.indices = {};
- this.maxZIndex = 0;
- },
-
- /**
- * APIMethod: exists
- *
- * Parameters:
- * node - {DOMElement} The node to test for existence.
- *
- * Returns:
- * {Boolean} Whether or not the node exists in the indexer?
- */
- exists: function(node) {
- return (this.indices[node.id] != null);
- },
-
- /**
- * APIMethod: getZIndex
- * Get the z-index value for the current node from the node data itself.
- *
- * Parameters:
- * node - {DOMElement} The node whose z-index to get.
- *
- * Returns:
- * {Integer} The z-index value for the specified node (from the node
- * data itself).
- */
- getZIndex: function(node) {
- return node._style.graphicZIndex;
- },
-
- /**
- * Method: determineZIndex
- * Determine the z-index for the current node if there isn't one,
- * and set the maximum value if we've found a new maximum.
- *
- * Parameters:
- * node - {DOMElement}
- */
- determineZIndex: function(node) {
- var zIndex = node._style.graphicZIndex;
-
- // Everything must have a zIndex. If none is specified,
- // this means the user *must* (hint: assumption) want this
- // node to succomb to drawing order. To enforce drawing order
- // over all indexing methods, we'll create a new z-index that's
- // greater than any currently in the indexer.
- if (zIndex == null) {
- zIndex = this.maxZIndex;
- node._style.graphicZIndex = zIndex;
- } else if (zIndex > this.maxZIndex) {
- this.maxZIndex = zIndex;
- }
- },
-
- /**
- * APIMethod: getNextElement
- * Get the next element in the order stack.
- *
- * Parameters:
- * index - {Integer} The index of the current node in this.order.
- *
- * Returns:
- * {DOMElement} the node following the index passed in, or
- * null.
- */
- getNextElement: function(index) {
- var nextIndex = index + 1;
- if (nextIndex < this.order.length) {
- var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);
- if (nextElement == undefined) {
- nextElement = this.getNextElement(nextIndex);
- }
- return nextElement;
- } else {
- return null;
- }
- },
-
- CLASS_NAME: "OpenLayers.ElementsIndexer"
-});
-
-/**
- * Namespace: OpenLayers.ElementsIndexer.IndexingMethods
- * These are the compare methods for figuring out where a new node should be
- * placed within the indexer. These methods are very similar to general
- * sorting methods in that they return -1, 0, and 1 to specify the
- * direction in which new nodes fall in the ordering.
- */
-OpenLayers.ElementsIndexer.IndexingMethods = {
-
- /**
- * Method: Z_ORDER
- * This compare method is used by other comparison methods.
- * It can be used individually for ordering, but is not recommended,
- * because it doesn't subscribe to drawing order.
- *
- * Parameters:
- * indexer - {}
- * newNode - {DOMElement}
- * nextNode - {DOMElement}
- *
- * Returns:
- * {Integer}
- */
- Z_ORDER: function(indexer, newNode, nextNode) {
- var newZIndex = indexer.getZIndex(newNode);
-
- var returnVal = 0;
- if (nextNode) {
- var nextZIndex = indexer.getZIndex(nextNode);
- returnVal = newZIndex - nextZIndex;
- }
-
- return returnVal;
- },
-
- /**
- * APIMethod: Z_ORDER_DRAWING_ORDER
- * This method orders nodes by their z-index, but does so in a way
- * that, if there are other nodes with the same z-index, the newest
- * drawn will be the front most within that z-index. This is the
- * default indexing method.
- *
- * Parameters:
- * indexer - {}
- * newNode - {DOMElement}
- * nextNode - {DOMElement}
- *
- * Returns:
- * {Integer}
- */
- Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {
- var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
- indexer,
- newNode,
- nextNode
- );
-
- // Make Z_ORDER subscribe to drawing order by pushing it above
- // all of the other nodes with the same z-index.
- if (nextNode && returnVal == 0) {
- returnVal = 1;
- }
-
- return returnVal;
- },
-
- /**
- * APIMethod: Z_ORDER_Y_ORDER
- * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it
- * best describes which ordering methods have precedence (though, the
- * name would be too long). This method orders nodes by their z-index,
- * but does so in a way that, if there are other nodes with the same
- * z-index, the nodes with the lower y position will be "closer" than
- * those with a higher y position. If two nodes have the exact same y
- * position, however, then this method will revert to using drawing
- * order to decide placement.
- *
- * Parameters:
- * indexer - {}
- * newNode - {DOMElement}
- * nextNode - {DOMElement}
- *
- * Returns:
- * {Integer}
- */
- Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {
- var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
- indexer,
- newNode,
- nextNode
- );
-
- if (nextNode && returnVal === 0) {
- var result = nextNode._boundsBottom - newNode._boundsBottom;
- returnVal = (result === 0) ? 1 : result;
- }
-
- return returnVal;
- }
-};
-
-/**
- * Class: OpenLayers.Renderer.Elements
- * This is another virtual class in that it should never be instantiated by
- * itself as a Renderer. It exists because there is *tons* of shared
- * functionality between different vector libraries which use nodes/elements
- * as a base for rendering vectors.
- *
- * The highlevel bits of code that are implemented here are the adding and
- * removing of geometries, which is essentially the same for any
- * element-based renderer. The details of creating each node and drawing the
- * paths are of course different, but the machinery is the same.
- *
- * Inherits:
- * -
- */
-OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
-
- /**
- * Property: rendererRoot
- * {DOMElement}
- */
- rendererRoot: null,
-
- /**
- * Property: root
- * {DOMElement}
- */
- root: null,
-
- /**
- * Property: vectorRoot
- * {DOMElement}
- */
- vectorRoot: null,
-
- /**
- * Property: textRoot
- * {DOMElement}
- */
- textRoot: null,
-
- /**
- * Property: xmlns
- * {String}
- */
- xmlns: null,
-
- /**
- * Property: xOffset
- * {Number} Offset to apply to the renderer viewport translation in x
- * direction. If the renderer extent's center is on the right of the
- * dateline (i.e. exceeds the world bounds), we shift the viewport to the
- * left by one world width. This avoids that features disappear from the
- * map viewport. Because our dateline handling logic in other places
- * ensures that extents crossing the dateline always have a center
- * exceeding the world bounds on the left, we need this offset to make sure
- * that the same is true for the renderer extent in pixel space as well.
- */
- xOffset: 0,
-
- /**
- * Property: rightOfDateLine
- * {Boolean} Keeps track of the location of the map extent relative to the
- * date line. The method compares this value (which is the one
- * from the previous call) with the current position of the map
- * extent relative to the date line and updates the xOffset when the extent
- * has moved from one side of the date line to the other.
- */
-
- /**
- * Property: Indexer
- * {} An instance of OpenLayers.ElementsIndexer
- * created upon initialization if the zIndexing or yOrdering options
- * passed to this renderer's constructor are set to true.
- */
- indexer: null,
-
- /**
- * Constant: BACKGROUND_ID_SUFFIX
- * {String}
- */
- BACKGROUND_ID_SUFFIX: "_background",
-
- /**
- * Constant: LABEL_ID_SUFFIX
- * {String}
- */
- LABEL_ID_SUFFIX: "_label",
-
- /**
- * Constant: LABEL_OUTLINE_SUFFIX
- * {String}
- */
- LABEL_OUTLINE_SUFFIX: "_outline",
-
- /**
- * Constructor: OpenLayers.Renderer.Elements
- *
- * Parameters:
- * containerID - {String}
- * options - {Object} options for this renderer.
- *
- * Supported options are:
- * yOrdering - {Boolean} Whether to use y-ordering
- * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
- * if yOrdering is set to true.
- */
- initialize: function(containerID, options) {
- OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
-
- this.rendererRoot = this.createRenderRoot();
- this.root = this.createRoot("_root");
- this.vectorRoot = this.createRoot("_vroot");
- this.textRoot = this.createRoot("_troot");
-
- this.root.appendChild(this.vectorRoot);
- this.root.appendChild(this.textRoot);
-
- this.rendererRoot.appendChild(this.root);
- this.container.appendChild(this.rendererRoot);
-
- if(options && (options.zIndexing || options.yOrdering)) {
- this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);
- }
- },
-
- /**
- * Method: destroy
- */
- destroy: function() {
-
- this.clear();
-
- this.rendererRoot = null;
- this.root = null;
- this.xmlns = null;
-
- OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
- },
-
- /**
- * Method: clear
- * Remove all the elements from the root
- */
- clear: function() {
- var child;
- var root = this.vectorRoot;
- if (root) {
- while (child = root.firstChild) {
- root.removeChild(child);
- }
- }
- root = this.textRoot;
- if (root) {
- while (child = root.firstChild) {
- root.removeChild(child);
- }
- }
- if (this.indexer) {
- this.indexer.clear();
- }
- },
-
- /**
- * Method: setExtent
- * Set the visible part of the layer.
- *
- * Parameters:
- * extent - {}
- * resolutionChanged - {Boolean}
- *
- * Returns:
- * {Boolean} true to notify the layer that the new extent does not exceed
- * the coordinate range, and the features will not need to be redrawn.
- * False otherwise.
- */
- setExtent: function(extent, resolutionChanged) {
- var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);
- var resolution = this.getResolution();
- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
- var rightOfDateLine,
- ratio = extent.getWidth() / this.map.getExtent().getWidth(),
- extent = extent.scale(1 / ratio),
- world = this.map.getMaxExtent();
- if (world.right > extent.left && world.right < extent.right) {
- rightOfDateLine = true;
- } else if (world.left > extent.left && world.left < extent.right) {
- rightOfDateLine = false;
- }
- if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) {
- coordSysUnchanged = false;
- this.xOffset = rightOfDateLine === true ?
- world.getWidth() / resolution : 0;
- }
- this.rightOfDateLine = rightOfDateLine;
- }
- return coordSysUnchanged;
- },
-
- /**
- * Method: getNodeType
- * This function is in charge of asking the specific renderer which type
- * of node to create for the given geometry and style. All geometries
- * in an Elements-based renderer consist of one node and some
- * attributes. We have the nodeFactory() function which creates a node
- * for us, but it takes a 'type' as input, and that is precisely what
- * this function tells us.
- *
- * Parameters:
- * geometry - {}
- * style - {Object}
- *
- * Returns:
- * {String} The corresponding node type for the specified geometry
- */
- getNodeType: function(geometry, style) { },
-
- /**
- * Method: drawGeometry
- * Draw the geometry, creating new nodes, setting paths, setting style,
- * setting featureId on the node. This method should only be called
- * by the renderer itself.
- *
- * Parameters:
- * geometry - {}
- * style - {Object}
- * featureId - {String}
- *
- * Returns:
- * {Boolean} true if the geometry has been drawn completely; null if
- * incomplete; false otherwise
- */
- drawGeometry: function(geometry, style, featureId) {
- var className = geometry.CLASS_NAME;
- var rendered = true;
- if ((className == "OpenLayers.Geometry.Collection") ||
- (className == "OpenLayers.Geometry.MultiPoint") ||
- (className == "OpenLayers.Geometry.MultiLineString") ||
- (className == "OpenLayers.Geometry.MultiPolygon")) {
- for (var i = 0, len=geometry.components.length; i}
- * style - {Object}
- * featureId - {String}
- *
- * Returns:
- * {Boolean} true if the complete geometry could be drawn, null if parts of
- * the geometry could not be drawn, false otherwise
- */
- redrawNode: function(id, geometry, style, featureId) {
- style = this.applyDefaultSymbolizer(style);
- // Get the node if it's already on the map.
- var node = this.nodeFactory(id, this.getNodeType(geometry, style));
-
- // Set the data for the node, then draw it.
- node._featureId = featureId;
- node._boundsBottom = geometry.getBounds().bottom;
- node._geometryClass = geometry.CLASS_NAME;
- node._style = style;
-
- var drawResult = this.drawGeometryNode(node, geometry, style);
- if(drawResult === false) {
- return false;
- }
-
- node = drawResult.node;
-
- // Insert the node into the indexer so it can show us where to
- // place it. Note that this operation is O(log(n)). If there's a
- // performance problem (when dragging, for instance) this is
- // likely where it would be.
- if (this.indexer) {
- var insert = this.indexer.insert(node);
- if (insert) {
- this.vectorRoot.insertBefore(node, insert);
- } else {
- this.vectorRoot.appendChild(node);
- }
- } else {
- // if there's no indexer, simply append the node to root,
- // but only if the node is a new one
- if (node.parentNode !== this.vectorRoot){
- this.vectorRoot.appendChild(node);
- }
- }
-
- this.postDraw(node);
-
- return drawResult.complete;
- },
-
- /**
- * Method: redrawBackgroundNode
- * Redraws the node using special 'background' style properties. Basically
- * just calls redrawNode(), but instead of directly using the
- * 'externalGraphic', 'graphicXOffset', 'graphicYOffset', and
- * 'graphicZIndex' properties directly from the specified 'style'
- * parameter, we create a new style object and set those properties
- * from the corresponding 'background'-prefixed properties from
- * specified 'style' parameter.
- *
- * Parameters:
- * id - {String}
- * geometry - {}
- * style - {Object}
- * featureId - {String}
- *
- * Returns:
- * {Boolean} true if the complete geometry could be drawn, null if parts of
- * the geometry could not be drawn, false otherwise
- */
- redrawBackgroundNode: function(id, geometry, style, featureId) {
- var backgroundStyle = OpenLayers.Util.extend({}, style);
-
- // Set regular style attributes to apply to the background styles.
- backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;
- backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;
- backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;
- backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;
- backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;
- backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;
-
- // Erase background styles.
- backgroundStyle.backgroundGraphic = null;
- backgroundStyle.backgroundXOffset = null;
- backgroundStyle.backgroundYOffset = null;
- backgroundStyle.backgroundGraphicZIndex = null;
-
- return this.redrawNode(
- id + this.BACKGROUND_ID_SUFFIX,
- geometry,
- backgroundStyle,
- null
- );
- },
-
- /**
- * Method: drawGeometryNode
- * Given a node, draw a geometry on the specified layer.
- * node and geometry are required arguments, style is optional.
- * This method is only called by the render itself.
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- * style - {Object}
- *
- * Returns:
- * {Object} a hash with properties "node" (the drawn node) and "complete"
- * (null if parts of the geometry could not be drawn, false if nothing
- * could be drawn)
- */
- drawGeometryNode: function(node, geometry, style) {
- style = style || node._style;
-
- var options = {
- 'isFilled': style.fill === undefined ?
- true :
- style.fill,
- 'isStroked': style.stroke === undefined ?
- !!style.strokeWidth :
- style.stroke
- };
- var drawn;
- switch (geometry.CLASS_NAME) {
- case "OpenLayers.Geometry.Point":
- if(style.graphic === false) {
- options.isFilled = false;
- options.isStroked = false;
- }
- drawn = this.drawPoint(node, geometry);
- break;
- case "OpenLayers.Geometry.LineString":
- options.isFilled = false;
- drawn = this.drawLineString(node, geometry);
- break;
- case "OpenLayers.Geometry.LinearRing":
- drawn = this.drawLinearRing(node, geometry);
- break;
- case "OpenLayers.Geometry.Polygon":
- drawn = this.drawPolygon(node, geometry);
- break;
- case "OpenLayers.Geometry.Rectangle":
- drawn = this.drawRectangle(node, geometry);
- break;
- default:
- break;
- }
-
- node._options = options;
-
- //set style
- //TBD simplify this
- if (drawn != false) {
- return {
- node: this.setStyle(node, style, options, geometry),
- complete: drawn
- };
- } else {
- return false;
- }
- },
-
- /**
- * Method: postDraw
- * Things that have do be done after the geometry node is appended
- * to its parent node. To be overridden by subclasses.
- *
- * Parameters:
- * node - {DOMElement}
- */
- postDraw: function(node) {},
-
- /**
- * Method: drawPoint
- * Virtual function for drawing Point Geometry.
- * Should be implemented by subclasses.
- * This method is only called by the renderer itself.
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- *
- * Returns:
- * {DOMElement} or false if the renderer could not draw the point
- */
- drawPoint: function(node, geometry) {},
-
- /**
- * Method: drawLineString
- * Virtual function for drawing LineString Geometry.
- * Should be implemented by subclasses.
- * This method is only called by the renderer itself.
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- *
- * Returns:
- * {DOMElement} or null if the renderer could not draw all components of
- * the linestring, or false if nothing could be drawn
- */
- drawLineString: function(node, geometry) {},
-
- /**
- * Method: drawLinearRing
- * Virtual function for drawing LinearRing Geometry.
- * Should be implemented by subclasses.
- * This method is only called by the renderer itself.
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- *
- * Returns:
- * {DOMElement} or null if the renderer could not draw all components
- * of the linear ring, or false if nothing could be drawn
- */
- drawLinearRing: function(node, geometry) {},
-
- /**
- * Method: drawPolygon
- * Virtual function for drawing Polygon Geometry.
- * Should be implemented by subclasses.
- * This method is only called by the renderer itself.
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- *
- * Returns:
- * {DOMElement} or null if the renderer could not draw all components
- * of the polygon, or false if nothing could be drawn
- */
- drawPolygon: function(node, geometry) {},
-
- /**
- * Method: drawRectangle
- * Virtual function for drawing Rectangle Geometry.
- * Should be implemented by subclasses.
- * This method is only called by the renderer itself.
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- *
- * Returns:
- * {DOMElement} or false if the renderer could not draw the rectangle
- */
- drawRectangle: function(node, geometry) {},
-
- /**
- * Method: drawCircle
- * Virtual function for drawing Circle Geometry.
- * Should be implemented by subclasses.
- * This method is only called by the renderer itself.
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- *
- * Returns:
- * {DOMElement} or false if the renderer could not draw the circle
- */
- drawCircle: function(node, geometry) {},
-
- /**
- * Method: removeText
- * Removes a label
- *
- * Parameters:
- * featureId - {String}
- */
- removeText: function(featureId) {
- var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);
- if (label) {
- this.textRoot.removeChild(label);
- }
- var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX);
- if (outline) {
- this.textRoot.removeChild(outline);
- }
- },
-
- /**
- * Method: getFeatureIdFromEvent
- *
- * Parameters:
- * evt - {Object} An object
- *
- * Returns:
- * {String} A feature id or undefined.
- */
- getFeatureIdFromEvent: function(evt) {
- var target = evt.target;
- var useElement = target && target.correspondingUseElement;
- var node = useElement ? useElement : (target || evt.srcElement);
- return node._featureId;
- },
-
- /**
- * Method: eraseGeometry
- * Erase a geometry from the renderer. In the case of a multi-geometry,
- * we cycle through and recurse on ourselves. Otherwise, we look for a
- * node with the geometry.id, destroy its geometry, and remove it from
- * the DOM.
- *
- * Parameters:
- * geometry - {}
- * featureId - {String}
- */
- eraseGeometry: function(geometry, featureId) {
- if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") ||
- (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") ||
- (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") ||
- (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) {
- for (var i=0, len=geometry.components.length; i} target renderer for the moved root
- */
- moveRoot: function(renderer) {
- var root = this.root;
- if(renderer.root.parentNode == this.rendererRoot) {
- root = renderer.root;
- }
- root.parentNode.removeChild(root);
- renderer.rendererRoot.appendChild(root);
- },
-
- /**
- * Method: getRenderLayerId
- * Gets the layer that this renderer's output appears on. If moveRoot was
- * used, this will be different from the id of the layer containing the
- * features rendered by this renderer.
- *
- * Returns:
- * {String} the id of the output layer.
- */
- getRenderLayerId: function() {
- return this.root.parentNode.parentNode.id;
- },
-
- /**
- * Method: isComplexSymbol
- * Determines if a symbol cannot be rendered using drawCircle
- *
- * Parameters:
- * graphicName - {String}
- *
- * Returns
- * {Boolean} true if the symbol is complex, false if not
- */
- isComplexSymbol: function(graphicName) {
- return (graphicName != "circle") && !!graphicName;
- },
-
- CLASS_NAME: "OpenLayers.Renderer.Elements"
-});
-
-/* ======================================================================
- OpenLayers/Renderer/VML.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Renderer/Elements.js
- */
-
-/**
- * Class: OpenLayers.Renderer.VML
- * Render vector features in browsers with VML capability. Construct a new
- * VML renderer with the constructor.
- *
- * Note that for all calculations in this class, we use (num | 0) to truncate a
- * float value to an integer. This is done because it seems that VML doesn't
- * support float values.
- *
- * Inherits from:
- * -
- */
-OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
-
- /**
- * Property: xmlns
- * {String} XML Namespace URN
- */
- xmlns: "urn:schemas-microsoft-com:vml",
-
- /**
- * Property: symbolCache
- * {DOMElement} node holding symbols. This hash is keyed by symbol name,
- * and each value is a hash with a "path" and an "extent" property.
- */
- symbolCache: {},
-
- /**
- * Property: offset
- * {Object} Hash with "x" and "y" properties
- */
- offset: null,
-
- /**
- * Constructor: OpenLayers.Renderer.VML
- * Create a new VML renderer.
- *
- * Parameters:
- * containerID - {String} The id for the element that contains the renderer
- */
- initialize: function(containerID) {
- if (!this.supported()) {
- return;
- }
- if (!document.namespaces.olv) {
- document.namespaces.add("olv", this.xmlns);
- var style = document.createStyleSheet();
- var shapes = ['shape','rect', 'oval', 'fill', 'stroke', 'imagedata', 'group','textbox'];
- for (var i = 0, len = shapes.length; i < len; i++) {
-
- style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " +
- "position: absolute; display: inline-block;");
- }
- }
-
- OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
- arguments);
- },
-
- /**
- * APIMethod: supported
- * Determine whether a browser supports this renderer.
- *
- * Returns:
- * {Boolean} The browser supports the VML renderer
- */
- supported: function() {
- return !!(document.namespaces);
- },
-
- /**
- * Method: setExtent
- * Set the renderer's extent
- *
- * Parameters:
- * extent - {}
- * resolutionChanged - {Boolean}
- *
- * Returns:
- * {Boolean} true to notify the layer that the new extent does not exceed
- * the coordinate range, and the features will not need to be redrawn.
- */
- setExtent: function(extent, resolutionChanged) {
- var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
- var resolution = this.getResolution();
-
- var left = (extent.left/resolution) | 0;
- var top = (extent.top/resolution - this.size.h) | 0;
- if (resolutionChanged || !this.offset) {
- this.offset = {x: left, y: top};
- left = 0;
- top = 0;
- } else {
- left = left - this.offset.x;
- top = top - this.offset.y;
- }
-
-
- var org = (left - this.xOffset) + " " + top;
- this.root.coordorigin = org;
- var roots = [this.root, this.vectorRoot, this.textRoot];
- var root;
- for(var i=0, len=roots.length; i} the size of the drawing surface
- */
- setSize: function(size) {
- OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
-
- // setting width and height on all roots to avoid flicker which we
- // would get with 100% width and height on child roots
- var roots = [
- this.rendererRoot,
- this.root,
- this.vectorRoot,
- this.textRoot
- ];
- var w = this.size.w + "px";
- var h = this.size.h + "px";
- var root;
- for(var i=0, len=roots.length; i}
- * style - {Object}
- *
- * Returns:
- * {String} The corresponding node type for the specified geometry
- */
- getNodeType: function(geometry, style) {
- var nodeType = null;
- switch (geometry.CLASS_NAME) {
- case "OpenLayers.Geometry.Point":
- if (style.externalGraphic) {
- nodeType = "olv:rect";
- } else if (this.isComplexSymbol(style.graphicName)) {
- nodeType = "olv:shape";
- } else {
- nodeType = "olv:oval";
- }
- break;
- case "OpenLayers.Geometry.Rectangle":
- nodeType = "olv:rect";
- break;
- case "OpenLayers.Geometry.LineString":
- case "OpenLayers.Geometry.LinearRing":
- case "OpenLayers.Geometry.Polygon":
- case "OpenLayers.Geometry.Curve":
- nodeType = "olv:shape";
- break;
- default:
- break;
- }
- return nodeType;
- },
-
- /**
- * Method: setStyle
- * Use to set all the style attributes to a VML node.
- *
- * Parameters:
- * node - {DOMElement} An VML element to decorate
- * style - {Object}
- * options - {Object} Currently supported options include
- * 'isFilled' {Boolean} and
- * 'isStroked' {Boolean}
- * geometry - {}
- */
- setStyle: function(node, style, options, geometry) {
- style = style || node._style;
- options = options || node._options;
- var fillColor = style.fillColor;
-
- if (node._geometryClass === "OpenLayers.Geometry.Point") {
- if (style.externalGraphic) {
- options.isFilled = true;
- if (style.graphicTitle) {
- node.title=style.graphicTitle;
- }
- var width = style.graphicWidth || style.graphicHeight;
- var height = style.graphicHeight || style.graphicWidth;
- width = width ? width : style.pointRadius*2;
- height = height ? height : style.pointRadius*2;
-
- var resolution = this.getResolution();
- var xOffset = (style.graphicXOffset != undefined) ?
- style.graphicXOffset : -(0.5 * width);
- var yOffset = (style.graphicYOffset != undefined) ?
- style.graphicYOffset : -(0.5 * height);
-
- node.style.left = ((((geometry.x - this.featureDx)/resolution - this.offset.x)+xOffset) | 0) + "px";
- node.style.top = (((geometry.y/resolution - this.offset.y)-(yOffset+height)) | 0) + "px";
- node.style.width = width + "px";
- node.style.height = height + "px";
- node.style.flip = "y";
-
- // modify fillColor and options for stroke styling below
- fillColor = "none";
- options.isStroked = false;
- } else if (this.isComplexSymbol(style.graphicName)) {
- var cache = this.importSymbol(style.graphicName);
- node.path = cache.path;
- node.coordorigin = cache.left + "," + cache.bottom;
- var size = cache.size;
- node.coordsize = size + "," + size;
- this.drawCircle(node, geometry, style.pointRadius);
- node.style.flip = "y";
- } else {
- this.drawCircle(node, geometry, style.pointRadius);
- }
- }
-
- // fill
- if (options.isFilled) {
- node.fillcolor = fillColor;
- } else {
- node.filled = "false";
- }
- var fills = node.getElementsByTagName("fill");
- var fill = (fills.length == 0) ? null : fills[0];
- if (!options.isFilled) {
- if (fill) {
- node.removeChild(fill);
- }
- } else {
- if (!fill) {
- fill = this.createNode('olv:fill', node.id + "_fill");
- }
- fill.opacity = style.fillOpacity;
-
- if (node._geometryClass === "OpenLayers.Geometry.Point" &&
- style.externalGraphic) {
-
- // override fillOpacity
- if (style.graphicOpacity) {
- fill.opacity = style.graphicOpacity;
- }
-
- fill.src = style.externalGraphic;
- fill.type = "frame";
-
- if (!(style.graphicWidth && style.graphicHeight)) {
- fill.aspect = "atmost";
- }
- }
- if (fill.parentNode != node) {
- node.appendChild(fill);
- }
- }
-
- // additional rendering for rotated graphics or symbols
- var rotation = style.rotation;
- if ((rotation !== undefined || node._rotation !== undefined)) {
- node._rotation = rotation;
- if (style.externalGraphic) {
- this.graphicRotate(node, xOffset, yOffset, style);
- // make the fill fully transparent, because we now have
- // the graphic as imagedata element. We cannot just remove
- // the fill, because this is part of the hack described
- // in graphicRotate
- fill.opacity = 0;
- } else if(node._geometryClass === "OpenLayers.Geometry.Point") {
- node.style.rotation = rotation || 0;
- }
- }
-
- // stroke
- var strokes = node.getElementsByTagName("stroke");
- var stroke = (strokes.length == 0) ? null : strokes[0];
- if (!options.isStroked) {
- node.stroked = false;
- if (stroke) {
- stroke.on = false;
- }
- } else {
- if (!stroke) {
- stroke = this.createNode('olv:stroke', node.id + "_stroke");
- node.appendChild(stroke);
- }
- stroke.on = true;
- stroke.color = style.strokeColor;
- stroke.weight = style.strokeWidth + "px";
- stroke.opacity = style.strokeOpacity;
- stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' :
- (style.strokeLinecap || 'round');
- if (style.strokeDashstyle) {
- stroke.dashstyle = this.dashStyle(style);
- }
- }
-
- if (style.cursor != "inherit" && style.cursor != null) {
- node.style.cursor = style.cursor;
- }
- return node;
- },
-
- /**
- * Method: graphicRotate
- * If a point is to be styled with externalGraphic and rotation, VML fills
- * cannot be used to display the graphic, because rotation of graphic
- * fills is not supported by the VML implementation of Internet Explorer.
- * This method creates a olv:imagedata element inside the VML node,
- * DXImageTransform.Matrix and BasicImage filters for rotation and
- * opacity, and a 3-step hack to remove rendering artefacts from the
- * graphic and preserve the ability of graphics to trigger events.
- * Finally, OpenLayers methods are used to determine the correct
- * insertion point of the rotated image, because DXImageTransform.Matrix
- * does the rotation without the ability to specify a rotation center
- * point.
- *
- * Parameters:
- * node - {DOMElement}
- * xOffset - {Number} rotation center relative to image, x coordinate
- * yOffset - {Number} rotation center relative to image, y coordinate
- * style - {Object}
- */
- graphicRotate: function(node, xOffset, yOffset, style) {
- var style = style || node._style;
- var rotation = style.rotation || 0;
-
- var aspectRatio, size;
- if (!(style.graphicWidth && style.graphicHeight)) {
- // load the image to determine its size
- var img = new Image();
- img.onreadystatechange = OpenLayers.Function.bind(function() {
- if(img.readyState == "complete" ||
- img.readyState == "interactive") {
- aspectRatio = img.width / img.height;
- size = Math.max(style.pointRadius * 2,
- style.graphicWidth || 0,
- style.graphicHeight || 0);
- xOffset = xOffset * aspectRatio;
- style.graphicWidth = size * aspectRatio;
- style.graphicHeight = size;
- this.graphicRotate(node, xOffset, yOffset, style);
- }
- }, this);
- img.src = style.externalGraphic;
-
- // will be called again by the onreadystate handler
- return;
- } else {
- size = Math.max(style.graphicWidth, style.graphicHeight);
- aspectRatio = style.graphicWidth / style.graphicHeight;
- }
-
- var width = Math.round(style.graphicWidth || size * aspectRatio);
- var height = Math.round(style.graphicHeight || size);
- node.style.width = width + "px";
- node.style.height = height + "px";
-
- // Three steps are required to remove artefacts for images with
- // transparent backgrounds (resulting from using DXImageTransform
- // filters on svg objects), while preserving awareness for browser
- // events on images:
- // - Use the fill as usual (like for unrotated images) to handle
- // events
- // - specify an imagedata element with the same src as the fill
- // - style the imagedata element with an AlphaImageLoader filter
- // with empty src
- var image = document.getElementById(node.id + "_image");
- if (!image) {
- image = this.createNode("olv:imagedata", node.id + "_image");
- node.appendChild(image);
- }
- image.style.width = width + "px";
- image.style.height = height + "px";
- image.src = style.externalGraphic;
- image.style.filter =
- "progid:DXImageTransform.Microsoft.AlphaImageLoader(" +
- "src='', sizingMethod='scale')";
-
- var rot = rotation * Math.PI / 180;
- var sintheta = Math.sin(rot);
- var costheta = Math.cos(rot);
-
- // do the rotation on the image
- var filter =
- "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta +
- ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta +
- ",SizingMethod='auto expand')\n";
-
- // set the opacity (needed for the imagedata)
- var opacity = style.graphicOpacity || style.fillOpacity;
- if (opacity && opacity != 1) {
- filter +=
- "progid:DXImageTransform.Microsoft.BasicImage(opacity=" +
- opacity+")\n";
- }
- node.style.filter = filter;
-
- // do the rotation again on a box, so we know the insertion point
- var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset);
- var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry();
- imgBox.rotate(style.rotation, centerPoint);
- var imgBounds = imgBox.getBounds();
-
- node.style.left = Math.round(
- parseInt(node.style.left) + imgBounds.left) + "px";
- node.style.top = Math.round(
- parseInt(node.style.top) - imgBounds.bottom) + "px";
- },
-
- /**
- * Method: postDraw
- * Does some node postprocessing to work around browser issues:
- * - Some versions of Internet Explorer seem to be unable to set fillcolor
- * and strokecolor to "none" correctly before the fill node is appended
- * to a visible vml node. This method takes care of that and sets
- * fillcolor and strokecolor again if needed.
- * - In some cases, a node won't become visible after being drawn. Setting
- * style.visibility to "visible" works around that.
- *
- * Parameters:
- * node - {DOMElement}
- */
- postDraw: function(node) {
- node.style.visibility = "visible";
- var fillColor = node._style.fillColor;
- var strokeColor = node._style.strokeColor;
- if (fillColor == "none" &&
- node.fillcolor != fillColor) {
- node.fillcolor = fillColor;
- }
- if (strokeColor == "none" &&
- node.strokecolor != strokeColor) {
- node.strokecolor = strokeColor;
- }
- },
-
-
- /**
- * Method: setNodeDimension
- * Get the geometry's bounds, convert it to our vml coordinate system,
- * then set the node's position, size, and local coordinate system.
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- */
- setNodeDimension: function(node, geometry) {
-
- var bbox = geometry.getBounds();
- if(bbox) {
- var resolution = this.getResolution();
-
- var scaledBox =
- new OpenLayers.Bounds(((bbox.left - this.featureDx)/resolution - this.offset.x) | 0,
- (bbox.bottom/resolution - this.offset.y) | 0,
- ((bbox.right - this.featureDx)/resolution - this.offset.x) | 0,
- (bbox.top/resolution - this.offset.y) | 0);
-
- // Set the internal coordinate system to draw the path
- node.style.left = scaledBox.left + "px";
- node.style.top = scaledBox.top + "px";
- node.style.width = scaledBox.getWidth() + "px";
- node.style.height = scaledBox.getHeight() + "px";
-
- node.coordorigin = scaledBox.left + " " + scaledBox.top;
- node.coordsize = scaledBox.getWidth()+ " " + scaledBox.getHeight();
- }
- },
-
- /**
- * Method: dashStyle
- *
- * Parameters:
- * style - {Object}
- *
- * Returns:
- * {String} A VML compliant 'stroke-dasharray' value
- */
- dashStyle: function(style) {
- var dash = style.strokeDashstyle;
- switch (dash) {
- case 'solid':
- case 'dot':
- case 'dash':
- case 'dashdot':
- case 'longdash':
- case 'longdashdot':
- return dash;
- default:
- // very basic guessing of dash style patterns
- var parts = dash.split(/[ ,]/);
- if (parts.length == 2) {
- if (1*parts[0] >= 2*parts[1]) {
- return "longdash";
- }
- return (parts[0] == 1 || parts[1] == 1) ? "dot" : "dash";
- } else if (parts.length == 4) {
- return (1*parts[0] >= 2*parts[1]) ? "longdashdot" :
- "dashdot";
- }
- return "solid";
- }
- },
-
- /**
- * Method: createNode
- * Create a new node
- *
- * Parameters:
- * type - {String} Kind of node to draw
- * id - {String} Id for node
- *
- * Returns:
- * {DOMElement} A new node of the given type and id
- */
- createNode: function(type, id) {
- var node = document.createElement(type);
- if (id) {
- node.id = id;
- }
-
- // IE hack to make elements unselectable, to prevent 'blue flash'
- // while dragging vectors; #1410
- node.unselectable = 'on';
- node.onselectstart = OpenLayers.Function.False;
-
- return node;
- },
-
- /**
- * Method: nodeTypeCompare
- * Determine whether a node is of a given type
- *
- * Parameters:
- * node - {DOMElement} An VML element
- * type - {String} Kind of node
- *
- * Returns:
- * {Boolean} Whether or not the specified node is of the specified type
- */
- nodeTypeCompare: function(node, type) {
-
- //split type
- var subType = type;
- var splitIndex = subType.indexOf(":");
- if (splitIndex != -1) {
- subType = subType.substr(splitIndex+1);
- }
-
- //split nodeName
- var nodeName = node.nodeName;
- splitIndex = nodeName.indexOf(":");
- if (splitIndex != -1) {
- nodeName = nodeName.substr(splitIndex+1);
- }
-
- return (subType == nodeName);
- },
-
- /**
- * Method: createRenderRoot
- * Create the renderer root
- *
- * Returns:
- * {DOMElement} The specific render engine's root element
- */
- createRenderRoot: function() {
- return this.nodeFactory(this.container.id + "_vmlRoot", "div");
- },
-
- /**
- * Method: createRoot
- * Create the main root element
- *
- * Parameters:
- * suffix - {String} suffix to append to the id
- *
- * Returns:
- * {DOMElement}
- */
- createRoot: function(suffix) {
- return this.nodeFactory(this.container.id + suffix, "olv:group");
- },
-
- /**************************************
- * *
- * GEOMETRY DRAWING FUNCTIONS *
- * *
- **************************************/
-
- /**
- * Method: drawPoint
- * Render a point
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- *
- * Returns:
- * {DOMElement} or false if the point could not be drawn
- */
- drawPoint: function(node, geometry) {
- return this.drawCircle(node, geometry, 1);
- },
-
- /**
- * Method: drawCircle
- * Render a circle.
- * Size and Center a circle given geometry (x,y center) and radius
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- * radius - {float}
- *
- * Returns:
- * {DOMElement} or false if the circle could not ne drawn
- */
- drawCircle: function(node, geometry, radius) {
- if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
- var resolution = this.getResolution();
-
- node.style.left = ((((geometry.x - this.featureDx) /resolution - this.offset.x) | 0) - radius) + "px";
- node.style.top = (((geometry.y /resolution - this.offset.y) | 0) - radius) + "px";
-
- var diameter = radius * 2;
-
- node.style.width = diameter + "px";
- node.style.height = diameter + "px";
- return node;
- }
- return false;
- },
-
-
- /**
- * Method: drawLineString
- * Render a linestring.
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- *
- * Returns:
- * {DOMElement}
- */
- drawLineString: function(node, geometry) {
- return this.drawLine(node, geometry, false);
- },
-
- /**
- * Method: drawLinearRing
- * Render a linearring
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- *
- * Returns:
- * {DOMElement}
- */
- drawLinearRing: function(node, geometry) {
- return this.drawLine(node, geometry, true);
- },
-
- /**
- * Method: DrawLine
- * Render a line.
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- * closeLine - {Boolean} Close the line? (make it a ring?)
- *
- * Returns:
- * {DOMElement}
- */
- drawLine: function(node, geometry, closeLine) {
-
- this.setNodeDimension(node, geometry);
-
- var resolution = this.getResolution();
- var numComponents = geometry.components.length;
- var parts = new Array(numComponents);
-
- var comp, x, y;
- for (var i = 0; i < numComponents; i++) {
- comp = geometry.components[i];
- x = ((comp.x - this.featureDx)/resolution - this.offset.x) | 0;
- y = (comp.y/resolution - this.offset.y) | 0;
- parts[i] = " " + x + "," + y + " l ";
- }
- var end = (closeLine) ? " x e" : " e";
- node.path = "m" + parts.join("") + end;
- return node;
- },
-
- /**
- * Method: drawPolygon
- * Render a polygon
- *
- * Parameters:
- * node - {DOMElement}
- * geometry - {}
- *
- * Returns:
- * {DOMElement}
- */
- drawPolygon: function(node, geometry) {
- this.setNodeDimension(node, geometry);
-
- var resolution = this.getResolution();
-
- var path = [];
- var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y;
- for (j=0, jj=geometry.components.length; j}
- *
- * Returns:
- * {DOMElement}
- */
- drawRectangle: function(node, geometry) {
- var resolution = this.getResolution();
-
- node.style.left = (((geometry.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
- node.style.top = ((geometry.y/resolution - this.offset.y) | 0) + "px";
- node.style.width = ((geometry.width/resolution) | 0) + "px";
- node.style.height = ((geometry.height/resolution) | 0) + "px";
-
- return node;
- },
-
- /**
- * Method: drawText
- * This method is only called by the renderer itself.
- *
- * Parameters:
- * featureId - {String}
- * style -
- * location - {}
- */
- drawText: function(featureId, style, location) {
- var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect");
- var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox");
-
- var resolution = this.getResolution();
- label.style.left = (((location.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
- label.style.top = ((location.y/resolution - this.offset.y) | 0) + "px";
- label.style.flip = "y";
-
- textbox.innerText = style.label;
-
- if (style.cursor != "inherit" && style.cursor != null) {
- textbox.style.cursor = style.cursor;
- }
- if (style.fontColor) {
- textbox.style.color = style.fontColor;
- }
- if (style.fontOpacity) {
- textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')';
- }
- if (style.fontFamily) {
- textbox.style.fontFamily = style.fontFamily;
- }
- if (style.fontSize) {
- textbox.style.fontSize = style.fontSize;
- }
- if (style.fontWeight) {
- textbox.style.fontWeight = style.fontWeight;
- }
- if (style.fontStyle) {
- textbox.style.fontStyle = style.fontStyle;
- }
- if(style.labelSelect === true) {
- label._featureId = featureId;
- textbox._featureId = featureId;
- textbox._geometry = location;
- textbox._geometryClass = location.CLASS_NAME;
- }
- textbox.style.whiteSpace = "nowrap";
- // fun with IE: IE7 in standards compliant mode does not display any
- // text with a left inset of 0. So we set this to 1px and subtract one
- // pixel later when we set label.style.left
- textbox.inset = "1px,0px,0px,0px";
-
- if(!label.parentNode) {
- label.appendChild(textbox);
- this.textRoot.appendChild(label);
- }
-
- var align = style.labelAlign || "cm";
- if (align.length == 1) {
- align += "m";
- }
- var xshift = textbox.clientWidth *
- (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0,1)]);
- var yshift = textbox.clientHeight *
- (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]);
- label.style.left = parseInt(label.style.left)-xshift-1+"px";
- label.style.top = parseInt(label.style.top)+yshift+"px";
-
- },
-
- /**
- * Method: moveRoot
- * moves this renderer's root to a different renderer.
- *
- * Parameters:
- * renderer - {} target renderer for the moved root
- * root - {DOMElement} optional root node. To be used when this renderer
- * holds roots from multiple layers to tell this method which one to
- * detach
- *
- * Returns:
- * {Boolean} true if successful, false otherwise
- */
- moveRoot: function(renderer) {
- var layer = this.map.getLayer(renderer.container.id);
- if(layer instanceof OpenLayers.Layer.Vector.RootContainer) {
- layer = this.map.getLayer(this.container.id);
- }
- layer && layer.renderer.clear();
- OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments);
- layer && layer.redraw();
- },
-
- /**
- * Method: importSymbol
- * add a new symbol definition from the rendererer's symbol hash
- *
- * Parameters:
- * graphicName - {String} name of the symbol to import
- *
- * Returns:
- * {Object} - hash of {DOMElement} "symbol" and {Number} "size"
- */
- importSymbol: function (graphicName) {
- var id = this.container.id + "-" + graphicName;
-
- // check if symbol already exists in the cache
- var cache = this.symbolCache[id];
- if (cache) {
- return cache;
- }
-
- var symbol = OpenLayers.Renderer.symbol[graphicName];
- if (!symbol) {
- throw new Error(graphicName + ' is not a valid symbol name');
- }
-
- var symbolExtent = new OpenLayers.Bounds(
- Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
-
- var pathitems = ["m"];
- for (var i=0; i 0) {
- symbolExtent.bottom = symbolExtent.bottom - diff;
- symbolExtent.top = symbolExtent.top + diff;
- } else {
- symbolExtent.left = symbolExtent.left + diff;
- symbolExtent.right = symbolExtent.right - diff;
- }
-
- cache = {
- path: path,
- size: symbolExtent.getWidth(), // equals getHeight() now
- left: symbolExtent.left,
- bottom: symbolExtent.bottom
- };
- this.symbolCache[id] = cache;
-
- return cache;
- },
-
- CLASS_NAME: "OpenLayers.Renderer.VML"
-});
-
-/**
- * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT
- * {Object}
- */
-OpenLayers.Renderer.VML.LABEL_SHIFT = {
- "l": 0,
- "c": .5,
- "r": 1,
- "t": 0,
- "m": .5,
- "b": 1
-};
-/* ======================================================================
- OpenLayers/Animation.js
- ====================================================================== */
-
-/**
- * Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license.
- *
- * @requires OpenLayers/SingleFile.js
- */
-
-/**
- * Namespace: OpenLayers.Animation
- * A collection of utility functions for executing methods that repaint a
- * portion of the browser window. These methods take advantage of the
- * browser's scheduled repaints where requestAnimationFrame is available.
- */
-OpenLayers.Animation = (function(window) {
-
- /**
- * Property: isNative
- * {Boolean} true if a native requestAnimationFrame function is available
- */
- var isNative = !!(window.requestAnimationFrame ||
- window.webkitRequestAnimationFrame ||
- window.mozRequestAnimationFrame ||
- window.oRequestAnimationFrame ||
- window.msRequestAnimationFrame);
-
- /**
- * Function: requestFrame
- * Schedule a function to be called at the next available animation frame.
- * Uses the native method where available. Where requestAnimationFrame is
- * not available, setTimeout will be called with a 16ms delay.
- *
- * Parameters:
- * callback - {Function} The function to be called at the next animation frame.
- * element - {DOMElement} Optional element that visually bounds the animation.
- */
- var requestFrame = (function() {
- var request = window.requestAnimationFrame ||
- window.webkitRequestAnimationFrame ||
- window.mozRequestAnimationFrame ||
- window.oRequestAnimationFrame ||
- window.msRequestAnimationFrame ||
- function(callback, element) {
- window.setTimeout(callback, 16);
- };
- // bind to window to avoid illegal invocation of native function
- return function(callback, element) {
- request.apply(window, [callback, element]);
- };
- })();
-
- // private variables for animation loops
- var counter = 0;
- var loops = {};
-
- /**
- * Function: start
- * Executes a method with in series for some
- * duration.
- *
- * Parameters:
- * callback - {Function} The function to be called at the next animation frame.
- * duration - {Number} Optional duration for the loop. If not provided, the
- * animation loop will execute indefinitely.
- * element - {DOMElement} Optional element that visually bounds the animation.
- *
- * Returns:
- * {Number} Identifier for the animation loop. Used to stop animations with
- * .
- */
- function start(callback, duration, element) {
- duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;
- var id = ++counter;
- var start = +new Date;
- loops[id] = function() {
- if (loops[id] && +new Date - start <= duration) {
- callback();
- if (loops[id]) {
- requestFrame(loops[id], element);
- }
- } else {
- delete loops[id];
- }
- };
- requestFrame(loops[id], element);
- return id;
- }
-
- /**
- * Function: stop
- * Terminates an animation loop started with .
- *
- * Parameters:
- * id - {Number} Identifier returned from .
- */
- function stop(id) {
- delete loops[id];
- }
-
- return {
- isNative: isNative,
- requestFrame: requestFrame,
- start: start,
- stop: stop
- };
-
-})(window);
-/* ======================================================================
- OpenLayers/Tween.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/BaseTypes/Class.js
- * @requires OpenLayers/Animation.js
- */
-
-/**
- * Namespace: OpenLayers.Tween
- */
-OpenLayers.Tween = OpenLayers.Class({
-
- /**
- * APIProperty: easing
- * {(Function)} Easing equation used for the animation
- * Defaultly set to OpenLayers.Easing.Expo.easeOut
- */
- easing: null,
-
- /**
- * APIProperty: begin
- * {Object} Values to start the animation with
- */
- begin: null,
-
- /**
- * APIProperty: finish
- * {Object} Values to finish the animation with
- */
- finish: null,
-
- /**
- * APIProperty: duration
- * {int} duration of the tween (number of steps)
- */
- duration: null,
-
- /**
- * APIProperty: callbacks
- * {Object} An object with start, eachStep and done properties whose values
- * are functions to be call during the animation. They are passed the
- * current computed value as argument.
- */
- callbacks: null,
-
- /**
- * Property: time
- * {int} Step counter
- */
- time: null,
-
- /**
- * Property: animationId
- * {int} Loop id returned by OpenLayers.Animation.start
- */
- animationId: null,
-
- /**
- * Property: playing
- * {Boolean} Tells if the easing is currently playing
- */
- playing: false,
-
- /**
- * Constructor: OpenLayers.Tween
- * Creates a Tween.
- *
- * Parameters:
- * easing - {(Function)} easing function method to use
- */
- initialize: function(easing) {
- this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;
- },
-
- /**
- * APIMethod: start
- * Plays the Tween, and calls the callback method on each step
- *
- * Parameters:
- * begin - {Object} values to start the animation with
- * finish - {Object} values to finish the animation with
- * duration - {int} duration of the tween (number of steps)
- * options - {Object} hash of options (for example callbacks (start, eachStep, done))
- */
- start: function(begin, finish, duration, options) {
- this.playing = true;
- this.begin = begin;
- this.finish = finish;
- this.duration = duration;
- this.callbacks = options.callbacks;
- this.time = 0;
- OpenLayers.Animation.stop(this.animationId);
- this.animationId = null;
- if (this.callbacks && this.callbacks.start) {
- this.callbacks.start.call(this, this.begin);
- }
- this.animationId = OpenLayers.Animation.start(
- OpenLayers.Function.bind(this.play, this)
- );
- },
-
- /**
- * APIMethod: stop
- * Stops the Tween, and calls the done callback
- * Doesn't do anything if animation is already finished
- */
- stop: function() {
- if (!this.playing) {
- return;
- }
-
- if (this.callbacks && this.callbacks.done) {
- this.callbacks.done.call(this, this.finish);
- }
- OpenLayers.Animation.stop(this.animationId);
- this.animationId = null;
- this.playing = false;
- },
-
- /**
- * Method: play
- * Calls the appropriate easing method
- */
- play: function() {
- var value = {};
- for (var i in this.begin) {
- var b = this.begin[i];
- var f = this.finish[i];
- if (b == null || f == null || isNaN(b) || isNaN(f)) {
- throw new TypeError('invalid value for Tween');
- }
-
- var c = f - b;
- value[i] = this.easing.apply(this, [this.time, b, c, this.duration]);
- }
- this.time++;
-
- if (this.callbacks && this.callbacks.eachStep) {
- this.callbacks.eachStep.call(this, value);
- }
-
- if (this.time > this.duration) {
- this.stop();
- }
- },
-
- /**
- * Create empty functions for all easing methods.
- */
- CLASS_NAME: "OpenLayers.Tween"
-});
-
-/**
- * Namespace: OpenLayers.Easing
- *
- * Credits:
- * Easing Equations by Robert Penner,
- */
-OpenLayers.Easing = {
- /**
- * Create empty functions for all easing methods.
- */
- CLASS_NAME: "OpenLayers.Easing"
-};
-
-/**
- * Namespace: OpenLayers.Easing.Linear
- */
-OpenLayers.Easing.Linear = {
-
- /**
- * Function: easeIn
- *
- * Parameters:
- * t - {Float} time
- * b - {Float} beginning position
- * c - {Float} total change
- * d - {Float} duration of the transition
- *
- * Returns:
- * {Float}
- */
- easeIn: function(t, b, c, d) {
- return c*t/d + b;
- },
-
- /**
- * Function: easeOut
- *
- * Parameters:
- * t - {Float} time
- * b - {Float} beginning position
- * c - {Float} total change
- * d - {Float} duration of the transition
- *
- * Returns:
- * {Float}
- */
- easeOut: function(t, b, c, d) {
- return c*t/d + b;
- },
-
- /**
- * Function: easeInOut
- *
- * Parameters:
- * t - {Float} time
- * b - {Float} beginning position
- * c - {Float} total change
- * d - {Float} duration of the transition
- *
- * Returns:
- * {Float}
- */
- easeInOut: function(t, b, c, d) {
- return c*t/d + b;
- },
-
- CLASS_NAME: "OpenLayers.Easing.Linear"
-};
-
-/**
- * Namespace: OpenLayers.Easing.Expo
- */
-OpenLayers.Easing.Expo = {
-
- /**
- * Function: easeIn
- *
- * Parameters:
- * t - {Float} time
- * b - {Float} beginning position
- * c - {Float} total change
- * d - {Float} duration of the transition
- *
- * Returns:
- * {Float}
- */
- easeIn: function(t, b, c, d) {
- return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
- },
-
- /**
- * Function: easeOut
- *
- * Parameters:
- * t - {Float} time
- * b - {Float} beginning position
- * c - {Float} total change
- * d - {Float} duration of the transition
- *
- * Returns:
- * {Float}
- */
- easeOut: function(t, b, c, d) {
- return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
- },
-
- /**
- * Function: easeInOut
- *
- * Parameters:
- * t - {Float} time
- * b - {Float} beginning position
- * c - {Float} total change
- * d - {Float} duration of the transition
- *
- * Returns:
- * {Float}
- */
- easeInOut: function(t, b, c, d) {
- if (t==0) return b;
- if (t==d) return b+c;
- if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
- return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
- },
-
- CLASS_NAME: "OpenLayers.Easing.Expo"
-};
-
-/**
- * Namespace: OpenLayers.Easing.Quad
- */
-OpenLayers.Easing.Quad = {
-
- /**
- * Function: easeIn
- *
- * Parameters:
- * t - {Float} time
- * b - {Float} beginning position
- * c - {Float} total change
- * d - {Float} duration of the transition
- *
- * Returns:
- * {Float}
- */
- easeIn: function(t, b, c, d) {
- return c*(t/=d)*t + b;
- },
-
- /**
- * Function: easeOut
- *
- * Parameters:
- * t - {Float} time
- * b - {Float} beginning position
- * c - {Float} total change
- * d - {Float} duration of the transition
- *
- * Returns:
- * {Float}
- */
- easeOut: function(t, b, c, d) {
- return -c *(t/=d)*(t-2) + b;
- },
-
- /**
- * Function: easeInOut
- *
- * Parameters:
- * t - {Float} time
- * b - {Float} beginning position
- * c - {Float} total change
- * d - {Float} duration of the transition
- *
- * Returns:
- * {Float}
- */
- easeInOut: function(t, b, c, d) {
- if ((t/=d/2) < 1) return c/2*t*t + b;
- return -c/2 * ((--t)*(t-2) - 1) + b;
- },
-
- CLASS_NAME: "OpenLayers.Easing.Quad"
-};
-/* ======================================================================
- OpenLayers/Geometry.js
- ====================================================================== */
-
-/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the 2-clause BSD license.
- * See license.txt in the OpenLayers distribution or repository for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/BaseTypes/Class.js
- */
-
-/**
- * Class: OpenLayers.Geometry
- * A Geometry is a description of a geographic object. Create an instance of
- * this class with the constructor. This is a base class,
- * typical geometry types are described by subclasses of this class.
- *
- * Note that if you use the method, you must
- * explicitly include the OpenLayers.Format.WKT in your build.
- */
-OpenLayers.Geometry = OpenLayers.Class({
-
- /**
- * Property: id
- * {String} A unique identifier for this geometry.
- */
- id: null,
-
- /**
- * Property: parent
- * {}This is set when a Geometry is added as component
- * of another geometry
- */
- parent: null,
-
- /**
- * Property: bounds
- * {